/* This file is part of the KDE project.
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 or 3 of the License.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library. If not, see .
*/
#include "volumeeffect.h"
#include "qbasefilter.h"
#include "qmeminputpin.h"
#include //for sqrt
QT_BEGIN_NAMESPACE
#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
namespace Phonon
{
namespace DS9
{
/**************************************************************************
* curve functions
*************************************************************************/
static qreal curveValueFadeIn3dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
return (fadeStart + fadeDiff * sqrt(completed));
}
static qreal curveValueFadeOut3dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
return (fadeStart + fadeDiff * (1.0 - sqrt(1.0 - completed)));
}
// in == out for a linear fade
static qreal curveValueFade6dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
return (fadeStart + fadeDiff * completed);
}
static qreal curveValueFadeIn9dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
return (fadeStart + fadeDiff * pow(completed, 1.5));
}
static qreal curveValueFadeOut9dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
return (fadeStart + fadeDiff * (1.0 - pow(1.0 - completed, 1.5)));
}
static qreal curveValueFadeIn12dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
return (fadeStart + fadeDiff * completed * completed);
}
static qreal curveValueFadeOut12dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed)
{
const qreal x = 1.0 - completed;
return (fadeStart + fadeDiff * (1.0 - x * x));
}
static const QVector audioMediaType()
{
QVector ret;
AM_MEDIA_TYPE mt = { MEDIATYPE_Audio, MEDIASUBTYPE_PCM, 1, 0, 1, GUID_NULL, 0, 0, 0};
ret << mt;
return ret;
}
class VolumeMemInputPin : public QMemInputPin
{
public:
VolumeMemInputPin(QBaseFilter *parent, const QVector &mt) : QMemInputPin(parent, mt, true /*transform*/)
{
}
~VolumeMemInputPin()
{
}
STDMETHODIMP NotifyAllocator(IMemAllocator *alloc, BOOL b)
{
ALLOCATOR_PROPERTIES prop;
HRESULT hr = alloc->GetProperties(&prop);
if (SUCCEEDED(hr) && prop.cBuffers > 1) {
//this allows to reduce the latency for sound
//the problem is that too low numbers makes the whole thing fail...
ALLOCATOR_PROPERTIES actual;
prop.cBuffers = 1;
alloc->SetProperties(&prop, &actual);
}
return QMemInputPin::NotifyAllocator(alloc, b);
}
};
class VolumeMemOutputPin : public QPin
{
public:
VolumeMemOutputPin(QBaseFilter *parent, const QVector &mt) : QPin(parent, PINDIR_OUTPUT, mt)
{
}
~VolumeMemOutputPin()
{
}
};
class VolumeEffectFilter : public QBaseFilter
{
public:
VolumeEffectFilter(VolumeEffect *);
//reimplementation
virtual HRESULT processSample(IMediaSample *);
private:
void treatOneSamplePerChannel(BYTE **buffer, int sampleSize, int channelCount, int frequency);
QMemInputPin *m_input;
QPin *m_output;
VolumeEffect *m_volumeEffect;
};
VolumeEffectFilter::VolumeEffectFilter(VolumeEffect *ve) : QBaseFilter(CLSID_NULL),
m_volumeEffect(ve)
{
QVector mt;
//creating the output
m_output = new VolumeMemOutputPin(this, mt);
//then creating the input
mt << audioMediaType();
m_input = new VolumeMemInputPin(this, mt);
m_input->addOutput(m_output); //make the connection here
}
void VolumeEffectFilter::treatOneSamplePerChannel(BYTE **buffer, int sampleSize, int channelCount, int frequency)
{
float volume = m_volumeEffect->volume();
if (m_volumeEffect->m_fading) {
const qreal lastSample = m_volumeEffect->m_fadeDuration * frequency / 1000;
const qreal completed = qreal(m_volumeEffect->m_fadeSamplePosition++) / lastSample;
if (qFuzzyCompare(completed, qreal(1.))) {
m_volumeEffect->setVolume(m_volumeEffect->m_targetVolume);
m_volumeEffect->m_fading = false; //we end the fading effect
} else {
volume = m_volumeEffect->m_fadeCurveFn(m_volumeEffect->m_initialVolume,
m_volumeEffect->m_targetVolume - m_volumeEffect->m_initialVolume,
completed);
}
}
for(int c = 0; c < channelCount; ++c) {
switch (sampleSize)
{
case 2:
{
short *shortBuffer = reinterpret_cast(*buffer);
*shortBuffer *= qRound(volume);
}
break;
case 1:
**buffer *= qRound(volume);
break;
default:
break;
}
*buffer += sampleSize;
}
}
HRESULT VolumeEffectFilter::processSample(IMediaSample * ms)
{
BYTE *buffer = 0;
ms->GetPointer(&buffer);
if (buffer) {
const AM_MEDIA_TYPE &mt = m_output->connectedType();
if (mt.formattype != FORMAT_WaveFormatEx) {
return VFW_E_INVALIDMEDIATYPE;
}
WAVEFORMATEX *format = reinterpret_cast(mt.pbFormat);
const int channelCount = format->nChannels;
const int sampleSize = format->wBitsPerSample / 8; //...in bytes
const BYTE *end = buffer + ms->GetActualDataLength();
while (buffer < end) {
treatOneSamplePerChannel(&buffer, sampleSize, channelCount, format->nSamplesPerSec);
}
}
return S_OK;
}
VolumeEffect::VolumeEffect(QObject *parent) : Effect(parent),
m_volume(1), m_fadeCurve(Phonon::VolumeFaderEffect::Fade3Decibel),
m_fading(false), m_initialVolume(0), m_targetVolume(0), m_fadeDuration(0),
m_fadeSamplePosition(0)
{
//creation of the effects
for(int i = 0; i < FILTER_COUNT; ++i) {
VolumeEffectFilter *f = new VolumeEffectFilter(this);
m_filters[i] = Filter(f);
}
}
float VolumeEffect::volume() const
{
return m_volume;
}
void VolumeEffect::setVolume(float newVolume)
{
m_volume = newVolume;
}
Phonon::VolumeFaderEffect::FadeCurve VolumeEffect::fadeCurve() const
{
return m_fadeCurve;
}
void VolumeEffect::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve curve)
{
m_fadeCurve = curve;
}
void VolumeEffect::fadeTo(float vol, int duration)
{
m_fading = true; //will be set back to false when fading is finished
m_targetVolume = vol;
m_fadeSamplePosition = 0;
m_initialVolume = m_volume;
m_fadeDuration = duration;
//in or out?
const bool in = vol > m_volume;
switch(m_fadeCurve)
{
case Phonon::VolumeFaderEffect::Fade6Decibel:
m_fadeCurveFn = curveValueFade6dB;
break;
case Phonon::VolumeFaderEffect::Fade9Decibel:
if (in) {
m_fadeCurveFn = curveValueFadeIn9dB;
} else {
m_fadeCurveFn = curveValueFadeOut9dB;
}
break;
case Phonon::VolumeFaderEffect::Fade12Decibel:
if (in) {
m_fadeCurveFn = curveValueFadeIn12dB;
} else {
m_fadeCurveFn = curveValueFadeOut12dB;
}
break;
case Phonon::VolumeFaderEffect::Fade3Decibel:
default:
if (in) {
m_fadeCurveFn = curveValueFadeIn3dB;
} else {
m_fadeCurveFn = curveValueFadeOut3dB;
}
break;
}
}
}
}
#endif //QT_NO_PHONON_VOLUMEFADEREFFECT
QT_END_NAMESPACE
#include "moc_volumeeffect.cpp"