aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2013-10-09 11:17:49 +0000
committertrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2013-10-09 11:17:49 +0000
commitbd043783db9ed8ec3c670265fdea84821aa3e600 (patch)
treec95630b0e39b1fc5b58ccfd0170aae066e3a2bcf /src
parented90cdc22d95e24a921a721a8307182d172c9795 (diff)
downloadqmmp-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.cpp2
-rw-r--r--src/plugins/Input/mad/decodermadfactory.cpp2
-rw-r--r--src/qmmp/decoder.cpp12
-rw-r--r--src/qmmp/decoder.h21
-rw-r--r--src/qmmp/qmmpaudioengine.cpp8
-rw-r--r--src/qmmp/replaygain.cpp124
-rw-r--r--src/qmmp/replaygain_p.h7
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