Added a normalization filter, based on SoX.

This commit is contained in:
LoRd_MuldeR 2011-01-25 23:12:56 +01:00
parent ab1b25d7a0
commit 58ded8c2e4
9 changed files with 190 additions and 14 deletions

View File

@ -410,6 +410,10 @@
RelativePath=".\src\Filter_Downmix.cpp" RelativePath=".\src\Filter_Downmix.cpp"
> >
</File> </File>
<File
RelativePath=".\src\Filter_Normalize.cpp"
>
</File>
<File <File
RelativePath=".\src\Genres.cpp" RelativePath=".\src\Genres.cpp"
> >
@ -1122,6 +1126,10 @@
RelativePath=".\src\Filter_Downmix.h" RelativePath=".\src\Filter_Downmix.h"
> >
</File> </File>
<File
RelativePath=".\src\Filter_Normalize.h"
>
</File>
<File <File
RelativePath=".\src\Genres.h" RelativePath=".\src\Genres.h"
> >

View File

@ -985,7 +985,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>602</width> <width>604</width>
<height>520</height> <height>520</height>
</rect> </rect>
</property> </property>
@ -1688,10 +1688,10 @@
</size> </size>
</property> </property>
<property name="minimum"> <property name="minimum">
<double>-12.000000000000000</double> <double>-32.000000000000000</double>
</property> </property>
<property name="maximum"> <property name="maximum">
<double>0.000000000000000</double> <double>-0.500000000000000</double>
</property> </property>
<property name="singleStep"> <property name="singleStep">
<double>0.500000000000000</double> <double>0.500000000000000</double>
@ -2298,6 +2298,8 @@
<include location="../res/Icons.qrc"/> <include location="../res/Icons.qrc"/>
<include location="../res/Icons.qrc"/> <include location="../res/Icons.qrc"/>
<include location="../res/Icons.qrc"/> <include location="../res/Icons.qrc"/>
<include location="../res/Icons.qrc"/>
<include location="../res/Icons.qrc"/>
</resources> </resources>
<connections> <connections>
<connection> <connection>

View File

@ -33,6 +33,7 @@
#include "Encoder_AAC.h" #include "Encoder_AAC.h"
#include "Encoder_FLAC.h" #include "Encoder_FLAC.h"
#include "Encoder_Wave.h" #include "Encoder_Wave.h"
#include "Filter_Normalize.h"
#include "WinSevenTaskbar.h" #include "WinSevenTaskbar.h"
#include <QApplication> #include <QApplication>
@ -496,6 +497,11 @@ void ProcessingDialog::startNextJob(void)
m_settings->prependRelativeSourcePath() m_settings->prependRelativeSourcePath()
); );
if(m_settings->normalizationFilterEnabled())
{
thread->addFilter(new NormalizeFilter(m_settings->normalizationFilterMaxVolume()));
}
m_threadList.append(thread); m_threadList.append(thread);
m_allJobs.append(thread->getId()); m_allJobs.append(thread->getId());

View File

@ -49,7 +49,7 @@ bool DownmixFilter::apply(const QString &sourceFile, const QString &outputFile,
process.setWorkingDirectory(lamexp_temp_folder()); process.setWorkingDirectory(lamexp_temp_folder());
args << "-V3"; args << "-V3";
args << "--norm" << "--temp" << "."; args << "--guard" << "--temp" << ".";
args << QDir::toNativeSeparators(sourceFile); args << QDir::toNativeSeparators(sourceFile);
args << "-c2"; args << "-c2";
args << QDir::toNativeSeparators(outputFile); args << QDir::toNativeSeparators(outputFile);

112
src/Filter_Normalize.cpp Normal file
View File

@ -0,0 +1,112 @@
///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
// Copyright (C) 2004-2011 LoRd_MuldeR <MuldeR2@GMX.de>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
#include "Filter_Normalize.h"
#include "Global.h"
#include <QDir>
#include <QProcess>
#include <QRegExp>
NormalizeFilter::NormalizeFilter(int peakVolume)
:
m_binary(lamexp_lookup_tool("sox.exe"))
{
if(m_binary.isEmpty())
{
throw "Error initializing SoX filter. Tool 'sox.exe' is not registred!";
}
m_peakVolume = min(-50, max(-3200, peakVolume));
}
NormalizeFilter::~NormalizeFilter(void)
{
}
bool NormalizeFilter::apply(const QString &sourceFile, const QString &outputFile, volatile bool *abortFlag)
{
QProcess process;
QStringList args;
process.setWorkingDirectory(lamexp_temp_folder());
args << "-V3";
args << "--temp" << ".";
args << QDir::toNativeSeparators(sourceFile);
args << QDir::toNativeSeparators(outputFile);
args << "gain";
args << "-n" << QString().sprintf("%.2f", static_cast<double>(m_peakVolume) / 100.0);
if(!startProcess(process, m_binary, args))
{
return false;
}
bool bTimeout = false;
bool bAborted = false;
while(process.state() != QProcess::NotRunning)
{
if(*abortFlag)
{
process.kill();
bAborted = true;
emit messageLogged("\nABORTED BY USER !!!");
break;
}
process.waitForReadyRead();
if(!process.bytesAvailable() && process.state() == QProcess::Running)
{
process.kill();
qWarning("SoX process timed out <-- killing!");
bTimeout = true;
break;
}
while(process.bytesAvailable() > 0)
{
QByteArray line = process.readLine();
QString text = QString::fromUtf8(line.constData()).simplified();
if(!text.isEmpty())
{
emit messageLogged(text);
}
}
}
process.waitForFinished();
if(process.state() != QProcess::NotRunning)
{
process.kill();
process.waitForFinished(-1);
}
emit statusUpdated(100);
emit messageLogged(QString().sprintf("\nExited with code: 0x%04X", process.exitCode()));
if(bTimeout || bAborted || process.exitStatus() != QProcess::NormalExit || QFileInfo(outputFile).size() == 0)
{
return false;
}
return true;
}

37
src/Filter_Normalize.h Normal file
View File

@ -0,0 +1,37 @@
///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
// Copyright (C) 2004-2011 LoRd_MuldeR <MuldeR2@GMX.de>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
#pragma once
#include "Filter_Abstract.h"
class NormalizeFilter : public AbstractFilter
{
public:
NormalizeFilter(int peakVolume = -50);
~NormalizeFilter(void);
virtual bool apply(const QString &sourceFile, const QString &outputFile, volatile bool *abortFlag);
private:
const QString m_binary;
int m_peakVolume;
};

View File

@ -55,10 +55,10 @@ g_lamexp_tools[] =
{"d837bf6ee4dab557d8b02d46c75a24e58980fffa", "gpgv.gpg", UINT_MAX}, {"d837bf6ee4dab557d8b02d46c75a24e58980fffa", "gpgv.gpg", UINT_MAX},
{"143fc001a2f6c56fe1b9e6f8a2eb2b53b9e1e504", "lame.exe", 39910}, {"143fc001a2f6c56fe1b9e6f8a2eb2b53b9e1e504", "lame.exe", 39910},
{"a4e929cfaa42fa2e61a3d0c6434c77a06d45aef3", "mac.exe", 406}, {"a4e929cfaa42fa2e61a3d0c6434c77a06d45aef3", "mac.exe", 406},
{"61d584ffaf428e9afb0ed9bd32706a954af492b0", "mediainfo_i386.exe", 739}, {"ec235c6404caa31e7975e9bbf9ba59599258ae1d", "mediainfo_i386.exe", 741},
{"81fb728cbc6057906fa1b637738e6aefe5dccf54", "mediainfo_x64.exe", 739}, {"72d7efdcafc2dee3b534f27ab6e01916d02ba470", "mediainfo_x64.exe", 741},
{"55c293a80475f7aeccf449ac9487a4626e5139cb", "mpcdec.exe", UINT_MAX}, {"55c293a80475f7aeccf449ac9487a4626e5139cb", "mpcdec.exe", UINT_MAX},
{"8bbf4a3fffe2ff143eb5ba2cf82ca16d676e865d", "mpg123.exe", UINT_MAX}, {"6b6913a54cac08b22d5b468aaed83550fc9ae5b4", "mpg123.exe", 1131},
{"8dd7138714c3bcb39f5a3213413addba13d06f1e", "oggdec.exe", UINT_MAX}, {"8dd7138714c3bcb39f5a3213413addba13d06f1e", "oggdec.exe", UINT_MAX},
{"ecd15abe103184aca96e406f5f1c82c6fb2e665d", "oggenc2_i386.exe", 287}, {"ecd15abe103184aca96e406f5f1c82c6fb2e665d", "oggenc2_i386.exe", 287},
{"ffe0fbd73352396dc3752ac9d484dbfc754a226d", "oggenc2_sse2.exe", 287}, {"ffe0fbd73352396dc3752ac9d484dbfc754a226d", "oggenc2_sse2.exe", 287},

View File

@ -73,6 +73,11 @@ ProcessThread::~ProcessThread(void)
lamexp_remove_file(m_tempFiles.takeFirst()); lamexp_remove_file(m_tempFiles.takeFirst());
} }
while(!m_filters.isEmpty())
{
delete m_filters.takeFirst();
}
LAMEXP_DELETE(m_encoder); LAMEXP_DELETE(m_encoder);
} }
@ -109,18 +114,16 @@ void ProcessThread::processFile()
return; return;
} }
QList<AbstractFilter*> filters;
//Do we need Stereo downmix? //Do we need Stereo downmix?
if(m_audioFile.formatAudioChannels() > 2 && m_encoder->requiresDownmix()) if(m_audioFile.formatAudioChannels() > 2 && m_encoder->requiresDownmix())
{ {
filters.prepend(new DownmixFilter()); m_filters.prepend(new DownmixFilter());
} }
QString sourceFile = m_audioFile.filePath(); QString sourceFile = m_audioFile.filePath();
//Decode source file //Decode source file
if(!filters.isEmpty() || !m_encoder->isFormatSupported(m_audioFile.formatContainerType(), m_audioFile.formatContainerProfile(), m_audioFile.formatAudioType(), m_audioFile.formatAudioProfile(), m_audioFile.formatAudioVersion())) if(!m_filters.isEmpty() || !m_encoder->isFormatSupported(m_audioFile.formatContainerType(), m_audioFile.formatContainerProfile(), m_audioFile.formatAudioType(), m_audioFile.formatAudioProfile(), m_audioFile.formatAudioVersion()))
{ {
m_currentStep = DecodingStep; m_currentStep = DecodingStep;
AbstractDecoder *decoder = DecoderRegistry::lookup(m_audioFile.formatContainerType(), m_audioFile.formatContainerProfile(), m_audioFile.formatAudioType(), m_audioFile.formatAudioProfile(), m_audioFile.formatAudioVersion()); AbstractDecoder *decoder = DecoderRegistry::lookup(m_audioFile.formatContainerType(), m_audioFile.formatContainerProfile(), m_audioFile.formatAudioType(), m_audioFile.formatAudioProfile(), m_audioFile.formatAudioVersion());
@ -150,10 +153,10 @@ void ProcessThread::processFile()
} }
//Apply all filters //Apply all filters
while(!filters.isEmpty()) while(!m_filters.isEmpty())
{ {
QString tempFile = generateTempFileName(); QString tempFile = generateTempFileName();
AbstractFilter *poFilter = filters.takeFirst(); AbstractFilter *poFilter = m_filters.takeFirst();
if(bSuccess) if(bSuccess)
{ {
@ -317,6 +320,11 @@ QString ProcessThread::generateTempFileName(void)
return tempFileName; return tempFileName;
} }
void ProcessThread::addFilter(AbstractFilter *filter)
{
m_filters.append(filter);
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// EVENTS // EVENTS
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -29,6 +29,7 @@
#include "Encoder_Abstract.h" #include "Encoder_Abstract.h"
class QMutex; class QMutex;
class AbstractFilter;
class ProcessThread: public QThread class ProcessThread: public QThread
{ {
@ -42,6 +43,7 @@ public:
void abort() { m_aborted = true; } void abort() { m_aborted = true; }
QUuid getId() { return m_jobId; } QUuid getId() { return m_jobId; }
void addFilter(AbstractFilter *filter);
private slots: private slots:
void handleUpdate(int progress); void handleUpdate(int progress);
@ -74,6 +76,7 @@ private:
ProcessStep m_currentStep; ProcessStep m_currentStep;
QStringList m_tempFiles; QStringList m_tempFiles;
const bool m_prependRelativeSourcePath; const bool m_prependRelativeSourcePath;
QList<AbstractFilter*> m_filters;
static QMutex *m_mutex_genFileName; static QMutex *m_mutex_genFileName;
}; };