aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2016-01-13 11:31:25 +0000
committertrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2016-01-13 11:31:25 +0000
commitdbc9b4065e2be58b531379b76ce2d934898c0d35 (patch)
tree8777f4edbfaa5f1686d0b0e2a373846573c26d22
parent2876369e1a07d00d54cfb0c439feb803c7c29347 (diff)
downloadqmmp-dbc9b4065e2be58b531379b76ce2d934898c0d35.tar.gz
qmmp-dbc9b4065e2be58b531379b76ce2d934898c0d35.tar.bz2
qmmp-dbc9b4065e2be58b531379b76ce2d934898c0d35.zip
added dithering support
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@6008 90c681e8-e032-0410-971d-27865f9a5e38
-rw-r--r--src/qmmp/dithering.cpp74
-rw-r--r--src/qmmp/dithering_p.h7
-rw-r--r--src/qmmp/qmmpaudioengine.cpp30
-rw-r--r--src/qmmp/qmmpaudioengine_p.h3
-rw-r--r--src/qmmp/qmmpsettings.cpp12
-rw-r--r--src/qmmp/qmmpsettings.h8
-rw-r--r--src/qmmpui/configdialog.cpp6
-rw-r--r--src/qmmpui/forms/configdialog.ui7
8 files changed, 111 insertions, 36 deletions
diff --git a/src/qmmp/dithering.cpp b/src/qmmp/dithering.cpp
index dd5513b47..32dcb9890 100644
--- a/src/qmmp/dithering.cpp
+++ b/src/qmmp/dithering.cpp
@@ -2,7 +2,7 @@
* Based on madplay project *
* *
* Copyright (C) 2000-2004 Robert Leslie <rob@mars.org> *
- * Copyright (C) 2015 Ilya Kotov forkotov02@hotmail.ru *
+ * Copyright (C) 2016 Ilya Kotov forkotov02@hotmail.ru *
* *
* 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 *
@@ -24,45 +24,66 @@
Dithering::Dithering() : Effect()
{
- qDebug("=========================");
-
- float output, random;
-
- for(int i = 0; i < 9; ++i)
- {
- m_dither[i].error[0] = 0.0f;
- m_dither[i].error[1] = 0.0f;
- m_dither[i].error[2] = 0.0f;
- m_dither[i].random = 0.0f;
- }
-
- //for(int i = 0; i < 500; ++i)
- // audioLinearDither(0, &m_dither[0]);
-
- qDebug("=%x=", 1<< 8);
+ m_lsb = 0.0f;
+ m_required = false;
+ m_enabled = false;
}
void Dithering::configure(quint32 srate, ChannelMap map)
{
m_chan = map.count();
+ m_required = false;
for(int i = 0; i < 9; ++i)
{
m_dither[i].error[0] = 0.0f;
m_dither[i].error[1] = 0.0f;
m_dither[i].error[2] = 0.0f;
- m_dither[i].random = 0.0f;
+ m_dither[i].random = 0;
}
Effect::configure(srate, map);
}
+void Dithering::setFormats(Qmmp::AudioFormat in, Qmmp::AudioFormat out)
+{
+ m_required = false;
+ if(AudioParameters::sampleSize(in) > AudioParameters::sampleSize(out))
+ {
+ switch (out)
+ {
+ case Qmmp::PCM_S8:
+ case Qmmp::PCM_U8:
+ m_lsb = 1.0f / 0x80;
+ m_required = true;
+ break;
+ case Qmmp::PCM_S16LE:
+ case Qmmp::PCM_S16BE:
+ case Qmmp::PCM_U16LE:
+ case Qmmp::PCM_U16BE:
+ m_lsb = 1.0f / 0x8000;
+ m_required = true;
+ break;
+ default:
+ ;
+ }
+ }
+ (m_required && m_enabled) ? qDebug("Dithering: enabled") : qDebug("Dithering: disabled");
+}
+
void Dithering::applyEffect(Buffer *b)
{
- for(size_t i = 0; i < b->samples; ++i)
+ if(m_required && m_enabled)
{
- b->data[i] = audioLinearDither(b->data[i], &m_dither[i % m_chan]);
+ for(size_t i = 0; i < b->samples; ++i)
+ b->data[i] = audioLinearDither(b->data[i], &m_dither[i % m_chan]);
}
}
+void Dithering::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+ (m_required && m_enabled) ? qDebug("Dithering: enabled") : qDebug("Dithering: disabled");
+}
+
quint32 Dithering::prng(quint32 state) // 32-bit pseudo-random number generator
{
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
@@ -78,11 +99,12 @@ float Dithering::audioLinearDither(float sample, AudioDither *dither)
dither->error[2] = dither->error[1];
dither->error[1] = dither->error[0] / 2;
- output = sample;
+ /* bias */
+ output = sample + m_lsb;
/* dither */
random = prng(dither->random);
- output += ((float) (random & 0x0000ffffL) - (dither->random & 0x0000ffffL)) / 0xffffffffL;
+ output += (float)(random - dither->random) / 0xffffffffL * m_lsb;
dither->random = random;
/* clip */
@@ -93,12 +115,12 @@ float Dithering::audioLinearDither(float sample, AudioDither *dither)
if (sample > 1.0f)
sample = 1.0f;
}
- else if (output < 1.0f)
+ else if (output < -1.0f)
{
- output = 1.0f;
+ output = -1.0f;
- if (sample < 1.0f)
- sample = 1.0f;
+ if (sample < -1.0f)
+ sample = -1.0f;
}
/* error feedback */
diff --git a/src/qmmp/dithering_p.h b/src/qmmp/dithering_p.h
index db25c00d2..a80a84504 100644
--- a/src/qmmp/dithering_p.h
+++ b/src/qmmp/dithering_p.h
@@ -2,7 +2,7 @@
* Based on madplay project *
* *
* Copyright (C) 2000-2004 Robert Leslie <rob@mars.org> *
- * Copyright (C) 2015 Ilya Kotov forkotov02@hotmail.ru *
+ * Copyright (C) 2016 Ilya Kotov forkotov02@hotmail.ru *
* *
* 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 *
@@ -35,7 +35,9 @@ public:
Dithering();
void configure(quint32 srate, ChannelMap map);
+ void setFormats(Qmmp::AudioFormat in, Qmmp::AudioFormat out);
void applyEffect(Buffer *b);
+ void setEnabled(bool enabled);
private:
int m_chan;
@@ -48,6 +50,9 @@ private:
} AudioDither;
AudioDither m_dither[9];
+ float m_lsb;
+ bool m_required, m_enabled;
+
quint32 prng(quint32 state);
float audioLinearDither(float sample, AudioDither *dither);
diff --git a/src/qmmp/qmmpaudioengine.cpp b/src/qmmp/qmmpaudioengine.cpp
index b736db2e8..b1f0b03aa 100644
--- a/src/qmmp/qmmpaudioengine.cpp
+++ b/src/qmmp/qmmpaudioengine.cpp
@@ -49,14 +49,15 @@ QmmpAudioEngine::QmmpAudioEngine(QObject *parent)
m_output = 0;
m_muted = false;
m_replayGain = 0;
+ m_dithering = 0;
m_converter = new AudioConverter;
+
m_settings = QmmpSettings::instance();
connect(m_settings,SIGNAL(replayGainSettingsChanged()), SLOT(updateReplayGainSettings()));
+ connect(m_settings,SIGNAL(audioSettingsChanged()), SLOT(updateAudioSettings()));
connect(m_settings, SIGNAL(eqSettingsChanged()), SLOT(updateEqSettings()));
reset();
m_instance = this;
-
- Dithering d;
}
QmmpAudioEngine::~QmmpAudioEngine()
@@ -339,6 +340,16 @@ void QmmpAudioEngine::updateReplayGainSettings()
}
}
+void QmmpAudioEngine::updateAudioSettings()
+{
+ if(m_dithering)
+ {
+ mutex()->lock();
+ m_dithering->setEnabled(m_settings->useDithering());
+ mutex()->unlock();
+ }
+}
+
void QmmpAudioEngine::updateEqSettings()
{
mutex()->lock();
@@ -362,6 +373,7 @@ void QmmpAudioEngine::run()
addOffset(); //offset
mutex()->unlock();
m_output->start();
+ m_dithering->setFormats(m_decoder->audioParameters().format(), m_output->format());
StateHandler::instance()->dispatch(Qmmp::Buffering);
StateHandler::instance()->dispatch(m_decoder->totalTime());
StateHandler::instance()->dispatch(Qmmp::Playing);
@@ -461,7 +473,6 @@ void QmmpAudioEngine::run()
mutex()->unlock();
sendMetaData();
addOffset(); //offset
- continue;
}
else
{
@@ -482,9 +493,13 @@ void QmmpAudioEngine::run()
StateHandler::instance()->dispatch(m_decoder->totalTime());
sendMetaData();
addOffset(); //offset
- continue;
}
}
+ if(m_output)
+ {
+ m_dithering->setFormats(m_decoder->audioParameters().format(), m_output->format());
+ continue;
+ }
}
flush(true);
@@ -623,6 +638,7 @@ void QmmpAudioEngine::prepareEffects(Decoder *d)
}
}
m_replayGain = 0;
+ m_dithering = 0;
QList <Effect *> tmp_effects = m_effects;
m_effects.clear();
@@ -637,6 +653,12 @@ void QmmpAudioEngine::prepareEffects(Decoder *d)
m_settings->replayGainPreventClipping());
m_replayGain->setReplayGainInfo(d->replayGainInfo());
}
+ //dithering
+ {
+ m_dithering = new Dithering;
+ m_dithering->configure(m_ap.sampleRate(), m_ap.channelMap());
+ m_effects << m_dithering;
+ }
//channel order converter
if(m_ap.channelMap() != m_ap.channelMap().remaped())
{
diff --git a/src/qmmp/qmmpaudioengine_p.h b/src/qmmp/qmmpaudioengine_p.h
index 92c39fc6e..c84d36e71 100644
--- a/src/qmmp/qmmpaudioengine_p.h
+++ b/src/qmmp/qmmpaudioengine_p.h
@@ -37,6 +37,7 @@ class EffectFactory;
class ReplayGain;
class QmmpSettings;
class AudioConverter;
+class Dithering;
/*! @internal
* @author Ilya Kotov <forkotov02@hotmail.ru>
@@ -62,6 +63,7 @@ public:
private slots:
void finish();
void updateReplayGainSettings();
+ void updateAudioSettings();
void updateEqSettings();
private:
@@ -96,6 +98,7 @@ private:
ReplayGain *m_replayGain;
QmmpSettings *m_settings;
AudioConverter *m_converter;
+ Dithering *m_dithering;
};
#endif // QMMPAUDIOENGINE_P_H
diff --git a/src/qmmp/qmmpsettings.cpp b/src/qmmp/qmmpsettings.cpp
index bd4d0faa4..497496cb9 100644
--- a/src/qmmp/qmmpsettings.cpp
+++ b/src/qmmp/qmmpsettings.cpp
@@ -38,11 +38,12 @@ QmmpSettings::QmmpSettings(QObject *parent) : QObject(parent)
m_rg_mode = (ReplayGainMode) settings.value("mode", REPLAYGAIN_DISABLED).toInt();
m_rg_preamp = settings.value("preamp", 0.0).toDouble();
m_rg_defaut_gain = settings.value("default_gain", 0.0).toDouble();
- m_rg_prevent_clipping = settings.value("prevent_clipping", false).toBool();
+ m_rg_prevent_clipping = settings.value("prevent_clipping", true).toBool();
settings.endGroup();
//audio settings
m_aud_software_volume = settings.value("Output/software_volume", false).toBool();
m_aud_16bit = settings.value("Output/use_16bit", false).toBool();
+ m_aud_dithering = settings.value("Output/dithering", true).toBool();
//cover settings
settings.beginGroup("Cover");
m_cover_inc = settings.value("include", (QStringList() << "*.jpg" << "*.png")).toStringList();
@@ -111,10 +112,16 @@ bool QmmpSettings::use16BitOutput() const
return m_aud_16bit;
}
-void QmmpSettings::setAudioSettings(bool soft_volume, bool use_16bit)
+bool QmmpSettings::useDithering() const
+{
+ return m_aud_dithering;
+}
+
+void QmmpSettings::setAudioSettings(bool soft_volume, bool use_16bit, bool use_dithering)
{
m_aud_software_volume = soft_volume;
m_aud_16bit = use_16bit;
+ m_aud_dithering = use_dithering;
m_timer->start();
emit audioSettingsChanged();
}
@@ -218,6 +225,7 @@ void QmmpSettings::sync()
//audio settings
settings.setValue("Output/software_volume", m_aud_software_volume);
settings.setValue("Output/use_16bit", m_aud_16bit);
+ settings.setValue("Output/dithering", m_aud_dithering);
//cover settings
settings.beginGroup("Cover");
settings.setValue("include", m_cover_inc);
diff --git a/src/qmmp/qmmpsettings.h b/src/qmmp/qmmpsettings.h
index 8b4392288..af8b465fa 100644
--- a/src/qmmp/qmmpsettings.h
+++ b/src/qmmp/qmmpsettings.h
@@ -87,11 +87,16 @@ public:
*/
bool use16BitOutput() const;
/*!
+ * Returns \b true if dithering is enabled; otherwise returns \b false.
+ */
+ bool useDithering() const;
+ /*!
* Sets audio settings.
* @param soft_volume State of software volume.
* @param use_16bit State of the 16-bit audio converter.
+ * @param use_dithering Enable/Disable audio dithering.
*/
- void setAudioSettings(bool soft_volume, bool use_16bit);
+ void setAudioSettings(bool soft_volume, bool use_16bit, bool use_dithering);
/*!
* If \b include is \b true, this function returns include cover file name filters,
* otherwise returns exclude filters.
@@ -208,6 +213,7 @@ private:
//audio settings
bool m_aud_software_volume;
bool m_aud_16bit;
+ bool m_aud_dithering;
//cover settings
QStringList m_cover_inc;
QStringList m_cover_exclude;
diff --git a/src/qmmpui/configdialog.cpp b/src/qmmpui/configdialog.cpp
index d0d1da687..f84123621 100644
--- a/src/qmmpui/configdialog.cpp
+++ b/src/qmmpui/configdialog.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2007-2014 by Ilya Kotov *
+ * Copyright (C) 2007-2016 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -151,6 +151,7 @@ void ConfigDialog::readSettings()
//audio
m_ui->softVolumeCheckBox->setChecked(gs->useSoftVolume());
m_ui->use16BitCheckBox->setChecked(gs->use16BitOutput());
+ m_ui->ditheringCheckBox->setChecked(gs->useDithering());
m_ui->bufferSizeSpinBox->setValue(gs->bufferSize());
//geometry
QSettings settings (Qmmp::configFile(), QSettings::IniFormat);
@@ -408,7 +409,8 @@ void ConfigDialog::saveSettings()
m_ui->preampDoubleSpinBox->value(),
m_ui->defaultGainDoubleSpinBox->value(),
m_ui->clippingCheckBox->isChecked());
- gs->setAudioSettings(m_ui->softVolumeCheckBox->isChecked(), m_ui->use16BitCheckBox->isChecked());
+ gs->setAudioSettings(m_ui->softVolumeCheckBox->isChecked(), m_ui->use16BitCheckBox->isChecked(),
+ m_ui->ditheringCheckBox->isChecked());
gs->setBufferSize(m_ui->bufferSizeSpinBox->value());
gs->setDetermineFileTypeByContent(m_ui->byContentCheckBox->isChecked());
QList<QVariant> var_sizes;
diff --git a/src/qmmpui/forms/configdialog.ui b/src/qmmpui/forms/configdialog.ui
index 844af865e..1cf7a46ff 100644
--- a/src/qmmpui/forms/configdialog.ui
+++ b/src/qmmpui/forms/configdialog.ui
@@ -820,6 +820,13 @@
</property>
</widget>
</item>
+ <item row="3" column="0" colspan="3">
+ <widget class="QCheckBox" name="ditheringCheckBox">
+ <property name="text">
+ <string>Use dithering</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>