Improved the resample filter. It will now also handle the adjustment of the bit-depth.
This commit is contained in:
parent
1a238db3db
commit
a392864916
@ -30,7 +30,7 @@
|
||||
#define VER_LAMEXP_MINOR_LO 4
|
||||
#define VER_LAMEXP_TYPE Alpha
|
||||
#define VER_LAMEXP_PATCH 11
|
||||
#define VER_LAMEXP_BUILD 825
|
||||
#define VER_LAMEXP_BUILD 827
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Tool versions (minimum expected versions!)
|
||||
|
@ -27,7 +27,12 @@
|
||||
#include <QProcess>
|
||||
#include <QRegExp>
|
||||
|
||||
ResampleFilter::ResampleFilter(int samplingRate)
|
||||
static __inline int multipleOf(int value, int base)
|
||||
{
|
||||
return qRound(static_cast<double>(value) / static_cast<double>(base)) * base;
|
||||
}
|
||||
|
||||
ResampleFilter::ResampleFilter(int samplingRate, int bitDepth)
|
||||
:
|
||||
m_binary(lamexp_lookup_tool("sox.exe"))
|
||||
{
|
||||
@ -36,7 +41,13 @@ ResampleFilter::ResampleFilter(int samplingRate)
|
||||
throw "Error initializing SoX filter. Tool 'sox.exe' is not registred!";
|
||||
}
|
||||
|
||||
m_samplingRate = qMin(192000, qMax(8000, samplingRate));
|
||||
m_samplingRate = (samplingRate > 0) ? qBound(8000, samplingRate, 192000) : 0;
|
||||
m_bitDepth = (bitDepth > 0) ? qBound(8, multipleOf(bitDepth, 8), 32) : 0;
|
||||
|
||||
if((m_samplingRate == 0) && (m_bitDepth == 0))
|
||||
{
|
||||
qWarning("ResampleFilter: Nothing to do, filter will be NOP!");
|
||||
}
|
||||
}
|
||||
|
||||
ResampleFilter::~ResampleFilter(void)
|
||||
@ -53,9 +64,26 @@ bool ResampleFilter::apply(const QString &sourceFile, const QString &outputFile,
|
||||
args << "-V3" << "-S";
|
||||
args << "--guard" << "--temp" << ".";
|
||||
args << QDir::toNativeSeparators(sourceFile);
|
||||
|
||||
if(m_bitDepth)
|
||||
{
|
||||
args << "-b" << QString::number(m_bitDepth);
|
||||
}
|
||||
|
||||
args << QDir::toNativeSeparators(outputFile);
|
||||
args << "rate";
|
||||
args << "-h" << QString::number(m_samplingRate);
|
||||
|
||||
if(m_samplingRate)
|
||||
{
|
||||
args << "rate";
|
||||
args << ((m_bitDepth > 16) ? "-v" : "-h"); //if resampling at/to > 16 bit depth (i.e. most commonly 24-bit), use VHQ (-v), otherwise, use HQ (-h)
|
||||
args << ((m_samplingRate > 40000) ? "-L" : "-I"); //if resampling to < 40k, use intermediate phase (-I), otherwise use linear phase (-L)
|
||||
args << QString::number(m_samplingRate);
|
||||
}
|
||||
|
||||
if((m_bitDepth || m_samplingRate) && (m_bitDepth <= 16))
|
||||
{
|
||||
args << "dither" << "-s"; //if you're mastering to 16-bit, you also need to add 'dither' (and in most cases noise-shaping) after the rate
|
||||
}
|
||||
|
||||
if(!startProcess(process, m_binary, args))
|
||||
{
|
||||
|
@ -26,7 +26,7 @@
|
||||
class ResampleFilter : public AbstractFilter
|
||||
{
|
||||
public:
|
||||
ResampleFilter(int samplingRate = 44100);
|
||||
ResampleFilter(int samplingRate = 0, int bitDepth = 0);
|
||||
~ResampleFilter(void);
|
||||
|
||||
virtual bool apply(const QString &sourceFile, const QString &outputFile, volatile bool *abortFlag);
|
||||
@ -34,4 +34,5 @@ public:
|
||||
private:
|
||||
const QString m_binary;
|
||||
int m_samplingRate;
|
||||
int m_bitDepth;
|
||||
};
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DIFF(X,Y) ((X > Y) ? (X-Y) : (Y-X))
|
||||
#define IS_WAVE(X) ((X.formatContainerType().compare("Wave", Qt::CaseInsensitive) == 0) && (X.formatAudioType().compare("PCM", Qt::CaseInsensitive) == 0))
|
||||
#define STRDEF(STR,DEF) ((!STR.isEmpty()) ? STR : DEF)
|
||||
|
||||
QMutex *ProcessThread::m_mutex_genFileName = NULL;
|
||||
@ -158,6 +159,8 @@ void ProcessThread::processFile()
|
||||
{
|
||||
sourceFile = tempFile;
|
||||
handleMessage("\n-------------------------------\n");
|
||||
m_audioFile.setFormatContainerType(QString::fromLatin1("Wave"));
|
||||
m_audioFile.setFormatAudioType(QString::fromLatin1("PCM"));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -167,36 +170,32 @@ void ProcessThread::processFile()
|
||||
emit processStateFinished(m_jobId, outFileName, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Update audio properties after decode
|
||||
if(bSuccess)
|
||||
//------------------------------------
|
||||
//Update audio properties after decode
|
||||
//------------------------------------
|
||||
if(bSuccess && IS_WAVE(m_audioFile))
|
||||
{
|
||||
if(m_encoder->supportedSamplerates() || m_encoder->supportedBitdepths() || m_encoder->requiresDownmix())
|
||||
{
|
||||
if(m_encoder->supportedSamplerates() || m_encoder->supportedBitdepths() || m_encoder->requiresDownmix())
|
||||
m_currentStep = AnalyzeStep;
|
||||
bSuccess = m_propDetect->detect(sourceFile, &m_audioFile, &m_aborted);
|
||||
|
||||
if(bSuccess)
|
||||
{
|
||||
m_currentStep = AnalyzeStep;
|
||||
bSuccess = m_propDetect->detect(sourceFile, &m_audioFile, &m_aborted);
|
||||
handleMessage("\n-------------------------------\n");
|
||||
|
||||
if(bSuccess)
|
||||
//Do we need to take care if Stereo downmix?
|
||||
if(m_encoder->requiresDownmix())
|
||||
{
|
||||
handleMessage("\n-------------------------------\n");
|
||||
insertDownmixFilter();
|
||||
}
|
||||
|
||||
//Do we need to take care of downsampling the input?
|
||||
if(m_encoder->supportedSamplerates())
|
||||
{
|
||||
insertDownsampleFilter();
|
||||
}
|
||||
|
||||
//Do we need to change the bits per sample of the input?
|
||||
if(m_encoder->supportedBitdepths())
|
||||
{
|
||||
insertBitdepthFilter();
|
||||
}
|
||||
|
||||
//Do we need Stereo downmix?
|
||||
if(m_encoder->requiresDownmix())
|
||||
{
|
||||
insertDownmixFilter();
|
||||
}
|
||||
//Do we need to take care of downsampling the input?
|
||||
if(m_encoder->supportedSamplerates() || m_encoder->supportedBitdepths())
|
||||
{
|
||||
insertDownsampleFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -391,47 +390,81 @@ QString ProcessThread::generateTempFileName(void)
|
||||
|
||||
void ProcessThread::insertDownsampleFilter(void)
|
||||
{
|
||||
bool applyDownsampling = true;
|
||||
int targetSampleRate = 0;
|
||||
int targetBitDepth = 0;
|
||||
|
||||
//Check if downsampling filter is already in the chain
|
||||
for(int i = 0; i < m_filters.count(); i++)
|
||||
/* Adjust sample rate */
|
||||
if(m_encoder->supportedSamplerates() && m_audioFile.formatAudioSamplerate())
|
||||
{
|
||||
if(dynamic_cast<ResampleFilter*>(m_filters.at(i)))
|
||||
bool applyDownsampling = true;
|
||||
|
||||
//Check if downsampling filter is already in the chain
|
||||
for(int i = 0; i < m_filters.count(); i++)
|
||||
{
|
||||
qWarning("Encoder requires downsampling, but user has already set resamling filter!");
|
||||
applyDownsampling = false;
|
||||
if(dynamic_cast<ResampleFilter*>(m_filters.at(i)))
|
||||
{
|
||||
qWarning("Encoder requires downsampling, but user has already set resamling filter!");
|
||||
handleMessage("WARNING: Encoder may need resampling, but already using resample filter. Encoding *may* fail!\n");
|
||||
applyDownsampling = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Now determine the target sample rate, if required
|
||||
if(applyDownsampling)
|
||||
{
|
||||
const unsigned int *supportedRates = m_encoder->supportedSamplerates();
|
||||
const unsigned int inputRate = m_audioFile.formatAudioSamplerate();
|
||||
unsigned int currentDiff = UINT_MAX, minimumDiff = UINT_MAX, bestRate = UINT_MAX;
|
||||
|
||||
//Find the most suitable supported sampling rate
|
||||
for(int i = 0; supportedRates[i]; i++)
|
||||
{
|
||||
currentDiff = DIFF(inputRate, supportedRates[i]);
|
||||
if(currentDiff < minimumDiff)
|
||||
{
|
||||
bestRate = supportedRates[i];
|
||||
minimumDiff = currentDiff;
|
||||
if(!(minimumDiff > 0)) break;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestRate != inputRate)
|
||||
{
|
||||
targetSampleRate = (bestRate != UINT_MAX) ? bestRate : supportedRates[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Now add the downsampling filter, if needed
|
||||
if(applyDownsampling)
|
||||
/* Adjust bit depth (word size) */
|
||||
if(m_encoder->supportedBitdepths() && m_audioFile.formatAudioBitdepth())
|
||||
{
|
||||
const unsigned int *supportedRates = m_encoder->supportedSamplerates();
|
||||
const unsigned int inputRate = m_audioFile.formatAudioSamplerate();
|
||||
unsigned int currentDiff = UINT_MAX, minimumDiff = UINT_MAX, bestRate = UINT_MAX;
|
||||
const unsigned int *supportedBPS = m_encoder->supportedBitdepths();
|
||||
const unsigned int inputBPS = m_audioFile.formatAudioBitdepth();
|
||||
unsigned int currentDiff = UINT_MAX, minimumDiff = UINT_MAX, bestBPS = UINT_MAX;
|
||||
|
||||
//Find the most suitable supported sampling rate
|
||||
for(int i = 0; supportedRates[i]; i++)
|
||||
//Find the most suitable supported bit depth
|
||||
for(int i = 0; supportedBPS[i]; i++)
|
||||
{
|
||||
currentDiff = DIFF(inputRate, supportedRates[i]);
|
||||
currentDiff = DIFF(inputBPS, supportedBPS[i]);
|
||||
if(currentDiff < minimumDiff)
|
||||
{
|
||||
bestRate = supportedRates[i];
|
||||
bestBPS = supportedBPS[i];
|
||||
minimumDiff = currentDiff;
|
||||
if(!(minimumDiff > 0)) break;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestRate != inputRate)
|
||||
if(bestBPS != inputBPS)
|
||||
{
|
||||
m_filters.prepend(new ResampleFilter((bestRate != UINT_MAX) ? bestRate : supportedRates[0]));
|
||||
targetBitDepth = (bestBPS != UINT_MAX) ? bestBPS : supportedBPS[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessThread::insertBitdepthFilter(void)
|
||||
{
|
||||
qFatal("ProcessThread::insertBitdepthFilter not implemented yet!");
|
||||
/* Insert the filter */
|
||||
if(targetSampleRate || targetBitDepth)
|
||||
{
|
||||
m_filters.append(new ResampleFilter(targetSampleRate, targetBitDepth));
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessThread::insertDownmixFilter(void)
|
||||
@ -454,7 +487,7 @@ void ProcessThread::insertDownmixFilter(void)
|
||||
unsigned int channels = m_audioFile.formatAudioChannels();
|
||||
if((channels == 0) || (channels > 2))
|
||||
{
|
||||
m_filters.prepend(new DownmixFilter());
|
||||
m_filters.append(new DownmixFilter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,6 @@ private:
|
||||
QString generateTempFileName(void);
|
||||
void insertDownsampleFilter(void);
|
||||
void insertDownmixFilter(void);
|
||||
void insertBitdepthFilter(void);
|
||||
|
||||
const QUuid m_jobId;
|
||||
AudioFileModel m_audioFile;
|
||||
|
Loading…
Reference in New Issue
Block a user