summaryrefslogtreecommitdiffstats
path: root/src/encoder
diff options
context:
space:
mode:
authorStéphane L <contact@slepin.fr>2019-01-17 09:25:40 +0100
committerDaniel Schürmann <daschuer@mixxx.org>2021-01-08 23:51:53 +0100
commit029b40539380119bcd80a28e53607b2530d1b548 (patch)
tree8178e8b2dee2f374ecf5274921c4027a572f4c56 /src/encoder
parenta1cc267f91826830ee4e1cab8318a44641e445e2 (diff)
fdk-aac encoder
Diffstat (limited to 'src/encoder')
-rw-r--r--src/encoder/encoder.cpp32
-rw-r--r--src/encoder/encoder.h14
-rw-r--r--src/encoder/encoderfdkaac.cpp395
-rw-r--r--src/encoder/encoderfdkaac.h218
-rw-r--r--src/encoder/encoderfdkaacsettings.cpp54
-rw-r--r--src/encoder/encoderfdkaacsettings.h28
6 files changed, 729 insertions, 12 deletions
diff --git a/src/encoder/encoder.cpp b/src/encoder/encoder.cpp
index 81716bea37..315eac0aca 100644
--- a/src/encoder/encoder.cpp
+++ b/src/encoder/encoder.cpp
@@ -1,5 +1,7 @@
#include "encoder/encoder.h"
+#include <QList>
+
#include "preferences/usersettings.h"
#include "recording/defs_recording.h"
// TODO(XXX): __FFMPEGFILE_ENCODERS__ is currently undefined because
@@ -23,10 +25,11 @@
#ifdef __OPUS__
#include "encoder/encoderopus.h"
-#endif
#include "encoder/encoderopussettings.h"
+#endif
-#include <QList>
+#include "encoder/encoderfdkaac.h"
+#include "encoder/encoderfdkaacsettings.h"
EncoderFactory EncoderFactory::factory;
@@ -42,10 +45,12 @@ EncoderFactory::EncoderFactory() {
m_formats.append(Encoder::Format("FLAC", ENCODING_FLAC, true));
m_formats.append(Encoder::Format("MP3", ENCODING_MP3, false));
m_formats.append(Encoder::Format("OGG Vorbis", ENCODING_OGG, false));
-
#ifdef __OPUS__
m_formats.append(Encoder::Format("Opus", ENCODING_OPUS, false));
#endif
+ m_formats.append(Encoder::Format("AAC", ENCODING_AAC, false, "m4a"));
+ m_formats.append(Encoder::Format("HE-AAC", ENCODING_HEAAC, false, "m4a"));
+ m_formats.append(Encoder::Format("HE-AACv2", ENCODING_HEAACV2, false, "m4a"));
}
const QList<Encoder::Format> EncoderFactory::getFormats() const
@@ -104,25 +109,28 @@ EncoderPointer EncoderFactory::createEncoder(
pEncoder = std::make_shared<EncoderVorbis>(pCallback);
#endif
pEncoder->setEncoderSettings(*pSettings);
- }
#ifdef __OPUS__
- else if (pSettings && pSettings->getFormat() == ENCODING_OPUS) {
+ } else if (pSettings && pSettings->getFormat() == ENCODING_OPUS) {
pEncoder = std::make_shared<EncoderOpus>(pCallback);
pEncoder->setEncoderSettings(*pSettings);
- }
#endif
- else {
+ } else if (pSettings &&
+ (pSettings->getFormat() == ENCODING_AAC ||
+ pSettings->getFormat() == ENCODING_HEAAC ||
+ pSettings->getFormat() == ENCODING_HEAACV2)) {
+ pEncoder = std::make_shared<EncoderFdkAac>(pCallback);
+ pEncoder->setEncoderSettings(*pSettings);
+ } else {
qWarning() << "Unsupported format requested! "
<< QString(pSettings ? pSettings->getFormat() : QString("NULL"));
DEBUG_ASSERT(false);
- pEncoder = std::make_shared<EncoderWave>(pCallback);;
+ pEncoder = std::make_shared<EncoderWave>(pCallback);
}
return pEncoder;
}
EncoderRecordingSettingsPointer EncoderFactory::getEncoderRecordingSettings(Encoder::Format format,
- UserSettingsPointer pConfig) const
-{
+ UserSettingsPointer pConfig) const {
if (format.internalName == ENCODING_WAVE) {
return std::make_shared<EncoderWaveSettings>(pConfig, format.internalName);
} else if (format.internalName == ENCODING_AIFF) {
@@ -135,6 +143,10 @@ EncoderRecordingSettingsPointer EncoderFactory::getEncoderRecordingSettings(Enco
return std::make_shared<EncoderVorbisSettings>(pConfig);
} else if (format.internalName == ENCODING_OPUS) {
return std::make_shared<EncoderOpusSettings>(pConfig);
+ } else if (format.internalName == ENCODING_AAC ||
+ format.internalName == ENCODING_HEAAC ||
+ format.internalName == ENCODING_HEAACV2) {
+ return std::make_shared<EncoderFdkAacSettings>(pConfig, format.internalName);
} else {
qWarning() << "Unsupported format requested! " << format.internalName;
DEBUG_ASSERT(false);
diff --git a/src/encoder/encoder.h b/src/encoder/encoder.h
index bc47f0cc81..e440ca9575 100644
--- a/src/encoder/encoder.h
+++ b/src/encoder/encoder.h
@@ -11,12 +11,22 @@ class Encoder {
public:
class Format {
public:
- Format(const QString& labelIn, const QString& nameIn, bool losslessIn)
- : label(labelIn), internalName(nameIn), lossless(losslessIn) {
+ Format(const QString& labelIn,
+ const QString& nameIn,
+ bool losslessIn,
+ const QString& fileExtIn = QString::null)
+ : label(labelIn),
+ internalName(nameIn),
+ lossless(losslessIn),
+ fileExtension(fileExtIn) {
+ if (fileExtension == QString::null) {
+ fileExtension = label;
+ }
}
QString label;
QString internalName;
bool lossless;
+ QString fileExtension;
};
Encoder() {}
diff --git a/src/encoder/encoderfdkaac.cpp b/src/encoder/encoderfdkaac.cpp
new file mode 100644
index 0000000000..235dacf8e8
--- /dev/null
+++ b/src/encoder/encoderfdkaac.cpp
@@ -0,0 +1,395 @@
+#include "encoder/encoderfdkaac.h"
+
+#include <QDir>
+#include <QStandardPaths>
+#include <QString>
+#include <QStringList>
+
+#include "engine/sidechain/enginesidechain.h"
+#include "recording/defs_recording.h"
+#include "util/logger.h"
+#include "util/sample.h"
+
+namespace {
+// recommended in encoder documentation, section 2.4.1
+const int kOutBufferBits = 6144;
+const mixxx::Logger kLogger("EncoderFdkAac");
+} // namespace
+
+EncoderFdkAac::EncoderFdkAac(EncoderCallback* pCallback)
+ : aacEncOpen(nullptr),
+ aacEncClose(nullptr),
+ aacEncEncode(nullptr),
+ aacEncInfo(nullptr),
+ aacEncoder_SetParam(nullptr),
+ m_aacAot(AOT_AAC_LC),
+ m_bitrate(0),
+ m_channels(0),
+ m_samplerate(0),
+ m_pCallback(pCallback),
+ m_library(nullptr),
+ m_pInputFifo(nullptr),
+ m_pFifoChunkBuffer(nullptr),
+ m_readRequired(0),
+ m_aacEnc(),
+ m_pAacDataBuffer(nullptr),
+ m_aacInfo() {
+ // Load shared library
+ // Code import from encodermp3.cpp
+ QStringList libnames;
+ QString libname = "";
+#ifdef __LINUX__
+ libnames << "fdk-aac";
+#elif __WINDOWS__
+ // Give top priority to libfdk-aac copied
+ // into Mixxx's installation folder
+ libnames << "libfdk-aac-1.dll";
+
+ // Fallback and user-friendly method: use libfdk-aac
+ // provided with B.U.T.T installed in
+ // a standard location
+ QString buttFdkAacPath = buttWindowsFdkAac();
+ if (!buttFdkAacPath.isNull()) {
+ kLogger.debug() << "Found libfdk-aac at" << buttFdkAacPath;
+ libnames << buttFdkAacPath;
+ }
+
+ // Last resort choices: try versions with unusual names
+ libnames << "libfdk-aac.dll";
+ libnames << "libfdkaac.dll";
+#elif __APPLE__
+ // Using Homebrew ('brew install fdk-aac' command):
+ libnames << "/usr/local/lib/libfdk-aac.dylib";
+ // Using MacPorts ('sudo port install libfdk-aac' command):
+ libnames << "/opt/local/lib/libfdk-aac.dylib";
+#endif
+
+ for (const auto& libname : libnames) {
+ m_library = new QLibrary(libname, 0);
+ if (m_library->load()) {
+ kLogger.debug() << "Successfully loaded encoder library " << libname;
+ break;
+ } else {
+ kLogger.warning() << "Failed to load " << libname << ", " << m_library->errorString();
+ }
+
+ delete m_library;
+ m_library = nullptr;
+ }
+
+ if (!m_library || !m_library->isLoaded()) {
+ ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
+ props->setType(DLG_WARNING);
+ props->setTitle(QObject::tr("Encoder"));
+
+ // TODO(Palakis): write installation guide on Mixxx's wiki
+ // and include link in message below
+ QString missingCodec = QObject::tr(
+ "<html>Mixxx cannot record or stream in AAC "
+ "or AAC+ without the FDK-AAC encoder. Due to licensing issues, "
+ "we cannot distribute this with Mixxx. "
+ "In order to record or stream in AAC or AAC+, you need to "
+ "download <b>libfdk-aac</b> "
+ "and install it on your system.");
+
+#ifdef __LINUX__
+ missingCodec = missingCodec.arg("linux");
+#elif __WINDOWS__
+ missingCodec = missingCodec.arg("windows");
+#elif __APPLE__
+ missingCodec = missingCodec.arg("mac_osx");
+#endif
+ props->setText(missingCodec);
+ props->setKey(missingCodec);
+ ErrorDialogHandler::instance()->requestErrorDialog(props);
+ return;
+ }
+
+ aacEncGetLibInfo = (aacEncGetLibInfo_)m_library->resolve("aacEncGetLibInfo");
+ aacEncOpen = (aacEncOpen_)m_library->resolve("aacEncOpen");
+ aacEncClose = (aacEncClose_)m_library->resolve("aacEncClose");
+ aacEncEncode = (aacEncEncode_)m_library->resolve("aacEncEncode");
+ aacEncInfo = (aacEncInfo_)m_library->resolve("aacEncInfo");
+ aacEncoder_SetParam = (aacEncoder_SetParam_)m_library->resolve("aacEncoder_SetParam");
+
+ // Check if all function pointers aren't null.
+ // Otherwise, the version of libfdk-aac loaded doesn't comply with the official distribution
+ // Shouldn't happen on Linux, mainly on Windows.
+ if (!aacEncGetLibInfo ||
+ !aacEncOpen ||
+ !aacEncClose ||
+ !aacEncEncode ||
+ !aacEncInfo ||
+ !aacEncoder_SetParam) {
+ m_library->unload();
+ delete m_library;
+ m_library = nullptr;
+
+ kLogger.debug() << "aacEncGetLibInfo:" << aacEncGetLibInfo;
+ kLogger.debug() << "aacEncOpen:" << aacEncOpen;
+ kLogger.debug() << "aacEncClose:" << aacEncClose;
+ kLogger.debug() << "aacEncEncode:" << aacEncEncode;
+ kLogger.debug() << "aacEncInfo:" << aacEncInfo;
+ kLogger.debug() << "aacEncoder_SetParam:" << aacEncoder_SetParam;
+
+ ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
+ props->setType(DLG_WARNING);
+ props->setTitle(QObject::tr("Encoder"));
+ QString key = QObject::tr(
+ "<html>Mixxx has detected that you use a modified version of libfdk-aac. "
+ "See <a href='http://mixxx.org/wiki/doku.php/internet_broadcasting'>Mixxx Wiki</a> "
+ "for more information.</html>");
+ props->setText(key);
+ props->setKey(key);
+ ErrorDialogHandler::instance()->requestErrorDialog(props);
+ return;
+ }
+
+ kLogger.debug() << "Loaded libfdk-aac";
+}
+
+EncoderFdkAac::~EncoderFdkAac() {
+ if (m_library && m_library->isLoaded()) {
+ aacEncClose(&m_aacEnc);
+
+ flush();
+ m_library->unload();
+ delete m_library;
+ kLogger.debug() << "Unloaded libfdk-aac";
+ }
+
+ delete[] m_pAacDataBuffer;
+ delete m_pFifoChunkBuffer;
+ delete m_pInputFifo;
+}
+
+QString EncoderFdkAac::buttWindowsFdkAac() {
+ // Return %APPDATA%/Local path
+ QString appData = QStandardPaths::writableLocation(
+ QStandardPaths::AppLocalDataLocation);
+ appData = QFileInfo(appData).absolutePath() + "/..";
+
+ // Candidate paths for a butt installation
+ QStringList searchPaths;
+ searchPaths << "C:/Program Files";
+ searchPaths << "C:/Program Files (x86)";
+ searchPaths << appData;
+
+ // Try to find a butt installation in one of the
+ // potential paths above
+ for (QString topPath : searchPaths) {
+ QDir folder(topPath);
+ if (!folder.exists()) {
+ continue;
+ }
+
+ // Typical name for a butt installation folder
+ // is "butt-x.x.x" so list subfolders beginning with "butt"
+ QStringList nameFilters("butt*");
+ QStringList subfolders =
+ folder.entryList(nameFilters, QDir::Dirs, QDir::Name);
+
+ // If a butt installation is found, try
+ // to find libfdk-aac in it
+ for (QString subName : subfolders) {
+ if (!folder.cd(subName)) {
+ continue;
+ }
+
+ kLogger.debug()
+ << "Found potential B.U.T.T installation at"
+ << (topPath + "/" + subName);
+
+ QString libFile = "libfdk-aac-1.dll";
+ if (folder.exists(libFile)) {
+ // Found a libfdk-aac here.
+ // Return the full path of the .dll file.
+ return folder.absoluteFilePath(libFile);
+ }
+
+ folder.cdUp();
+ }
+ }
+
+ return QString::null;
+}
+
+void EncoderFdkAac::setEncoderSettings(const EncoderSettings& settings) {
+ if (settings.getFormat() == ENCODING_AAC) {
+ // MPEG-4 AAC-LC
+ m_aacAot = AOT_AAC_LC;
+ } else if (settings.getFormat() == ENCODING_HEAAC) {
+ // MPEG-4 HE-AAC
+ m_aacAot = AOT_SBR;
+ } else if (settings.getFormat() == ENCODING_HEAACV2) {
+ // MPEG-4 HE-AACv2
+ m_aacAot = AOT_PS;
+ } else {
+ // Fallback to AAC-LC in case
+ // of unknown value
+ m_aacAot = AOT_AAC_LC;
+ }
+
+ // TODO(Palakis): support more bitrate configurations
+ m_bitrate = settings.getQuality();
+ switch (settings.getChannelMode()) {
+ case EncoderSettings::ChannelMode::MONO:
+ m_channels = 1;
+ break;
+ case EncoderSettings::ChannelMode::STEREO:
+ m_channels = 2;
+ break;
+ case EncoderSettings::ChannelMode::AUTOMATIC:
+ m_channels = 2;
+ break;
+ }
+}
+
+int EncoderFdkAac::initEncoder(int samplerate, QString& errorMessage) {
+ (void)errorMessage;
+ m_samplerate = samplerate;
+
+ if (!m_library) {
+ kLogger.warning() << "initEncoder failed: fdk-aac library not loaded";
+ return -1;
+ }
+
+ // This initializes the encoder handle but not the encoder itself.
+ // Actual encoder init is done below.
+ aacEncOpen(&m_aacEnc, 0, m_channels);
+ m_pAacDataBuffer = new unsigned char[kOutBufferBits * m_channels]();
+
+ // AAC Object Type: specifies "mode": AAC-LC, HE-AAC, HE-AACv2, DAB AAC, etc...
+ aacEncoder_SetParam(m_aacEnc, AACENC_AOT, m_aacAot);
+
+ // Input audio samplerate
+ aacEncoder_SetParam(m_aacEnc, AACENC_SAMPLERATE, m_samplerate);
+ // Input and output audio channel count
+ aacEncoder_SetParam(m_aacEnc, AACENC_CHANNELMODE, m_channels);
+ // Input audio channel order (fixed to 1 for traditional WAVE ordering: L, R, ...)
+ aacEncoder_SetParam(m_aacEnc, AACENC_CHANNELORDER, 1);
+
+ // Output bitrate in bits per second
+ // m_bitrate is in kilobits per second, conversion needed
+ aacEncoder_SetParam(m_aacEnc, AACENC_BITRATE, m_bitrate * 1000);
+ // Transport type (2 = ADTS)
+ aacEncoder_SetParam(m_aacEnc, AACENC_TRANSMUX, 2);
+ // Enable the AAC Afterburner, which increases audio quality
+ // at the cost of increased CPU and memory usage.
+ // Fraunhofer recommends to enable this if increased CPU and memory
+ // consumption is not a problem.
+ // TODO(Palakis): is this an issue even with 12-year old computers
+ // and notebooks?
+ aacEncoder_SetParam(m_aacEnc, AACENC_AFTERBURNER, 1);
+
+ // Actual encoder init, validates settings provided above
+ int result = aacEncEncode(m_aacEnc, nullptr, nullptr, nullptr, nullptr);
+ if (result != AACENC_OK) {
+ kLogger.warning() << "aac encoder init failed! error code:" << result;
+ return -1;
+ }
+
+ aacEncInfo(m_aacEnc, &m_aacInfo);
+ m_readRequired = m_aacInfo.frameLength * m_channels;
+
+ // Size the input FIFO buffer with twice the maximum possible sample count that can be
+ // processed at once, to avoid skipping frames or waiting for the required sample count
+ // and encode at a regular pace.
+ // This is set to the buffer size of the sidechain engine because
+ // Recording (which uses this engine) sends more samples at once to the encoder than
+ // the Live Broadcasting implementation
+ m_pInputFifo = new FIFO<SAMPLE>(EngineSideChain::SIDECHAIN_BUFFER_SIZE * 2);
+
+ m_pFifoChunkBuffer = new SAMPLE[m_readRequired * sizeof(SAMPLE)]();
+ return 0;
+}
+
+void EncoderFdkAac::encodeBuffer(const CSAMPLE* samples, const int sampleCount) {
+ if (!m_pInputFifo) {
+ return;
+ }
+
+ int writeRequired = sampleCount;
+ int writeAvailable = m_pInputFifo->writeAvailable();
+ if (writeRequired > writeAvailable) {
+ kLogger.warning() << "FIFO buffer too small, loosing samples!"
+ << "required:" << writeRequired
+ << "; available: " << writeAvailable;
+ }
+
+ int writeCount = math_min(writeRequired, writeAvailable);
+ if (writeCount > 0) {
+ // fdk-aac doesn't support float samples, so convert
+ // to integers instead
+ SAMPLE convertedSamples[writeCount];
+ SampleUtil::convertFloat32ToS16(convertedSamples, samples, writeCount);
+ m_pInputFifo->write(convertedSamples, writeCount);
+ }
+
+ processFIFO();
+}
+
+void EncoderFdkAac::processFIFO() {
+ if (!m_pInputFifo || !m_pFifoChunkBuffer) {
+ return;
+ }
+
+ while (m_pInputFifo->readAvailable() >= m_readRequired) {
+ m_pInputFifo->read(m_pFifoChunkBuffer, m_readRequired);
+
+ // fdk-aac only accept pointers for most buffer settings.
+ // Declare settings here and point to them below.
+ int inSampleSize = sizeof(SAMPLE);
+ int inDataSize = m_readRequired * inSampleSize;
+ int inDataDescription = IN_AUDIO_DATA;
+
+ int outElemSize = sizeof(unsigned char);
+ int outDataSize = kOutBufferBits * m_channels * outElemSize;
+ int outDataDescription = OUT_BITSTREAM_DATA;
+
+ // Input Buffer
+ AACENC_BufDesc inputBuf;
+ inputBuf.numBufs = 1;
+ inputBuf.bufs = (void**)&m_pFifoChunkBuffer;
+ inputBuf.bufSizes = &inDataSize;
+ inputBuf.bufElSizes = &inSampleSize;
+ inputBuf.bufferIdentifiers = &inDataDescription;
+
+ AACENC_InArgs inputDesc;
+ inputDesc.numInSamples = m_readRequired;
+ inputDesc.numAncBytes = 0;
+
+ // Output (result) Buffer
+ AACENC_BufDesc outputBuf;
+ outputBuf.numBufs = 1;
+ outputBuf.bufs = (void**)&m_pAacDataBuffer;
+ outputBuf.bufSizes = &outDataSize;
+ outputBuf.bufElSizes = &outElemSize;
+ outputBuf.bufferIdentifiers = &outDataDescription;
+
+ // Populated by aacEncEncode
+ AACENC_OutArgs outputDesc;
+
+ int result = aacEncEncode(m_aacEnc, &inputBuf, &outputBuf, &inputDesc, &outputDesc);
+ if (result != AACENC_OK) {
+ kLogger.warning() << "aacEncEncode failed! error code:" << result;
+ return;
+ }
+
+ int sampleDiff = inputDesc.numInSamples - outputDesc.numInSamples;
+ if (sampleDiff > 0) {
+ kLogger.warning() << "encoder ignored" << sampleDiff << "samples!";
+ }
+
+ m_pCallback->write(nullptr, m_pAacDataBuffer, 0, outputDesc.numOutBytes);
+ }
+}
+
+void EncoderFdkAac::updateMetaData(const QString& artist, const QString& title, const QString& album) {
+ (void)artist, (void)title, (void)album;
+}
+
+void EncoderFdkAac::flush() {
+ // At this point there may still be samples in the FIFO buffer.
+ processFIFO();
+}
diff --git a/src/encoder/encoderfdkaac.h b/src/encoder/encoderfdkaac.h
new file mode 100644
index 0000000000..03d5cbb0d0
--- /dev/null
+++ b/src/encoder/encoderfdkaac.h
@@ -0,0 +1,218 @@
+// encoderfdkaac.h
+// Created on Aug 15 2017 by Palakis
+
+#ifndef ENCODER_ENCODERFDKAAC_H
+#define ENCODER_ENCODERFDKAAC_H
+
+#include <QLibrary>
+#include <QString>
+
+#include "encoder/encoder.h"
+#include "util/fifo.h"
+
+class EncoderFdkAac : public Encoder {
+ public:
+ EncoderFdkAac(EncoderCallback* pCallback);
+ virtual ~EncoderFdkAac();
+
+ int initEncoder(int samplerate, QString& errorMessage) override;
+ void encodeBuffer(const CSAMPLE* samples, const int sampleCount) override;
+ void updateMetaData(const QString& artist, const QString& title, const QString& album) override;
+ void flush() override;
+ void setEncoderSettings(const EncoderSettings& settings) override;
+
+ private:
+ QString buttWindowsFdkAac();
+
+ // libfdk-aac common AOTs
+ static const int AOT_AAC_LC = 2; // AAC-LC
+ static const int AOT_SBR = 5; // HE-AAC
+ static const int AOT_PS = 29; // HE-AACv2
+
+ // libfdk-aac types and structs
+ typedef signed int INT;
+ typedef unsigned int UINT;
+ typedef signed short SHORT;
+ typedef unsigned short USHORT;
+ typedef signed char SCHAR;
+ typedef unsigned char UCHAR;
+
+ typedef enum {
+ AACENC_OK = 0x0000,
+
+ AACENC_INVALID_HANDLE = 0x0020,
+ AACENC_MEMORY_ERROR = 0x0021,
+ AACENC_UNSUPPORTED_PARAMETER = 0x0022,
+ AACENC_INVALID_CONFIG = 0x0023,
+
+ AACENC_INIT_ERROR = 0x0040,
+ AACENC_INIT_AAC_ERROR = 0x0041,
+ AACENC_INIT_SBR_ERROR = 0x0042,
+ AACENC_INIT_TP_ERROR = 0x0043,
+ AACENC_INIT_META_ERROR = 0x0044,
+
+ AACENC_ENCODE_ERROR = 0x0060,
+
+ AACENC_ENCODE_EOF = 0x0080,
+ } AACENC_ERROR;
+
+ typedef enum {
+ AACENC_AOT = 0x0100,
+ AACENC_BITRATE = 0x0101,
+ AACENC_BITRATEMODE = 0x0102,
+ AACENC_SAMPLERATE = 0x0103,
+ AACENC_SBR_MODE = 0x0104,
+ AACENC_GRANULE_LENGTH = 0x0105,
+ AACENC_CHANNELMODE = 0x0106,
+ AACENC_CHANNELORDER = 0x0107,
+ AACENC_SBR_RATIO = 0x0108,
+ AACENC_AFTERBURNER = 0x0200,
+ AACENC_BANDWIDTH = 0x0203,
+ AACENC_PEAK_BITRATE = 0x0207,
+ AACENC_TRANSMUX = 0x0300,
+ AACENC_HEADER_PERIOD = 0x0301,
+ AACENC_SIGNALING_MODE = 0x0302,
+ AACENC_TPSUBFRAMES = 0x0303,
+ AACENC_AUDIOMUXVER = 0x0304,
+ AACENC_PROTECTION = 0x0306,
+ AACENC_ANCILLARY_BITRATE = 0x0500,
+ AACENC_METADATA_MODE = 0x0600,
+ AACENC_CONTROL_STATE = 0xFF00,
+ AACENC_NONE = 0xFFFF
+ } AACENC_PARAM;
+
+ typedef enum {
+ IN_AUDIO_DATA = 0,
+ IN_ANCILLRY_DATA = 1, // as is in fdk-aac
+ IN_METADATA_SETUP = 2,
+ OUT_BITSTREAM_DATA = 3,
+ OUT_AU_SIZES = 4
+ } AACENC_BufferIdentifier;
+
+ typedef enum {
+ FDK_NONE = 0,
+ FDK_TOOLS = 1,
+ FDK_SYSLIB = 2,
+ FDK_AACDEC = 3,
+ FDK_AACENC = 4,
+ FDK_SBRDEC = 5,
+ FDK_SBRENC = 6,
+ FDK_TPDEC = 7,
+ FDK_TPENC = 8,
+ FDK_MPSDEC = 9,
+ FDK_MPEGFILEREAD = 10,
+ FDK_MPEGFILEWRITE = 11,
+ FDK_MP2DEC = 12,
+ FDK_DABDEC = 13,
+ FDK_DABPARSE = 14,
+ FDK_DRMDEC = 15,
+ FDK_DRMPARSE = 16,
+ FDK_AACLDENC = 17,
+ FDK_MP2ENC = 18,
+ FDK_MP3ENC = 19,
+ FDK_MP3DEC = 20,
+ FDK_MP3HEADPHONE = 21,
+ FDK_MP3SDEC = 22,
+ FDK_MP3SENC = 23,
+ FDK_EAEC = 24,
+ FDK_DABENC = 25,
+ FDK_DMBDEC = 26,
+ FDK_FDREVERB = 27,
+ FDK_DRMENC = 28,
+ FDK_METADATATRANSCODER = 29,
+ FDK_AC3DEC = 30,
+ FDK_PCMDMX = 31,
+
+ FDK_MODULE_LAST
+ } FDK_MODULE_ID;
+
+ typedef struct AACENCODER* HANDLE_AACENCODER;
+ typedef struct {
+ UINT maxOutBufBytes;
+ UINT maxAncBytes;
+ UINT inBufFillLevel;
+ UINT inputChannels;
+ UINT frameLength;
+ UINT encoderDelay;
+ UCHAR confBuf[64];
+ UINT confSize;
+ } AACENC_InfoStruct;
+ typedef struct {
+ INT numBufs;
+ void** bufs;
+ INT* bufferIdentifiers;
+ INT* bufSizes;
+ INT* bufElSizes;
+ } AACENC_BufDesc;
+ typedef struct {
+ INT numInSamples;
+ INT numAncBytes;
+ } AACENC_InArgs;
+ typedef struct {
+ INT numOutBytes;
+ INT numInSamples;
+ INT numAncBytes;
+ } AACENC_OutArgs;
+ typedef struct {
+ const char* title;
+ const char* build_date;
+ const char* build_time;
+ FDK_MODULE_ID module_id;
+ INT version;
+ UINT flags;
+ char versionStr[32];
+ } LIB_INFO;
+
+ // libfdk-aac functions prototypes
+ typedef AACENC_ERROR (*aacEncGetLibInfo_)(LIB_INFO*);
+
+ typedef AACENC_ERROR (*aacEncOpen_)(
+ HANDLE_AACENCODER*,
+ const UINT,
+ const UINT);
+
+ typedef AACENC_ERROR (*aacEncClose_)(HANDLE_AACENCODER*);
+
+ typedef AACENC_ERROR (*aacEncEncode_)(
+ const HANDLE_AACENCODER,
+ const AACENC_BufDesc*,
+ const AACENC_BufDesc*,
+ const AACENC_InArgs*,
+ AACENC_OutArgs*);
+
+ typedef AACENC_ERROR (*aacEncInfo_)(
+ const HANDLE_AACENCODER,
+ AACENC_InfoStruct*);
+
+ typedef AACENC_ERROR (*aacEncoder_SetParam_)(
+ const HANDLE_AACENCODER,
+ const AACENC_PARAM,
+ const UINT);
+
+ // libfdk-aac function pointers
+ aacEncGetLibInfo_ aacEncGetLibInfo;
+ aacEncOpen_ aacEncOpen;
+ aacEncClose_ aacEncClose;
+ aacEncEncode_ aacEncEncode;
+ aacEncInfo_ aacEncInfo;
+ aacEncoder_SetParam_ aacEncoder_SetParam;
+
+ // Instance methods
+ void processFIFO();
+
+ // Instance attributes
+ int m_aacAot;
+ int m_bitrate;
+ int m_channels;
+ int m_samplerate;
+ EncoderCallback* m_pCallback;
+ QLibrary* m_library;
+ FIFO<SAMPLE>* m_pInputFifo;
+ SAMPLE* m_pFifoChunkBuffer;
+ int m_readRequired;
+ HANDLE_AACENCODER m_aacEnc;
+ unsigned char* m_pAacDataBuffer;
+ AACENC_InfoStruct m_aacInfo;
+};
+
+#endif // ENCODER_ENCODERFDKAAC_H
diff --git a/src/encoder/encoderfdkaacsettings.cpp b/src/encoder/encoderfdkaacsettings.cpp
new file mode 100644
index 0000000000..c2b0048676
--- /dev/null
+++ b/src/encoder/encoderfdkaacsettings.cpp
@@ -0,0 +1,54 @@
+#include "encoder/encoderfdkaacsettings.h"
+
+#include "recording/defs_recording.h"
+#include "util/logger.h"
+
+namespace {
+const int kDefaultQualityIndex = 6;
+const char* kQualityKey = "FdkAac_Quality";
+const mixxx::Logger kLogger("EncoderFdkAacSettings");
+} // namespace
+
+EncoderFdkAacSettings::EncoderFdkAacSettings(
+ UserSettingsPointer pConfig,
+ QString format)
+ : m_pConfig(pConfig),
+ m_format(std::move(format)) {
+ m_qualList.append(32);
+ m_qualList.append(48);
+ m_qualList.append(64);
+ m_qualList.append(80);
+ m_qualList.append(96);
+ m_qualList.append(112); // stereo
+ m_qualList.append(128); // stereo
+ m_qualList.append(160); // stereo
+ m_qualList.append(192); // stereo
+ m_qualList.append(224); // stereo
+ m_qualList.append(256); // stereo
+ m_qualList.append(320); // stereo
+}
+
+EncoderFdkAacSettings::~EncoderFdkAacSettings() {
+}
+
+QList<int> EncoderFdkAacSettings::getQualityValues() const {
+ return m_qualList;
+}
+
+int EncoderFdkAacSettings::getQuality() const {
+ return m_qualList.at(getQualityIndex());
+}
+
+int EncoderFdkAacSettings::getQualityIndex() const {
+ int qualityIndex = m_pConfig->getValue(
+ ConfigKey(RECORDING_PREF_KEY, kQualityKey), kDefaultQualityIndex);
+ if (qualityIndex >= 0 && qualityIndex < m_qualList.size()) {
+ return qualityIndex;
+ } else {
+ kLogger.warning()
+ << "Invalid qualityIndex:"
+ << qualityIndex << "(Max is:" << m_qualList.size() << "). Ignoring it"
+ << "and returning default, which is:" << kDefaultQualityIndex;
+ }
+ return kDefaultQualityIndex;
+}
diff --git a/src/encoder/encoderfdkaacsettings.h b/src/encoder/encoderfdkaacsettings.h
new file mode 100644
index 0000000000..df3a21d169
--- /dev/null
+++ b/src/encoder/encoderfdkaacsettings.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <QList>
+
+#include "encoder/encoder.h"
+#include "encoder/encodersettings.h"
+
+class EncoderFdkAacSettings : public EncoderRecordingSettings {
+ public:
+ EncoderFdkAacSettings(UserSettingsPointer pConfig, QString format);
+ virtual ~EncoderFdkAacSettings();
+
+ // Returns the list of quality values that it supports, to assign them to the slider
+ QList<int> getQualityValues() const override;
+ // Returns the current quality value
+ int getQuality() const override;
+ int getQualityIndex() const override;
+
+ // Returns the format of this encoder settings.
+ QString getFormat() const override {
+ return m_format;
+ }
+
+ private:
+ QList<int> m_qualList;
+ UserSettingsPointer m_pConfig;
+ QString m_format;
+};