diff options
| author | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2013-10-09 11:17:49 +0000 |
|---|---|---|
| committer | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2013-10-09 11:17:49 +0000 |
| commit | bd043783db9ed8ec3c670265fdea84821aa3e600 (patch) | |
| tree | c95630b0e39b1fc5b58ccfd0170aae066e3a2bcf /src | |
| parent | ed90cdc22d95e24a921a721a8307182d172c9795 (diff) | |
| download | qmmp-bd043783db9ed8ec3c670265fdea84821aa3e600.tar.gz qmmp-bd043783db9ed8ec3c670265fdea84821aa3e600.tar.bz2 qmmp-bd043783db9ed8ec3c670265fdea84821aa3e600.zip | |
added peak overflow support
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@3755 90c681e8-e032-0410-971d-27865f9a5e38
Diffstat (limited to 'src')
| -rw-r--r-- | src/plugins/Input/cue/decoder_cue.cpp | 2 | ||||
| -rw-r--r-- | src/plugins/Input/mad/decodermadfactory.cpp | 2 | ||||
| -rw-r--r-- | src/qmmp/decoder.cpp | 12 | ||||
| -rw-r--r-- | src/qmmp/decoder.h | 21 | ||||
| -rw-r--r-- | src/qmmp/qmmpaudioengine.cpp | 8 | ||||
| -rw-r--r-- | src/qmmp/replaygain.cpp | 124 | ||||
| -rw-r--r-- | src/qmmp/replaygain_p.h | 7 |
7 files changed, 123 insertions, 53 deletions
diff --git a/src/plugins/Input/cue/decoder_cue.cpp b/src/plugins/Input/cue/decoder_cue.cpp index f9d31f48d..a4e5296e2 100644 --- a/src/plugins/Input/cue/decoder_cue.cpp +++ b/src/plugins/Input/cue/decoder_cue.cpp @@ -99,7 +99,7 @@ bool DecoderCUE::initialize() configure(m_decoder->audioParameters().sampleRate(), m_decoder->audioParameters().channels(), m_decoder->audioParameters().format()); - setReplayGainInfo(m_parser->replayGain(m_track)); + setReplayGainInfo(m_parser->replayGain(m_track), m_decoder->hasHeadroom()); length_in_bytes = audioParameters().sampleRate() * audioParameters().channels() * audioParameters().sampleSize() * m_length/1000; diff --git a/src/plugins/Input/mad/decodermadfactory.cpp b/src/plugins/Input/mad/decodermadfactory.cpp index a254a8a96..2b6aeb8ba 100644 --- a/src/plugins/Input/mad/decodermadfactory.cpp +++ b/src/plugins/Input/mad/decodermadfactory.cpp @@ -100,7 +100,7 @@ Decoder *DecoderMADFactory::create(const QString &url, QIODevice *input) if(!url.contains("://")) //local file { ReplayGainReader rg(url); - d->setReplayGainInfo(rg.replayGainInfo()); + d->setReplayGainInfo(rg.replayGainInfo(), true); } return d; } diff --git a/src/qmmp/decoder.cpp b/src/qmmp/decoder.cpp index 9b48c6a35..77afe2726 100644 --- a/src/qmmp/decoder.cpp +++ b/src/qmmp/decoder.cpp @@ -21,14 +21,16 @@ extern "C" Decoder::Decoder(QIODevice *input) : m_input(input) { m_hasMetaData = false; + m_hasHeadroom = false; } Decoder::~Decoder() {} -void Decoder::setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &rg) +void Decoder::setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &rg, bool headroom) { m_rg = rg; + m_hasHeadroom = headroom; } void Decoder::configure(quint32 srate, int chan, Qmmp::AudioFormat format) @@ -38,6 +40,9 @@ void Decoder::configure(quint32 srate, int chan, Qmmp::AudioFormat format) qint64 Decoder::read(float *data, qint64 samples) { + Q_UNUSED(data); + Q_UNUSED(samples); + qFatal("Decoder: peak overflow is enabled but does not implemented"); return -1; } @@ -75,6 +80,11 @@ bool Decoder::hasMetaData() const return m_hasMetaData; } +bool Decoder::hasHeadroom() const +{ + return m_hasHeadroom; +} + QMap<Qmmp::MetaData, QString> Decoder::takeMetaData() { m_hasMetaData = false; diff --git a/src/qmmp/decoder.h b/src/qmmp/decoder.h index d568aacbe..36251c798 100644 --- a/src/qmmp/decoder.h +++ b/src/qmmp/decoder.h @@ -55,6 +55,15 @@ public: * Subclass should reimplement this function. */ virtual qint64 read(char *data, qint64 maxSize) = 0; + /*! + * Reads up of decoded audio using float audio format. + * Reimplement this function if a decoder supports peak overflow (i.e. has headroom). + * Audio engine uses this function to prevent clipping. + * Default implementation doesn nothing and returns -1. + * @param data output audio data + * @param samples maximum samples count + * @return the number of samples read, or -1 if an error occurred. + */ virtual qint64 read(float *data, qint64 samples); /*! * Returns current bitrate (in kbps). @@ -84,8 +93,11 @@ public: QMap<Qmmp::ReplayGainKey, double> replayGainInfo() const; /*! * Sets ReplayGain information. Use this function before playback. + * @param rg ReplayGain information + * @param headroom Indicates that decoder allows peak overflow. + * In this case the decoder should support float output. */ - void setReplayGainInfo(const QMap<Qmmp::ReplayGainKey,double> &rg); + void setReplayGainInfo(const QMap<Qmmp::ReplayGainKey,double> &rg, bool headroom = false); /*! * Returns QIODevice-based input source assigned for this decoder. */ @@ -101,13 +113,17 @@ public: */ bool hasMetaData() const; /*! + * Returns \b true if a decoder allows peak overflow, otherwise returns \b false. + */ + bool hasHeadroom() const; + /*! * Takes metadata out of decoder and returns it. * Attention: hasMetaData() should return \b true before use of this fuction. */ QMap<Qmmp::MetaData, QString> takeMetaData(); /*! * Returns DecoderFactory pointer which supports file \b path or 0 if file \b path is unsupported - * @param path Full local file path + * @param path Full local file path. * @param useContent Content-based file type determination (\b true - enabled, \b false - disabled) */ static DecoderFactory *findByPath(const QString &path, bool useContent = false); @@ -171,6 +187,7 @@ private: AudioParameters m_parameters; QIODevice *m_input; bool m_hasMetaData; + bool m_hasHeadroom; QMap<Qmmp::MetaData, QString> m_metaData; QMap <Qmmp::ReplayGainKey, double> m_rg; //replay gain information }; diff --git a/src/qmmp/qmmpaudioengine.cpp b/src/qmmp/qmmpaudioengine.cpp index 32eacda86..9a4bb0a33 100644 --- a/src/qmmp/qmmpaudioengine.cpp +++ b/src/qmmp/qmmpaudioengine.cpp @@ -330,7 +330,7 @@ void QmmpAudioEngine::run() } m_decoder = m_decoders.dequeue(); addOffset(); //offset - m_replayGain->setReplayGainInfo(m_decoder->replayGainInfo()); + m_replayGain->setReplayGainInfo(m_decoder->replayGainInfo(), m_decoder->hasHeadroom()); mutex()->unlock(); m_output->start(); StateHandler::instance()->dispatch(Qmmp::Playing); @@ -402,7 +402,7 @@ void QmmpAudioEngine::run() StateHandler::instance()->dispatch(Qmmp::Playing); m_decoder->next(); StateHandler::instance()->dispatch(m_decoder->totalTime()); - m_replayGain->setReplayGainInfo(m_decoder->replayGainInfo()); + m_replayGain->setReplayGainInfo(m_decoder->replayGainInfo(), m_decoder->hasHeadroom()); m_output->seek(0); //reset counter addOffset(); //offset mutex()->unlock(); @@ -414,7 +414,7 @@ void QmmpAudioEngine::run() delete m_decoder; m_decoder = m_decoders.dequeue(); //m_seekTime = m_inputs.value(m_decoder)->offset(); - m_replayGain->setReplayGainInfo(m_decoder->replayGainInfo()); + m_replayGain->setReplayGainInfo(m_decoder->replayGainInfo(), m_decoder->hasHeadroom()); //use current output if possible prepareEffects(m_decoder); if(m_ap == m_output->audioParameters()) @@ -595,6 +595,8 @@ void QmmpAudioEngine::prepareEffects(Decoder *d) { m_ap = d->audioParameters(); + //m_ap = AudioParameters(44100, 2, Qmmp::PCM_S24LE); + m_replayGain->configure(m_ap); foreach(Effect *e, m_effects) //remove disabled and external effects diff --git a/src/qmmp/replaygain.cpp b/src/qmmp/replaygain.cpp index eeab49d50..c653c3fa3 100644 --- a/src/qmmp/replaygain.cpp +++ b/src/qmmp/replaygain.cpp @@ -31,6 +31,9 @@ ReplayGain::ReplayGain() m_default_gain = 0.0; m_prebuf = 0; m_prevent_clipping = false; + m_disabled = true; + m_headroom = false; + m_sample_size = 2; } ReplayGain::~ReplayGain() @@ -45,11 +48,13 @@ void ReplayGain::configure(const AudioParameters &p) if(m_prebuf) delete [] m_prebuf; m_prebuf = new float[QMMP_BLOCK_FRAMES * p.channels() * 4]; + m_sample_size = AudioParameters::sampleSize(m_format); } -void ReplayGain::setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &info) +void ReplayGain::setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &info, bool headroom) { m_info = info; + m_headroom = headroom; updateScale(); if(m_mode != QmmpSettings::REPLAYGAIN_DISABLED) { @@ -59,69 +64,99 @@ void ReplayGain::setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &info m_info[Qmmp::REPLAYGAIN_ALBUM_GAIN], m_info[Qmmp::REPLAYGAIN_ALBUM_PEAK]); qDebug("ReplayGain: scale=%f", m_scale); + qDebug("ReplayGain: headroom=%d", m_headroom); } - else - qDebug("ReplayGain: disabled"); } qint64 ReplayGain::read(Decoder *decoder, char *data, qint64 size) { - if(m_mode == QmmpSettings::REPLAYGAIN_DISABLED || m_scale == 1.0) + if(m_disabled) return decoder->read(data, size); - qint64 samples = 0; - - switch (m_format) + if(m_headroom) //with peak overflow support { - case Qmmp::PCM_S8: - samples = decoder->read(m_prebuf, size); - break; - case Qmmp::PCM_S16LE: - samples = decoder->read(m_prebuf, size >> 1); - break; - case Qmmp::PCM_S24LE: - case Qmmp::PCM_S32LE: - samples = decoder->read(m_prebuf, size >> 2); - break; - default: - break; - } + qint64 samples = decoder->read(m_prebuf, size >> (m_sample_size >> 1)); //size / m_sample_size; + + if(samples <= 0) + return samples; - for(qint64 i = 0; i < samples; ++i) + for(qint64 i = 0; i < samples; ++i) + { + m_prebuf[i] *= m_scale; + m_prebuf[i] = qBound((float)-1.0, m_prebuf[i], (float)1.0); + + switch (m_format) + { + case Qmmp::PCM_S8: + ((char*)data)[i] = m_prebuf[i] * (128.0 - 0.5); + break; + case Qmmp::PCM_S16LE: + ((short*)data)[i] = m_prebuf[i] * (32768.0 - 0.5); + break; + case Qmmp::PCM_S24LE: + ((qint32*)data)[i] = m_prebuf[i] * ((double)(1U << 23) - 0.5); + break; + case Qmmp::PCM_S32LE: + ((qint32*)data)[i] = m_prebuf[i] * ((double)(1U << 31) - 0.5); + break; + default: + return -1; + } + } + return samples << (m_sample_size >> 1); //samples * m_sample_size; + } + else //without peak overflow support { - m_prebuf[i] *= m_scale; - m_prebuf[i] = qBound((float)-1.0, m_prebuf[i], (float)1.0); + size = decoder->read(data, size); + + if(size <= 0) + return size; + + qint64 samples = size >> (m_sample_size >> 1); //size / m_sample_size; + + double s = 0; switch (m_format) { case Qmmp::PCM_S8: - ((char*)data)[i] = m_prebuf[i] * 127.0; + { + for (qint64 i = 0; i < samples; i++) + { + s = qBound(-128.0, ((char*)data)[i] * m_scale, 127.0); + ((char*)data)[i] = s; + } break; + } case Qmmp::PCM_S16LE: - ((short*)data)[i] = m_prebuf[i] * 32767.0; + { + for (qint64 i = 0; i < samples; i++) + ((short*)data)[i] = qBound(-32768.0, ((short*)data)[i] * m_scale, 32767.0); break; + } case Qmmp::PCM_S24LE: - ((qint32*)data)[i] = m_prebuf[i] * (1U << 28); + { + for (qint64 i = 0; i < samples; i++) + { + ((qint32*)data)[i] = qBound((double)(-(1U << 23)), + ((qint32*)data)[i] * m_scale, + (double)((1U << 23) - 1)); + } break; + } case Qmmp::PCM_S32LE: - ((qint32*)data)[i] = m_prebuf[i] * (1U << 31); + { + for (qint64 i = 0; i < samples; i++) + { + ((qint32*)data)[i] = qBound((double)(-(1U << 31)), + ((qint32*)data)[i] * m_scale, + (double)((1U << 31) - 1)); + } break; + } default: - break; + return -1; } - } - - switch (m_format) - { - case Qmmp::PCM_S8: - return samples; - case Qmmp::PCM_S16LE: - return samples << 1; - case Qmmp::PCM_S24LE: - case Qmmp::PCM_S32LE: - return samples << 2; - default: - return -1; + return size; } } @@ -132,13 +167,14 @@ void ReplayGain::updateSettings(QmmpSettings::ReplayGainMode mode, double preamp m_preamp = preamp; m_default_gain = default_gain; m_prevent_clipping = clip; - setReplayGainInfo(m_info); + setReplayGainInfo(m_info, m_headroom); } void ReplayGain::updateScale() { double peak = 0.0; m_scale = 1.0; + m_disabled = true; switch(m_mode) { case QmmpSettings::REPLAYGAIN_TRACK: @@ -151,6 +187,7 @@ void ReplayGain::updateScale() break; case QmmpSettings::REPLAYGAIN_DISABLED: m_scale = 1.0; + qDebug("ReplayGain: disabled"); return; } if(m_scale == 1.0) @@ -160,5 +197,6 @@ void ReplayGain::updateScale() m_scale = m_scale*peak > 1.0 ? 1.0 / peak : m_scale; m_scale = qMin(m_scale, 5.6234); // +15 dB m_scale = qMax(m_scale, 0.1778); // -15 dB*/ - m_scale = 0.5; + if((m_disabled = (m_scale == 1.0))) + qDebug("ReplayGain: disabled"); } diff --git a/src/qmmp/replaygain_p.h b/src/qmmp/replaygain_p.h index 46fc1da62..691865bc9 100644 --- a/src/qmmp/replaygain_p.h +++ b/src/qmmp/replaygain_p.h @@ -39,7 +39,7 @@ public: void configure(const AudioParameters &p); void updateSettings(QmmpSettings::ReplayGainMode mode, double preamp, double default_gain, bool clip); - void setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &info); + void setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &info, bool headroom); qint64 read(Decoder *decoder, char *data, qint64 size); private: @@ -51,7 +51,10 @@ private: double m_default_gain; float *m_prebuf; bool m_prevent_clipping; - int m_format; + Qmmp::AudioFormat m_format; + bool m_disabled; + bool m_headroom; + int m_sample_size; }; #endif // REPLAYGAIN_H |
