diff options
| -rw-r--r-- | src/qmmp/dithering.cpp | 74 | ||||
| -rw-r--r-- | src/qmmp/dithering_p.h | 7 | ||||
| -rw-r--r-- | src/qmmp/qmmpaudioengine.cpp | 30 | ||||
| -rw-r--r-- | src/qmmp/qmmpaudioengine_p.h | 3 | ||||
| -rw-r--r-- | src/qmmp/qmmpsettings.cpp | 12 | ||||
| -rw-r--r-- | src/qmmp/qmmpsettings.h | 8 | ||||
| -rw-r--r-- | src/qmmpui/configdialog.cpp | 6 | ||||
| -rw-r--r-- | src/qmmpui/forms/configdialog.ui | 7 |
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> |
