Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ADSR.h"
#include "fx/ADSRReader.h"
AUD_NAMESPACE_BEGIN
ADSR::ADSR(std::shared_ptr<ISound> sound, float attack, float decay, float sustain, float release) :
Effect(sound),
m_attack(attack), m_decay(decay), m_sustain(sustain), m_release(release)
{
}
float ADSR::getAttack() const
{
return m_attack;
}
void ADSR::setAttack(float attack)
{
m_attack = attack;
}
float ADSR::getDecay() const
{
return m_decay;
}
void ADSR::setDecay(float decay)
{
m_decay = decay;
}
float ADSR::getSustain() const
{
return m_sustain;
}
void ADSR::setSustain(float sustain)
{
m_sustain = sustain;
}
float ADSR::getRelease() const
{
return m_release;
}
void ADSR::setRelease(float release)
{
m_release = release;
}
std::shared_ptr<IReader> ADSR::createReader()
{
return std::shared_ptr<IReader>(new ADSRReader(getReader(), m_attack, m_decay, m_sustain, m_release));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ADSRReader.h"
AUD_NAMESPACE_BEGIN
ADSRReader::ADSRReader(std::shared_ptr<IReader> reader, float attack, float decay, float sustain, float release) :
EffectReader(reader),
m_attack(attack), m_decay(decay), m_sustain(sustain), m_release(release)
{
nextState(ADSR_STATE_ATTACK);
}
ADSRReader::~ADSRReader()
{
}
void ADSRReader::nextState(ADSRState state)
{
m_state = state;
switch(m_state)
{
case ADSR_STATE_ATTACK:
m_level = 0;
if(m_attack <= 0)
{
nextState(ADSR_STATE_DECAY);
return;
}
return;
case ADSR_STATE_DECAY:
if(m_decay <= 0)
{
nextState(ADSR_STATE_SUSTAIN);
return;
}
if(m_level > 1.0)
m_level = 1 - (m_level - 1) * m_attack / m_decay * (1 - m_sustain);
if(m_level <= m_sustain)
nextState(ADSR_STATE_SUSTAIN);
break;
case ADSR_STATE_SUSTAIN:
m_level = m_sustain;
break;
case ADSR_STATE_RELEASE:
if(m_release <= 0)
{
nextState(ADSR_STATE_INVALID);
return;
}
break;
case ADSR_STATE_INVALID:
break;
}
}
void ADSRReader::read(int & length, bool &eos, sample_t* buffer)
{
Specs specs = m_reader->getSpecs();
m_reader->read(length, eos, buffer);
for(int i = 0; i < length; i++)
{
for(int channel = 0; channel < specs.channels; channel++)
{
buffer[i * specs.channels + channel] *= m_level;
}
switch(m_state)
{
case ADSR_STATE_ATTACK:
m_level += 1 / m_attack / specs.rate;
if(m_level >= 1)
nextState(ADSR_STATE_DECAY);
break;
case ADSR_STATE_DECAY:
m_level -= (1 - m_sustain) / m_decay / specs.rate;
if(m_level <= m_sustain)
nextState(ADSR_STATE_SUSTAIN);
break;
case ADSR_STATE_SUSTAIN:
break;
case ADSR_STATE_RELEASE:
m_level -= m_sustain / m_release / specs.rate ;
if(m_level <= 0)
nextState(ADSR_STATE_INVALID);
break;
case ADSR_STATE_INVALID:
length = i;
return;
}
}
}
void ADSRReader::release()
{
nextState(ADSR_STATE_RELEASE);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Accumulator.h"
#include "fx/CallbackIIRFilterReader.h"
AUD_NAMESPACE_BEGIN
sample_t Accumulator::accumulatorFilterAdditive(CallbackIIRFilterReader* reader, void* useless)
{
float in = reader->x(0);
float lastin = reader->x(-1);
float out = reader->y(-1) + in - lastin;
if(in > lastin)
out += in - lastin;
return out;
}
sample_t Accumulator::accumulatorFilter(CallbackIIRFilterReader* reader, void* useless)
{
float in = reader->x(0);
float lastin = reader->x(-1);
float out = reader->y(-1);
if(in > lastin)
out += in - lastin;
return out;
}
Accumulator::Accumulator(std::shared_ptr<ISound> sound,
bool additive) :
Effect(sound),
m_additive(additive)
{
}
std::shared_ptr<IReader> Accumulator::createReader()
{
return std::shared_ptr<IReader>(new CallbackIIRFilterReader(getReader(), 2, 2, m_additive ? accumulatorFilterAdditive : accumulatorFilter));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright 2009-2025 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/AnimateableTimeStretchPitchScale.h"
#include "fx/AnimateableTimeStretchPitchScaleReader.h"
AUD_NAMESPACE_BEGIN
AnimateableTimeStretchPitchScale::AnimateableTimeStretchPitchScale(std::shared_ptr<ISound> sound, float fps, float timeStretch, float pitchScale, StretcherQuality quality,
bool preserveFormant) :
Effect(sound),
m_fps(fps),
m_timeStretch(std::make_shared<AnimateableProperty>(1, timeStretch)),
m_pitchScale(std::make_shared<AnimateableProperty>(1, pitchScale)),
m_quality(quality),
m_preserveFormant(preserveFormant)
{
}
AnimateableTimeStretchPitchScale::AnimateableTimeStretchPitchScale(std::shared_ptr<ISound> sound, float fps, std::shared_ptr<AnimateableProperty> timeStretch,
std::shared_ptr<AnimateableProperty> pitchScale, StretcherQuality quality, bool preserveFormant) :
Effect(sound), m_fps(fps), m_timeStretch(timeStretch), m_pitchScale(pitchScale), m_quality(quality), m_preserveFormant(preserveFormant)
{
}
std::shared_ptr<IReader> AnimateableTimeStretchPitchScale::createReader()
{
return std::make_shared<AnimateableTimeStretchPitchScaleReader>(getReader(), m_fps, m_timeStretch, m_pitchScale, m_quality, m_preserveFormant);
}
bool AnimateableTimeStretchPitchScale::getPreserveFormant() const
{
return m_preserveFormant;
}
StretcherQuality AnimateableTimeStretchPitchScale::getStretcherQuality() const
{
return m_quality;
}
std::shared_ptr<AnimateableProperty> AnimateableTimeStretchPitchScale::getAnimProperty(AnimateablePropertyType type)
{
switch(type)
{
case AP_TIME_STRETCH:
return m_timeStretch;
case AP_PITCH_SCALE:
return m_pitchScale;
default:
return nullptr;
}
}
float AnimateableTimeStretchPitchScale::getFPS() const
{
return m_fps;
}
void AnimateableTimeStretchPitchScale::setFPS(float fps)
{
m_fps = fps;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright 2009-2025 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/AnimateableTimeStretchPitchScaleReader.h"
#include "IReader.h"
AUD_NAMESPACE_BEGIN
AnimateableTimeStretchPitchScaleReader::AnimateableTimeStretchPitchScaleReader(std::shared_ptr<IReader> reader, float fps, std::shared_ptr<AnimateableProperty> timeStretch,
std::shared_ptr<AnimateableProperty> pitchScale, StretcherQuality quality, bool preserveFormant) :
TimeStretchPitchScaleReader(reader, timeStretch->readSingle(0), pitchScale->readSingle(0), quality, preserveFormant),
m_fps(fps),
m_timeStretch(timeStretch),
m_pitchScale(pitchScale)
{
}
void AnimateableTimeStretchPitchScaleReader::read(int& length, bool& eos, sample_t* buffer)
{
int position = getPosition();
double time = double(position) / double(m_reader->getSpecs().rate);
float frame = time * m_fps;
float timeRatio = m_timeStretch->readSingle(frame);
setTimeRatio(timeRatio);
float pitchScale = m_pitchScale->readSingle(frame);
setPitchScale(pitchScale);
TimeStretchPitchScaleReader::read(length, eos, buffer);
}
void AnimateableTimeStretchPitchScaleReader::seek(int position)
{
const double sampleRate = double(m_reader->getSpecs().rate);
const double samplesPerFrame = sampleRate / m_fps;
const double frame = double(position) / samplesPerFrame;
float timeRatio = m_timeStretch->readSingle(frame);
setTimeRatio(timeRatio);
float pitchScale = m_pitchScale->readSingle(frame);
setPitchScale(pitchScale);
const int totalFrames = static_cast<int>(frame);
float ratio = 1.0f;
double inputSamplePos = 0.0;
const sample_t* animationSamples = m_timeStretch->getBuffer().getBuffer();
const int bufferFrames = m_timeStretch->getBuffer().getSize() / (sizeof(sample_t) * m_timeStretch->getCount());
for(int frameIndex = 0; frameIndex < std::min(bufferFrames, totalFrames); frameIndex++)
{
ratio = std::max(animationSamples[frameIndex], 1.0f / 256.0f);
inputSamplePos += samplesPerFrame / ratio;
}
if(totalFrames > bufferFrames)
{
// The position is past the end of animation buffer and so use the last read ratio
// This already includes the fractional frame
inputSamplePos += (samplesPerFrame * (frame - bufferFrames)) / ratio;
}
else
{
// The position is before the end of the animation buffer and so read one last time for the remaining fractional frame
double remainderFrame = frame - totalFrames;
float remainderRatio = std::max(m_timeStretch->readSingle(frame), 1.0f / 256.0f);
inputSamplePos += (samplesPerFrame * remainderFrame) / remainderRatio;
}
m_reader->seek(static_cast<int>(inputSamplePos));
m_finishedReader = false;
m_stretcher->reset();
reset();
m_position = position;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,125 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/BaseIIRFilterReader.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
BaseIIRFilterReader::BaseIIRFilterReader(std::shared_ptr<IReader> reader, int in, int out) :
EffectReader(reader),
m_specs(reader->getSpecs()),
m_xlen(in), m_ylen(out),
m_xpos(0), m_ypos(0), m_channel(0)
{
m_x = new sample_t[m_xlen * m_specs.channels];
m_y = new sample_t[m_ylen * m_specs.channels];
std::memset(m_x, 0, sizeof(sample_t) * m_xlen * m_specs.channels);
std::memset(m_y, 0, sizeof(sample_t) * m_ylen * m_specs.channels);
}
BaseIIRFilterReader::~BaseIIRFilterReader()
{
delete[] m_x;
delete[] m_y;
}
void BaseIIRFilterReader::setLengths(int in, int out)
{
if(in != m_xlen)
{
sample_t* xn = new sample_t[in * m_specs.channels];
std::memset(xn, 0, sizeof(sample_t) * in * m_specs.channels);
for(m_channel = 0; m_channel < m_specs.channels; m_channel++)
{
for(int i = 1; i <= in && i <= m_xlen; i++)
{
xn[(in - i) * m_specs.channels + m_channel] = x(-i);
}
}
delete[] m_x;
m_x = xn;
m_xpos = 0;
m_xlen = in;
}
if(out != m_ylen)
{
sample_t* yn = new sample_t[out * m_specs.channels];
std::memset(yn, 0, sizeof(sample_t) * out * m_specs.channels);
for(m_channel = 0; m_channel < m_specs.channels; m_channel++)
{
for(int i = 1; i <= out && i <= m_ylen; i++)
{
yn[(out - i) * m_specs.channels + m_channel] = y(-i);
}
}
delete[] m_y;
m_y = yn;
m_ypos = 0;
m_ylen = out;
}
}
void BaseIIRFilterReader::read(int& length, bool& eos, sample_t* buffer)
{
Specs specs = m_reader->getSpecs();
if(specs.channels != m_specs.channels)
{
m_specs.channels = specs.channels;
delete[] m_x;
delete[] m_y;
m_x = new sample_t[m_xlen * m_specs.channels];
m_y = new sample_t[m_ylen * m_specs.channels];
std::memset(m_x, 0, sizeof(sample_t) * m_xlen * m_specs.channels);
std::memset(m_y, 0, sizeof(sample_t) * m_ylen * m_specs.channels);
}
if(specs.rate != m_specs.rate)
{
m_specs = specs;
sampleRateChanged(m_specs.rate);
}
m_reader->read(length, eos, buffer);
for(m_channel = 0; m_channel < m_specs.channels; m_channel++)
{
for(int i = 0; i < length; i++)
{
m_x[m_xpos * m_specs.channels + m_channel] = buffer[i * m_specs.channels + m_channel];
m_y[m_ypos * m_specs.channels + m_channel] = buffer[i * m_specs.channels + m_channel] = filter();
m_xpos = m_xlen ? (m_xpos + 1) % m_xlen : 0;
m_ypos = m_ylen ? (m_ypos + 1) % m_ylen : 0;
}
}
}
void BaseIIRFilterReader::sampleRateChanged(SampleRate rate)
{
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,255 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/BinauralReader.h"
#include "Exception.h"
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define NUM_OUTCHANNELS 2
#define NUM_CONVOLVERS 4
#define CROSSFADE_SAMPLES 1024
AUD_NAMESPACE_BEGIN
BinauralReader::BinauralReader(std::shared_ptr<IReader> reader, std::shared_ptr<HRTF> hrtfs, std::shared_ptr<Source> source, std::shared_ptr<ThreadPool> threadPool, std::shared_ptr<FFTPlan> plan) :
m_position(0), m_reader(reader), m_hrtfs(hrtfs), m_source(source), m_N(plan->getSize()), m_transition(false), m_transPos(CROSSFADE_SAMPLES*NUM_OUTCHANNELS), m_eosReader(false), m_eosTail(false), m_threadPool(threadPool)
{
if(m_hrtfs->isEmpty())
AUD_THROW(StateException, "The provided HRTF object is empty");
if(m_reader->getSpecs().channels != 1)
AUD_THROW(StateException, "The sound must have only one channel");
if(m_reader->getSpecs().rate != m_hrtfs->getSpecs().rate)
AUD_THROW(StateException, "The sound and the HRTFs must have the same rate");
m_M = m_L = m_N / 2;
m_RealAzimuth = m_Azimuth = m_source->getAzimuth();
m_RealElevation = m_Elevation = m_source->getElevation();
auto irs = m_hrtfs->getImpulseResponse(m_RealAzimuth, m_RealElevation);
for(unsigned int i = 0; i < NUM_CONVOLVERS; i++)
if(i%NUM_OUTCHANNELS==0)
m_convolvers.push_back(std::unique_ptr<Convolver>(new Convolver(irs.first->getChannel(0), irs.first->getLength(), m_threadPool, plan)));
else
m_convolvers.push_back(std::unique_ptr<Convolver>(new Convolver(irs.second->getChannel(0), irs.second->getLength(), m_threadPool, plan)));
m_futures.resize(NUM_CONVOLVERS);
m_outBuffer = (sample_t*)std::malloc(m_L*NUM_OUTCHANNELS*sizeof(sample_t));
m_eOutBufLen = m_outBufLen = m_outBufferPos = m_L * NUM_OUTCHANNELS;
m_inBuffer = (sample_t*)std::malloc(m_L * sizeof(sample_t));
for(int i = 0; i < NUM_CONVOLVERS; i++)
m_vecOut.push_back((sample_t*)std::calloc(m_L, sizeof(sample_t)));
}
BinauralReader::~BinauralReader()
{
std::free(m_outBuffer);
std::free(m_inBuffer);
for(int i = 0; i < m_vecOut.size(); i++)
std::free(m_vecOut[i]);
}
bool BinauralReader::isSeekable() const
{
return m_reader->isSeekable();
}
void BinauralReader::seek(int position)
{
m_position = position;
m_reader->seek(position);
for(int i = 0; i < NUM_CONVOLVERS; i++)
m_convolvers[i]->reset();
m_eosTail = false;
m_eosReader = false;
m_outBufferPos = m_eOutBufLen = m_outBufLen;
m_transition = false;
m_transPos = CROSSFADE_SAMPLES*NUM_OUTCHANNELS;
}
int BinauralReader::getLength() const
{
return m_reader->getLength();
}
int BinauralReader::getPosition() const
{
return m_position;
}
Specs BinauralReader::getSpecs() const
{
Specs specs = m_reader->getSpecs();
specs.channels = CHANNELS_STEREO;
return specs;
}
void BinauralReader::read(int& length, bool& eos, sample_t* buffer)
{
int samples = 0;
int iteration = 0;
if(length <= 0)
{
length = 0;
eos = (m_eosTail && m_outBufferPos >= m_eOutBufLen);
return;
}
eos = false;
int writePos = 0;
do
{
int bufRest = m_eOutBufLen - m_outBufferPos;
int writeLength = std::min((length*NUM_OUTCHANNELS) - writePos, m_eOutBufLen + bufRest);
if(bufRest < writeLength || (m_eOutBufLen == 0 && m_eosTail))
{
if(bufRest > 0)
std::memcpy(buffer + writePos, m_outBuffer + m_outBufferPos, bufRest*sizeof(sample_t));
if(!m_eosTail)
{
int n = NUM_OUTCHANNELS;
if(m_transition)
n = NUM_CONVOLVERS;
else if(checkSource())
n = NUM_CONVOLVERS;
loadBuffer(n);
int len = std::min(std::abs(writeLength - bufRest), m_eOutBufLen);
std::memcpy(buffer + writePos + bufRest, m_outBuffer, len*sizeof(sample_t));
samples += len;
m_outBufferPos = len;
writeLength = std::min((length*NUM_OUTCHANNELS) - writePos, m_eOutBufLen + bufRest);
}
else
{
m_outBufferPos += bufRest;
length = (writePos+bufRest) / NUM_OUTCHANNELS;
eos = true;
return;
}
}
else
{
std::memcpy(buffer + writePos, m_outBuffer + m_outBufferPos, writeLength*sizeof(sample_t));
m_outBufferPos += writeLength;
}
writePos += writeLength;
iteration++;
} while(writePos < length*NUM_OUTCHANNELS);
m_position += length;
}
bool BinauralReader::checkSource()
{
if((m_Azimuth != m_source->getAzimuth() || m_Elevation != m_source->getElevation()) && (!m_eosReader && !m_eosTail))
{
float az = m_Azimuth = m_source->getAzimuth();
float el = m_Elevation = m_source->getElevation();
auto irs = m_hrtfs->getImpulseResponse(az, el);
if(az != m_RealAzimuth || el != m_RealElevation)
{
m_RealAzimuth = az;
m_RealElevation = el;
for(int i = 0; i < NUM_OUTCHANNELS; i++)
{
auto temp = std::move(m_convolvers[i]);
m_convolvers[i] = std::move(m_convolvers[i + NUM_OUTCHANNELS]);
m_convolvers[i + NUM_OUTCHANNELS] = std::move(temp);
}
for(int i = 0; i < NUM_OUTCHANNELS; i++)
if(i%NUM_OUTCHANNELS == 0)
m_convolvers[i]->setImpulseResponse(irs.first->getChannel(0));
else
m_convolvers[i]->setImpulseResponse(irs.second->getChannel(0));
m_transPos = CROSSFADE_SAMPLES*NUM_OUTCHANNELS;
m_transition = true;
return true;
}
}
return false;
}
void BinauralReader::loadBuffer(int nConvolvers)
{
m_lastLengthIn = m_L;
m_reader->read(m_lastLengthIn, m_eosReader, m_inBuffer);
if(!m_eosReader || m_lastLengthIn > 0)
{
int len = m_lastLengthIn;
for(int i = 0; i < nConvolvers; i++)
m_futures[i] = m_threadPool->enqueue(&BinauralReader::threadFunction, this, i, true);
for(int i = 0; i < nConvolvers; i++)
len = m_futures[i].get();
joinByChannel(0, len, nConvolvers);
m_eOutBufLen = len*NUM_OUTCHANNELS;
}
else if(!m_eosTail)
{
int len = m_lastLengthIn = m_L;
for(int i = 0; i < nConvolvers; i++)
m_futures[i] = m_threadPool->enqueue(&BinauralReader::threadFunction, this, i, false);
for(int i = 0; i < nConvolvers; i++)
len = m_futures[i].get();
joinByChannel(0, len, nConvolvers);
m_eOutBufLen = len*NUM_OUTCHANNELS;
}
}
void BinauralReader::joinByChannel(int start, int len, int nConvolvers)
{
int k = 0;
float vol = 0;
const int l = CROSSFADE_SAMPLES*NUM_OUTCHANNELS;
for(int i = 0; i < len*NUM_OUTCHANNELS; i += NUM_OUTCHANNELS)
{
if(m_transition)
{
vol = (m_transPos - i) / (float)l;
if(vol > 1.0f)
vol = 1.0f;
else if(vol < 0.0f)
vol = 0.0f;
}
for(int j = 0; j < NUM_OUTCHANNELS; j++)
m_outBuffer[i + j + start] = ((m_vecOut[j][k] * (1.0f - vol)) + (m_vecOut[j + NUM_OUTCHANNELS][k] * vol))*m_source->getVolume();
k++;
}
if(m_transition)
{
m_transPos -= len*NUM_OUTCHANNELS;
if(m_transPos <= 0)
{
m_transition = false;
m_transPos = l;
}
}
}
int BinauralReader::threadFunction(int id, bool input)
{
int l = m_lastLengthIn;
if(input)
m_convolvers[id]->getNext(m_inBuffer, m_vecOut[id], l, m_eosTail);
else
m_convolvers[id]->getNext(nullptr, m_vecOut[id], l, m_eosTail);
return l;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/BinauralSound.h"
#include "fx/BinauralReader.h"
#include "Exception.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
BinauralSound::BinauralSound(std::shared_ptr<ISound> sound, std::shared_ptr<HRTF> hrtfs, std::shared_ptr<Source> source, std::shared_ptr<ThreadPool> threadPool) :
BinauralSound(sound, hrtfs, source, threadPool, std::make_shared<FFTPlan>(0.0))
{
}
BinauralSound::BinauralSound(std::shared_ptr<ISound> sound, std::shared_ptr<HRTF> hrtfs, std::shared_ptr<Source> source, std::shared_ptr<ThreadPool> threadPool, std::shared_ptr<FFTPlan> plan) :
m_sound(sound), m_hrtfs(hrtfs), m_source(source), m_threadPool(threadPool), m_plan(plan)
{
}
std::shared_ptr<IReader> BinauralSound::createReader()
{
return std::make_shared<BinauralReader>(m_sound->createReader(), m_hrtfs, m_source, m_threadPool, m_plan);
}
std::shared_ptr<HRTF> BinauralSound::getHRTFs()
{
return m_hrtfs;
}
void BinauralSound::setHRTFs(std::shared_ptr<HRTF> hrtfs)
{
m_hrtfs = hrtfs;
}
std::shared_ptr<Source> BinauralSound::getSource()
{
return m_source;
}
void BinauralSound::setSource(std::shared_ptr<Source> source)
{
m_source = source;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Butterworth.h"
#include "fx/ButterworthCalculator.h"
AUD_NAMESPACE_BEGIN
Butterworth::Butterworth(std::shared_ptr<ISound> sound, float frequency) :
DynamicIIRFilter(sound, std::shared_ptr<IDynamicIIRFilterCalculator>(new ButterworthCalculator(frequency)))
{
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ButterworthCalculator.h"
#include <cmath>
#define BWPB41 0.76536686473
#define BWPB42 1.84775906502
AUD_NAMESPACE_BEGIN
ButterworthCalculator::ButterworthCalculator(float frequency) :
m_frequency(frequency)
{
}
void ButterworthCalculator::recalculateCoefficients(SampleRate rate, std::vector<float> &b, std::vector<float> &a)
{
float omega = 2 * std::tan(m_frequency * M_PI / rate);
float o2 = omega * omega;
float o4 = o2 * o2;
float x1 = o2 + 2.0f * (float)BWPB41 * omega + 4.0f;
float x2 = o2 + 2.0f * (float)BWPB42 * omega + 4.0f;
float y1 = o2 - 2.0f * (float)BWPB41 * omega + 4.0f;
float y2 = o2 - 2.0f * (float)BWPB42 * omega + 4.0f;
float o228 = 2.0f * o2 - 8.0f;
float norm = x1 * x2;
a.push_back(1);
a.push_back((x1 + x2) * o228 / norm);
a.push_back((x1 * y2 + x2 * y1 + o228 * o228) / norm);
a.push_back((y1 + y2) * o228 / norm);
a.push_back(y1 * y2 / norm);
b.push_back(o4 / norm);
b.push_back(4 * o4 / norm);
b.push_back(6 * o4 / norm);
b.push_back(b[1]);
b.push_back(b[0]);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/CallbackIIRFilterReader.h"
AUD_NAMESPACE_BEGIN
CallbackIIRFilterReader::CallbackIIRFilterReader(std::shared_ptr<IReader> reader, int in, int out, doFilterIIR doFilter, endFilterIIR endFilter, void* data) :
BaseIIRFilterReader(reader, in, out),
m_filter(doFilter), m_endFilter(endFilter), m_data(data)
{
}
CallbackIIRFilterReader::~CallbackIIRFilterReader()
{
if(m_endFilter)
m_endFilter(m_data);
}
sample_t CallbackIIRFilterReader::filter()
{
return m_filter(this, m_data);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Convolver.h"
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>
AUD_NAMESPACE_BEGIN
Convolver::Convolver(std::shared_ptr<std::vector<std::shared_ptr<std::vector<std::complex<sample_t>>>>> ir, int irLength, std::shared_ptr<ThreadPool> threadPool, std::shared_ptr<FFTPlan> plan) :
m_N(plan->getSize()), m_M(plan->getSize()/2), m_L(plan->getSize()/2), m_irBuffers(ir), m_numThreads(std::min(threadPool->getNumOfThreads(), static_cast<unsigned int>(m_irBuffers->size() - 1))), m_threadPool(threadPool), m_irLength(irLength), m_tailCounter(0), m_eos(false)
{
m_resetFlag = false;
m_futures.resize(m_numThreads);
for(int i = 0; i < m_irBuffers->size(); i++)
{
m_fftConvolvers.push_back(std::unique_ptr<FFTConvolver>(new FFTConvolver((*m_irBuffers)[i], plan)));
m_delayLine.push_front((fftwf_complex*)std::calloc((m_N / 2) + 1, sizeof(fftwf_complex)));
}
m_accBuffer = (fftwf_complex*)std::calloc((m_N / 2) + 1, sizeof(fftwf_complex));
for(int i = 0; i < m_numThreads; i++)
m_threadAccBuffers.push_back((fftwf_complex*)std::calloc((m_N / 2) + 1, sizeof(fftwf_complex)));
}
Convolver::~Convolver()
{
m_resetFlag = true;
for(auto &fut : m_futures)
if(fut.valid())
fut.get();
std::free(m_accBuffer);
for(auto buf : m_threadAccBuffers)
std::free(buf);
while(!m_delayLine.empty())
{
std::free(m_delayLine.front());
m_delayLine.pop_front();
}
}
void Convolver::getNext(sample_t* inBuffer, sample_t* outBuffer, int& length, bool& eos)
{
if(length > m_L)
{
length = 0;
eos = m_eos;
return;
}
if(m_eos)
{
eos = m_eos;
length = 0;
return;
}
eos = false;
for(auto &fut : m_futures)
if(fut.valid())
fut.get();
if(inBuffer != nullptr)
m_fftConvolvers[0]->getNextFDL(inBuffer, reinterpret_cast<std::complex<sample_t>*>(m_accBuffer), length, m_delayLine[0]);
else
{
m_tailCounter++;
std::memset(outBuffer, 0, m_L*sizeof(sample_t));
m_fftConvolvers[0]->getNextFDL(outBuffer, reinterpret_cast<std::complex<sample_t>*>(m_accBuffer), length, m_delayLine[0]);
}
m_delayLine.push_front(m_delayLine.back());
m_delayLine.pop_back();
length = m_L;
m_fftConvolvers[0]->IFFT_FDL(m_accBuffer, outBuffer, length);
std::memset(m_accBuffer, 0, ((m_N / 2) + 1)*sizeof(fftwf_complex));
if(m_tailCounter >= m_delayLine.size() && inBuffer == nullptr)
{
eos = m_eos = true;
length = m_irLength%m_M;
if(length == 0)
length = m_M;
}
else
for(int i = 0; i < m_futures.size(); i++)
m_futures[i] = m_threadPool->enqueue(&Convolver::threadFunction, this, i);
}
void Convolver::reset()
{
m_resetFlag = true;
for(auto &fut : m_futures)
if(fut.valid())
fut.get();
for(int i = 0; i < m_delayLine.size();i++)
std::memset(m_delayLine[i], 0, ((m_N / 2) + 1)*sizeof(fftwf_complex));
for(int i = 0; i < m_fftConvolvers.size(); i++)
m_fftConvolvers[i]->clear();
std::memset(m_accBuffer, 0, ((m_N / 2) + 1)*sizeof(fftwf_complex));
m_tailCounter = 0;
m_eos = false;
m_resetFlag = false;
}
std::shared_ptr<std::vector<std::shared_ptr<std::vector<std::complex<sample_t>>>>> Convolver::getImpulseResponse()
{
return m_irBuffers;
}
void Convolver::setImpulseResponse(std::shared_ptr<std::vector<std::shared_ptr<std::vector<std::complex<sample_t>>>>> ir)
{
reset();
m_irBuffers = ir;
for(int i = 0; i < m_irBuffers->size(); i++)
m_fftConvolvers[i]->setImpulseResponse((*m_irBuffers)[i]);
}
bool Convolver::threadFunction(int id)
{
int total = m_irBuffers->size();
int share = std::ceil(((float)total - 1) / (float)m_numThreads);
int start = id*share + 1;
int end = std::min(start + share, total);
std::memset(m_threadAccBuffers[id], 0, ((m_N / 2) + 1)*sizeof(fftwf_complex));
for(int i = start; i < end && !m_resetFlag; i++)
m_fftConvolvers[i]->getNextFDL(reinterpret_cast<std::complex<sample_t>*>(m_delayLine[i]), reinterpret_cast<std::complex<sample_t>*>(m_threadAccBuffers[id]));
m_sumMutex.lock();
for(int i = 0; (i < m_N / 2 + 1) && !m_resetFlag; i++)
{
m_accBuffer[i][0] += m_threadAccBuffers[id][i][0];
m_accBuffer[i][1] += m_threadAccBuffers[id][i][1];
}
m_sumMutex.unlock();
return true;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,203 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ConvolverReader.h"
#include "Exception.h"
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
AUD_NAMESPACE_BEGIN
ConvolverReader::ConvolverReader(std::shared_ptr<IReader> reader, std::shared_ptr<ImpulseResponse> ir, std::shared_ptr<ThreadPool> threadPool, std::shared_ptr<FFTPlan> plan) :
m_position(0), m_reader(reader), m_ir(ir), m_N(plan->getSize()), m_eosReader(false), m_eosTail(false), m_inChannels(reader->getSpecs().channels), m_irChannels(ir->getSpecs().channels), m_threadPool(threadPool)
{
m_nChannelThreads = std::min((int)threadPool->getNumOfThreads(), m_inChannels);
m_futures.resize(m_nChannelThreads);
int irLength = m_ir->getLength();
if(m_irChannels != 1 && m_irChannels != m_inChannels)
AUD_THROW(StateException, "The impulse response and the sound must either have the same amount of channels or the impulse response must be mono");
if(m_reader->getSpecs().rate != m_ir->getSpecs().rate)
AUD_THROW(StateException, "The sound and the impulse response. must have the same rate");
m_M = m_L = m_N / 2;
if(m_irChannels > 1)
for(int i = 0; i < m_inChannels; i++)
m_convolvers.push_back(std::unique_ptr<Convolver>(new Convolver(ir->getChannel(i), irLength, m_threadPool, plan)));
else
for(int i = 0; i < m_inChannels; i++)
m_convolvers.push_back(std::unique_ptr<Convolver>(new Convolver(ir->getChannel(0), irLength, m_threadPool, plan)));
for(int i = 0; i < m_inChannels; i++)
m_vecInOut.push_back((sample_t*)std::malloc(m_L*sizeof(sample_t)));
m_outBuffer = (sample_t*)std::malloc(m_L*m_inChannels*sizeof(sample_t));
m_outBufLen = m_eOutBufLen = m_outBufferPos = m_L*m_inChannels;
}
ConvolverReader::~ConvolverReader()
{
std::free(m_outBuffer);
for(int i = 0; i < m_inChannels; i++)
std::free(m_vecInOut[i]);
}
bool ConvolverReader::isSeekable() const
{
return m_reader->isSeekable();
}
void ConvolverReader::seek(int position)
{
m_position = position;
m_reader->seek(position);
for(int i = 0; i < m_inChannels; i++)
m_convolvers[i]->reset();
m_eosTail = false;
m_eosReader = false;
m_outBufferPos = m_eOutBufLen = m_outBufLen;
}
int ConvolverReader::getLength() const
{
return m_reader->getLength();
}
int ConvolverReader::getPosition() const
{
return m_position;
}
Specs ConvolverReader::getSpecs() const
{
return m_reader->getSpecs();
}
void ConvolverReader::read(int& length, bool& eos, sample_t* buffer)
{
if(length <= 0)
{
length = 0;
eos = (m_eosTail && m_outBufferPos >= m_eOutBufLen);
return;
}
eos = false;
int writePos = 0;
do
{
int bufRest = m_eOutBufLen - m_outBufferPos;
int writeLength = std::min((length*m_inChannels) - writePos, m_eOutBufLen + bufRest);
if(bufRest < writeLength || (m_eOutBufLen == 0 && m_eosTail))
{
if(bufRest > 0)
std::memcpy(buffer + writePos, m_outBuffer + m_outBufferPos, bufRest*sizeof(sample_t));
if(!m_eosTail)
{
loadBuffer();
int len = std::min(std::abs(writeLength - bufRest), m_eOutBufLen);
std::memcpy(buffer + writePos + bufRest, m_outBuffer, len*sizeof(sample_t));
m_outBufferPos = len;
writeLength = std::min((length*m_inChannels) - writePos, m_eOutBufLen + bufRest);
}
else
{
m_outBufferPos += bufRest;
length = (writePos + bufRest) / m_inChannels;
eos = true;
return;
}
}
else
{
std::memcpy(buffer + writePos, m_outBuffer + m_outBufferPos, writeLength*sizeof(sample_t));
m_outBufferPos += writeLength;
}
writePos += writeLength;
} while(writePos < length*m_inChannels);
m_position += length;
}
void ConvolverReader::loadBuffer()
{
m_lastLengthIn = m_L;
m_reader->read(m_lastLengthIn, m_eosReader, m_outBuffer);
if(!m_eosReader || m_lastLengthIn>0)
{
divideByChannel(m_outBuffer, m_lastLengthIn*m_inChannels);
int len = m_lastLengthIn;
for(int i = 0; i < m_futures.size(); i++)
m_futures[i] = m_threadPool->enqueue(&ConvolverReader::threadFunction, this, i, true);
for(auto &fut : m_futures)
len = fut.get();
joinByChannel(0, len);
m_eOutBufLen = len*m_inChannels;
}
else if(!m_eosTail)
{
int len = m_lastLengthIn = m_L;
for(int i = 0; i < m_futures.size(); i++)
m_futures[i] = m_threadPool->enqueue(&ConvolverReader::threadFunction, this, i, false);
for(auto &fut : m_futures)
len = fut.get();
joinByChannel(0, len);
m_eOutBufLen = len*m_inChannels;
}
}
void ConvolverReader::divideByChannel(const sample_t* buffer, int len)
{
int k = 0;
for(int i = 0; i < len; i += m_inChannels)
{
for(int j = 0; j < m_inChannels; j++)
m_vecInOut[j][k] = buffer[i + j];
k++;
}
}
void ConvolverReader::joinByChannel(int start, int len)
{
int k = 0;
for(int i = 0; i < len*m_inChannels; i += m_inChannels)
{
for(int j = 0; j < m_vecInOut.size(); j++)
m_outBuffer[i + j + start] = m_vecInOut[j][k];
k++;
}
}
int ConvolverReader::threadFunction(int id, bool input)
{
int share = std::ceil((float)m_inChannels / (float)m_nChannelThreads);
int start = id*share;
int end = std::min(start + share, m_inChannels);
int l=m_lastLengthIn;
for(int i = start; i < end; i++)
if(input)
m_convolvers[i]->getNext(m_vecInOut[i], m_vecInOut[i], l, m_eosTail);
else
m_convolvers[i]->getNext(nullptr, m_vecInOut[i], l, m_eosTail);
return l;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ConvolverSound.h"
#include "fx/ConvolverReader.h"
#include "Exception.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
ConvolverSound::ConvolverSound(std::shared_ptr<ISound> sound, std::shared_ptr<ImpulseResponse> impulseResponse, std::shared_ptr<ThreadPool> threadPool) :
ConvolverSound(sound, impulseResponse, threadPool, std::make_shared<FFTPlan>(0.0))
{
}
ConvolverSound::ConvolverSound(std::shared_ptr<ISound> sound, std::shared_ptr<ImpulseResponse> impulseResponse, std::shared_ptr<ThreadPool> threadPool, std::shared_ptr<FFTPlan> plan) :
m_sound(sound), m_impulseResponse(impulseResponse), m_threadPool(threadPool), m_plan(plan)
{
}
std::shared_ptr<IReader> ConvolverSound::createReader()
{
return std::make_shared<ConvolverReader>(m_sound->createReader(), m_impulseResponse, m_threadPool, m_plan);
}
std::shared_ptr<ImpulseResponse> ConvolverSound::getImpulseResponse()
{
return m_impulseResponse;
}
void ConvolverSound::setImpulseResponse(std::shared_ptr<ImpulseResponse> impulseResponse)
{
m_impulseResponse = impulseResponse;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Delay.h"
#include "fx/DelayReader.h"
AUD_NAMESPACE_BEGIN
Delay::Delay(std::shared_ptr<ISound> sound, double delay) :
Effect(sound),
m_delay(delay)
{
}
double Delay::getDelay() const
{
return m_delay;
}
std::shared_ptr<IReader> Delay::createReader()
{
return std::shared_ptr<IReader>(new DelayReader(getReader(), m_delay));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/DelayReader.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
DelayReader::DelayReader(std::shared_ptr<IReader> reader, double delay) :
EffectReader(reader),
m_delay(int((SampleRate)delay * reader->getSpecs().rate)),
m_remdelay(int((SampleRate)delay * reader->getSpecs().rate))
{
}
void DelayReader::seek(int position)
{
if(position < m_delay)
{
m_remdelay = m_delay - position;
m_reader->seek(0);
}
else
{
m_remdelay = 0;
m_reader->seek(position - m_delay);
}
}
int DelayReader::getLength() const
{
int len = m_reader->getLength();
if(len < 0)
return len;
return len + m_delay;
}
int DelayReader::getPosition() const
{
if(m_remdelay > 0)
return m_delay - m_remdelay;
return m_reader->getPosition() + m_delay;
}
void DelayReader::read(int& length, bool& eos, sample_t* buffer)
{
if(m_remdelay > 0)
{
Specs specs = m_reader->getSpecs();
int samplesize = AUD_SAMPLE_SIZE(specs);
if(length > m_remdelay)
{
std::memset(buffer, 0, m_remdelay * samplesize);
int len = length - m_remdelay;
m_reader->read(len, eos, buffer + m_remdelay * specs.channels);
length = m_remdelay + len;
m_remdelay = 0;
}
else
{
std::memset(buffer, 0, length * samplesize);
m_remdelay -= length;
}
}
else
m_reader->read(length, eos, buffer);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/DynamicIIRFilter.h"
#include "fx/DynamicIIRFilterReader.h"
AUD_NAMESPACE_BEGIN
DynamicIIRFilter::DynamicIIRFilter(std::shared_ptr<ISound> sound,
std::shared_ptr<IDynamicIIRFilterCalculator> calculator) :
Effect(sound),
m_calculator(calculator)
{
}
std::shared_ptr<IReader> DynamicIIRFilter::createReader()
{
return std::shared_ptr<IReader>(new DynamicIIRFilterReader(getReader(), m_calculator));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/DynamicIIRFilterReader.h"
#include "fx/IDynamicIIRFilterCalculator.h"
AUD_NAMESPACE_BEGIN
DynamicIIRFilterReader::DynamicIIRFilterReader(std::shared_ptr<IReader> reader, std::shared_ptr<IDynamicIIRFilterCalculator> calculator) :
IIRFilterReader(reader, std::vector<float>(), std::vector<float>()),
m_calculator(calculator)
{
sampleRateChanged(reader->getSpecs().rate);
}
void DynamicIIRFilterReader::sampleRateChanged(SampleRate rate)
{
std::vector<float> a, b;
m_calculator->recalculateCoefficients(rate, b, a);
setCoefficients(b, a);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,343 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/DynamicMusic.h"
#include <mutex>
#include <condition_variable>
AUD_NAMESPACE_BEGIN
DynamicMusic::DynamicMusic(std::shared_ptr<IDevice> device) :
m_fadeTime(1.0f), m_device(device)
{
m_id = 0;
m_transitioning = false;
m_stopThread = false;
m_volume = m_device->getVolume();
m_scenes.push_back(std::vector<std::shared_ptr<ISound>>(1, nullptr));
}
DynamicMusic::~DynamicMusic()
{
stop();
}
int DynamicMusic::addScene(std::shared_ptr<ISound> sound)
{
std::vector<std::shared_ptr<ISound>> v;
m_scenes.push_back(v);
for(int i = 0; i < m_scenes.size()-1; i++)
m_scenes.back().push_back(nullptr);
for(int i = 0; i < m_scenes.size()-1; i++)
m_scenes[i].push_back(nullptr);
m_scenes.back().push_back(sound);
return m_scenes.size() - 1;
}
bool DynamicMusic::changeScene(int id)
{
if(id >= m_scenes.size() || m_transitioning)
return false;
else
{
if(m_fadeThread.joinable())
m_fadeThread.join();
m_device->lock();
if(id == m_id)
{
m_currentHandle->setVolume(m_volume);
m_currentHandle->setLoopCount(-1);
}
else
{
m_soundTarget = id;
if(m_scenes[m_id][id] == nullptr)
{
m_stopThread = false;
if((m_scenes[m_id][m_id] != nullptr && m_currentHandle->getStatus() != STATUS_INVALID) || m_scenes[m_soundTarget][m_soundTarget] != nullptr)
{
m_transitioning = true;
if(m_scenes[m_id][m_id] == nullptr || m_currentHandle->getStatus() == STATUS_INVALID)
{
m_device->lock();
m_currentHandle = m_device->play(m_scenes[m_soundTarget][m_soundTarget]);
m_currentHandle->setVolume(0.0f);
m_currentHandle->setLoopCount(-1);
m_device->unlock();
m_fadeThread = std::thread(&DynamicMusic::fadeInThread, this);
}
else
{
if(m_scenes[m_soundTarget][m_soundTarget] != nullptr)
{
m_device->lock();
m_transitionHandle = m_currentHandle;
m_currentHandle = m_device->play(m_scenes[m_soundTarget][m_soundTarget]);
m_currentHandle->setVolume(0.0f);
m_currentHandle->setLoopCount(-1);
m_device->unlock();
m_fadeThread = std::thread(&DynamicMusic::crossfadeThread, this);
}
else
{
m_transitionHandle = m_currentHandle;
m_currentHandle = nullptr;
m_fadeThread = std::thread(&DynamicMusic::fadeOutThread, this);
}
}
}
}
else
{
if(m_scenes[m_id][m_id] == nullptr || m_currentHandle->getStatus() == STATUS_INVALID)
transitionCallback(this);
else
{
m_currentHandle->setLoopCount(0);
m_currentHandle->setStopCallback(transitionCallback, this);
}
}
}
m_device->unlock();
return true;
}
}
int DynamicMusic::getScene()
{
return m_id;
}
bool DynamicMusic::addTransition(int init, int end, std::shared_ptr<ISound> sound)
{
if(init != end && init < m_scenes.size() && end < m_scenes.size() && init >= 0 && end >= 0)
{
m_scenes[init][end] = sound;
return true;
}
return false;
}
void DynamicMusic::setFadeTime(double seconds)
{
m_device->lock();
m_fadeTime = seconds;
m_device->unlock();
}
double DynamicMusic::getFadeTime()
{
return m_fadeTime;
}
bool DynamicMusic::resume()
{
bool result = false, resultTrans = false;
if(m_currentHandle != nullptr)
result = m_currentHandle->resume();
if(m_transitionHandle != nullptr)
resultTrans = m_transitionHandle->resume();
return result || resultTrans;
}
bool DynamicMusic::pause()
{
bool result = false, resultTrans = false;
if(m_currentHandle != nullptr)
result = m_currentHandle->pause();
if(m_transitionHandle != nullptr)
resultTrans = m_transitionHandle->pause();
return result || resultTrans;
}
bool DynamicMusic::seek(double position)
{
bool result = false;
if(m_currentHandle != nullptr)
{
result = m_currentHandle->seek(position);
if(m_transitionHandle != nullptr && result == true)
m_transitionHandle->stop();
}
return result;
}
double DynamicMusic::getPosition()
{
double result = 0.0f;
if(m_currentHandle != nullptr)
result = m_currentHandle->getPosition();
return result;
}
float DynamicMusic::getVolume()
{
return m_volume;
}
bool DynamicMusic::setVolume(float volume)
{
m_volume = volume;
bool result = false, resultTrans = false;
if(m_currentHandle != nullptr)
result = m_currentHandle->setVolume(volume);
if(m_transitionHandle != nullptr)
{
m_device->lock();
if(volume<m_transitionHandle->getVolume())
resultTrans = m_transitionHandle->setVolume(0.0f);
m_device->unlock();
}
if(m_currentHandle == nullptr && m_transitionHandle == nullptr)
result = true;
return result || resultTrans;
}
Status DynamicMusic::getStatus()
{
if(m_currentHandle != nullptr)
{
Status result = m_currentHandle->getStatus();
return result;
}
else
return STATUS_INVALID;
}
bool DynamicMusic::stop()
{
m_stopThread = true;
bool result = false, resultTrans = false;
if(m_currentHandle != nullptr)
result = m_currentHandle->stop();
if(m_transitionHandle != nullptr)
resultTrans = m_transitionHandle->stop();
if(m_fadeThread.joinable())
m_fadeThread.join();
m_id = 0;
return result || resultTrans;
}
void DynamicMusic::transitionCallback(void* player)
{
auto dat = reinterpret_cast<DynamicMusic*>(player);
dat->m_transitioning = true;
dat->m_device->lock();
dat->m_currentHandle = dat->m_device->play(dat->m_scenes[dat->m_id][dat->m_soundTarget]);
dat->m_currentHandle->setVolume(dat->m_volume);
if(dat->m_scenes[dat->m_soundTarget][dat->m_soundTarget] != nullptr)
dat->m_currentHandle->setStopCallback(sceneCallback, player);
dat->m_device->unlock();
}
void DynamicMusic::sceneCallback(void* player)
{
auto dat = reinterpret_cast<DynamicMusic*>(player);
dat->m_device->lock();
dat->m_currentHandle = dat->m_device->play(dat->m_scenes[dat->m_soundTarget][dat->m_soundTarget]);
dat->m_currentHandle->setVolume(dat->m_volume);
dat->m_currentHandle->setLoopCount(-1);
dat->m_device->unlock();
dat->m_id = int(dat->m_soundTarget);
dat->m_soundTarget = -1;
dat->m_transitioning = false;
}
void DynamicMusic::crossfadeThread()
{
float currentVol = m_transitionHandle->getVolume();
float nextVol = m_currentHandle->getVolume();
float increment;
while(nextVol < m_volume && !m_stopThread)
{
increment = (m_volume / (m_fadeTime * 1000)) * 20;
currentVol -= increment;
nextVol += increment;
if(currentVol < 0)
currentVol = 0;
if(nextVol > m_volume)
nextVol = m_volume;
m_transitionHandle->setVolume(currentVol);
m_currentHandle->setVolume(nextVol);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
if(m_stopThread)
m_transitionHandle->setVolume(m_volume);
m_transitionHandle->stop();
m_id = int(m_soundTarget);
m_transitioning = false;
}
void DynamicMusic::fadeInThread()
{
float nextVol = m_currentHandle->getVolume();
float increment;
while(nextVol < m_volume && !m_stopThread)
{
increment = (m_volume / (m_fadeTime * 1000)) * 20;
nextVol += increment;
if(nextVol > m_volume)
nextVol = m_volume;
m_currentHandle->setVolume(nextVol);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
if(m_stopThread)
m_currentHandle->setVolume(m_volume);
m_id = int(m_soundTarget);
m_transitioning = false;
}
void DynamicMusic::fadeOutThread()
{
float currentVol = m_transitionHandle->getVolume();
float increment;
while(currentVol > 0.0f && !m_stopThread)
{
increment = (m_volume / (m_fadeTime * 1000)) * 20;
currentVol -= increment;
if(currentVol < 0)
currentVol = 0;
m_transitionHandle->setVolume(currentVol);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
m_transitionHandle->stop();
m_id = int(m_soundTarget);
m_transitioning = false;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,17 @@
#include "fx/Echo.h"
#include "fx/EchoReader.h"
AUD_NAMESPACE_BEGIN
Echo::Echo(std::shared_ptr<ISound> sound, float delay, float feedback, float mix, bool resetBuffer) :
Effect(sound), m_delay(delay), m_feedback(feedback), m_mix(mix), m_resetBuffer(resetBuffer)
{
}
std::shared_ptr<IReader> Echo::createReader()
{
return std::make_shared<EchoReader>(getReader(), m_delay, m_feedback, m_mix, m_resetBuffer);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright 2009-2025 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/EchoReader.h"
#include <cstring>
#include "IReader.h"
#include "util/Buffer.h"
AUD_NAMESPACE_BEGIN
EchoReader::EchoReader(std::shared_ptr<IReader> reader, float delay, float feedback, float mix, bool resetBuffer) :
EffectReader(reader), m_delay(delay), m_feedback(feedback), m_mix(mix), m_resetBuffer(resetBuffer)
{
}
void EchoReader::read(int& length, bool& eos, sample_t* buffer)
{
auto specs = m_reader->getSpecs();
auto delaySamples = static_cast<int>(m_delay * specs.rate);
m_inBuffer.assureSize(length * AUD_SAMPLE_SIZE(specs));
// Note: should this ever do something another time than in the beginning,
// it will likely cause an audible glitch, as samples are not reordered
m_delayBuffer.assureSize(delaySamples * AUD_SAMPLE_SIZE(specs));
m_reader->read(length, eos, m_inBuffer.getBuffer());
sample_t* delayBuffer = m_delayBuffer.getBuffer();
for(int i = 0; i < length; i++)
{
for(int channel = 0; channel < specs.channels; channel++)
{
int delayPosition = ((m_writePosition + i) % delaySamples) * specs.channels + channel;
sample_t inSample = m_inBuffer.getBuffer()[i * specs.channels + channel];
sample_t delayedSample = delayPosition < m_samplesAvailable * specs.channels ? delayBuffer[delayPosition] : 0;
sample_t outSample = inSample + delayedSample * m_feedback;
buffer[i * specs.channels + channel] = inSample * (1.0f - m_mix) + outSample * m_mix;
// Update delay buffer with feedback
delayBuffer[delayPosition] = outSample;
}
}
m_writePosition = (m_writePosition + length) % delaySamples;
m_samplesAvailable = std::min(delaySamples, m_samplesAvailable + length);
}
void EchoReader::seek(int position)
{
m_reader->seek(position);
if(m_resetBuffer)
{
m_samplesAvailable = 0;
m_writePosition = 0;
}
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Effect.h"
AUD_NAMESPACE_BEGIN
Effect::Effect(std::shared_ptr<ISound> sound)
{
m_sound = sound;
}
Effect::~Effect()
{
}
std::shared_ptr<ISound> Effect::getSound() const
{
return m_sound;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/EffectReader.h"
AUD_NAMESPACE_BEGIN
EffectReader::EffectReader(std::shared_ptr<IReader> reader)
{
m_reader = reader;
}
EffectReader::~EffectReader()
{
}
bool EffectReader::isSeekable() const
{
return m_reader->isSeekable();
}
void EffectReader::seek(int position)
{
m_reader->seek(position);
}
int EffectReader::getLength() const
{
return m_reader->getLength();
}
int EffectReader::getPosition() const
{
return m_reader->getPosition();
}
Specs EffectReader::getSpecs() const
{
return m_reader->getSpecs();
}
void EffectReader::read(int& length, bool& eos, sample_t* buffer)
{
m_reader->read(length, eos, buffer);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Envelope.h"
#include "fx/CallbackIIRFilterReader.h"
#include <cmath>
AUD_NAMESPACE_BEGIN
struct EnvelopeParameters
{
float attack;
float release;
float threshold;
float arthreshold;
};
sample_t Envelope::envelopeFilter(CallbackIIRFilterReader* reader, EnvelopeParameters* param)
{
float in = std::fabs(reader->x(0));
float out = reader->y(-1);
if(in < param->threshold)
in = 0.0f;
return (in > out ? param->attack : param->release) * (out - in) + in;
}
void Envelope::endEnvelopeFilter(EnvelopeParameters* param)
{
delete param;
}
Envelope::Envelope(std::shared_ptr<ISound> sound, float attack, float release, float threshold, float arthreshold) :
Effect(sound),
m_attack(attack),
m_release(release),
m_threshold(threshold),
m_arthreshold(arthreshold)
{
}
std::shared_ptr<IReader> Envelope::createReader()
{
std::shared_ptr<IReader> reader = getReader();
EnvelopeParameters* param = new EnvelopeParameters();
param->arthreshold = m_arthreshold;
param->attack = std::pow(m_arthreshold, 1.0f/(static_cast<float>(reader->getSpecs().rate) * m_attack));
param->release = std::pow(m_arthreshold, 1.0f/(static_cast<float>(reader->getSpecs().rate) * m_release));
param->threshold = m_threshold;
return std::shared_ptr<IReader>(new CallbackIIRFilterReader(reader, 1, 2,
(doFilterIIR) envelopeFilter,
(endFilterIIR) endEnvelopeFilter,
param));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,367 @@
/*******************************************************************************
* Copyright 2022 Marcos Perez Gonzalez
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Equalizer.h"
#include <chrono>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include "Exception.h"
#include "fx/ConvolverReader.h"
#include "fx/ImpulseResponse.h"
#include "util/Buffer.h"
#include "util/FFTPlan.h"
#include "util/ThreadPool.h"
AUD_NAMESPACE_BEGIN
Equalizer::Equalizer(std::shared_ptr<ISound> sound, std::shared_ptr<Buffer> bufEQ, int externalSizeEq, float maxFreqEq, int sizeConversion) : m_sound(sound), m_bufEQ(bufEQ)
{
this->maxFreqEq = maxFreqEq;
this->external_size_eq = externalSizeEq;
filter_length = sizeConversion;
}
Equalizer::~Equalizer()
{
}
std::shared_ptr<IReader> Equalizer::createReader()
{
std::shared_ptr<FFTPlan> fp = std::shared_ptr<FFTPlan>(new FFTPlan(filter_length));
// 2 threads to start with
return std::shared_ptr<ConvolverReader>(new ConvolverReader(m_sound->createReader(), createImpulseResponse(), std::shared_ptr<ThreadPool>(new ThreadPool(2)), fp));
}
float calculateValueArray(float* data, float minX, float maxX, int length, float posX)
{
if(posX < minX)
return 1.0;
if(posX > maxX)
return data[length - 1];
float interval = (maxX - minX) / (float) length;
int idx = (int) ((posX - minX) / interval);
return data[idx];
}
void complex_prod(float a, float b, float c, float d, float* r, float* imag)
{
float prod1 = a * c;
float prod2 = b * d;
float prod3 = (a + b) * (c + d);
// Real Part
*r = prod1 - prod2;
// Imaginary Part
*imag = prod3 - (prod1 + prod2);
}
/**
* The creation of the ImpuseResponse which will be convoluted with the sound
*
* The implementation is based on scikit-signal
*/
std::shared_ptr<ImpulseResponse> Equalizer::createImpulseResponse()
{
std::shared_ptr<FFTPlan> fp = std::shared_ptr<FFTPlan>(new FFTPlan(filter_length));
fftwf_complex* buffer = (fftwf_complex*) fp->getBuffer();
std::memset(buffer, 0, filter_length * sizeof(fftwf_complex));
std::shared_ptr<IReader> soundReader = m_sound.get()->createReader();
Specs specsSound = soundReader.get()->getSpecs();
int sampleRate = specsSound.rate;
for(unsigned i = 0; i < filter_length / 2; i++)
{
double freq = (((float) i) / (float) filter_length) * (float) sampleRate;
double dbGain = calculateValueArray(m_bufEQ->getBuffer(), 0.0, maxFreqEq, external_size_eq, freq);
// gain = 10^(decibels / 20.0)
// 0 db = 1
// 20 db = 10
// 40 db = 100
float gain = (float) pow(10.0, dbGain / 20.0);
if(i == filter_length / 2 - 1)
{
gain = 0;
}
// IMPORTANT!!!! It is needed for the minimum phase step.
// Without this, the amplitude would be square rooted
//
gain *= gain;
// Calculation of exponential with std.. or "by hand"
/*
std::complex<float> preShift= std::complex<float>(0.0, -(filter_length - 1)
/ 2. * M_PI * freq / ( sampleRate/2)); std::complex<float> shift =
std::exp(preShift);
std::complex<float> cGain = gain * shift;
*/
float imaginary_shift = -(filter_length - 1) / 2. * M_PI * freq / (sampleRate / 2);
float cGain_real = gain * cos(imaginary_shift);
float cGain_imag = gain * sin(imaginary_shift);
int i2 = filter_length - i - 1;
buffer[i][0] = cGain_real; // Real
buffer[i][1] = cGain_imag; // Imag
if(i > 0 && i2 < filter_length)
{
buffer[i2][0] = cGain_real; // Real
buffer[i2][1] = cGain_imag; // Imag
}
}
// In place. From Complex to sample_t
fp->IFFT(buffer);
// Window Hamming
sample_t* pt_sample_t = (sample_t*) buffer;
float half_filter = ((float) filter_length) / 2.0;
for(int i = 0; i < filter_length; i++)
{
// Centered in filter_length/2
float window = 0.54 - 0.46 * cos((2 * M_PI * (float) i) / (float) (filter_length - 1));
pt_sample_t[i] *= window;
}
std::shared_ptr<Buffer> b2 = std::shared_ptr<Buffer>(new Buffer(filter_length * sizeof(sample_t)));
sample_t* buffer_real = (sample_t*) buffer;
sample_t* buffer2 = b2->getBuffer();
float normaliziter = (float) filter_length;
for(int i = 0; i < filter_length; i++)
{
buffer2[i] = (buffer_real[i] / normaliziter);
}
fp->freeBuffer(buffer);
//
// Here b2 is the buffer with a "valid" FIR (remember the squared amplitude
//
std::shared_ptr<Buffer> ir_minimum = minimumPhaseFilterHomomorphic(b2, filter_length, -1);
Specs specsIR;
specsIR.rate = sampleRate;
specsIR.channels = CHANNELS_MONO;
return std::shared_ptr<ImpulseResponse>(new ImpulseResponse(std::shared_ptr<StreamBuffer>(new StreamBuffer(ir_minimum, specsIR)), fp));
}
std::shared_ptr<Buffer> Equalizer::minimumPhaseFilterHomomorphic(std::shared_ptr<Buffer> original, int lOriginal, int lWork)
{
void* b_orig = original->getBuffer();
if(lWork < lOriginal || lWork < 0)
{
lWork = (int) pow(2, ceil(log2((float) (2 * (lOriginal - 1) / 0.01))));
}
std::shared_ptr<FFTPlan> fp = std::shared_ptr<FFTPlan>(new FFTPlan(lWork, 0.1));
fftwf_complex* buffer = (fftwf_complex*) fp->getBuffer();
sample_t* b_work = (sample_t*) buffer;
// Padding with 0
std::memset(b_work, 0, lWork * sizeof(sample_t));
std::memcpy(b_work, b_orig, lOriginal * sizeof(sample_t));
fp->FFT(b_work);
for(int i = 0; i < lWork / 2; i++)
{
buffer[i][0] = fabs(sqrt(buffer[i][0] * buffer[i][0] + buffer[i][1] * buffer[i][1]));
buffer[i][1] = 0.0;
int conjugate = lWork - i - 1;
buffer[conjugate][0] = buffer[i][0];
buffer[conjugate][1] = 0.0;
}
double threshold = pow(10.0, -7);
float logThreshold = (float) log(threshold);
// take 0.25*log(|H|**2) = 0.5*log(|H|)
for(int i = 0; i < lWork; i++)
{
if(buffer[i][0] < threshold)
{
buffer[i][0] = 0.5 * logThreshold;
}
else
{
buffer[i][0] = 0.5 * log(buffer[i][0]);
}
}
fp->IFFT(buffer);
// homomorphic filter
int stop = (lOriginal + 1) / 2;
b_work[0] = b_work[0] / (float) lWork;
for(int i = 1; i < stop; i++)
{
b_work[i] = b_work[i] / (float) lWork * 2.0;
}
for(int i = stop; i < lWork; i++)
{
b_work[i] = 0;
}
fp->FFT(buffer);
// EXP
// e^x = e^ (a+bi)= e^a * e^bi = e^a * (cos b + i sin b)
for(int i = 0; i < lWork / 2; i++)
{
float new_real;
float new_imag;
new_real = exp(buffer[i][0]) * cos(buffer[i][1]);
new_imag = exp(buffer[i][0]) * sin(buffer[i][1]);
buffer[i][0] = new_real;
buffer[i][1] = new_imag;
int conjugate = lWork - i - 1;
buffer[conjugate][0] = new_real;
buffer[conjugate][1] = new_imag;
}
// IFFT
fp->IFFT(buffer);
// Create new clean Buffer with only the result and normalization
int lOut = (lOriginal / 2) + lOriginal % 2;
std::shared_ptr<Buffer> bOut = std::shared_ptr<Buffer>(new Buffer(sizeof(float) * lOut));
float* bbOut = (float*) bOut->getBuffer();
// Copy and normalize
for(int i = 0; i < lOut; i++)
{
bbOut[i] = b_work[i] / (float) lWork;
}
fp->freeBuffer(buffer);
return bOut;
}
std::shared_ptr<Buffer> Equalizer::minimumPhaseFilterHilbert(std::shared_ptr<Buffer> original, int lOriginal, int lWork)
{
void* b_orig = original->getBuffer();
if(lWork < lOriginal || lWork < 0)
{
lWork = (int) pow(2, ceil(log2((float) (2 * (lOriginal - 1) / 0.01))));
}
std::shared_ptr<FFTPlan> fp = std::shared_ptr<FFTPlan>(new FFTPlan(lWork, 0.1));
fftwf_complex* buffer = (fftwf_complex*) fp->getBuffer();
sample_t* b_work = (sample_t*) buffer;
// Padding with 0
std::memset(b_work, 0, lWork * sizeof(sample_t));
std::memcpy(b_work, b_orig, lOriginal * sizeof(sample_t));
fp->FFT(b_work);
float mymax, mymin;
float n_half = (float) (lOriginal >> 1);
for(int i = 0; i < lWork; i++)
{
float w = ((float) i) * 2.0 * M_PI / (float) lWork * n_half;
float f1 = cos(w);
float f2 = sin(w);
float f3, f4;
complex_prod(buffer[i][0], buffer[i][1], f1, f2, &f3, &f4);
buffer[i][0] = f3;
buffer[i][1] = 0.0;
if(i == 0)
{
mymax = f3;
mymin = f3;
}
else
{
if(f3 < mymin)
mymin = f3;
if(f3 > mymax)
mymax = f3;
}
}
float dp = mymax - 1;
float ds = 0 - mymin;
float S = 4.0 / pow(2, (sqrt(1 + dp + ds) + sqrt(1 - dp + ds)));
for(int i = 0; i < lWork; i++)
{
buffer[i][0] = sqrt((buffer[i][0] + ds) * S) + 1.0E-10;
}
fftwf_complex* buffer_tmp = (fftwf_complex*) std::malloc(lWork * sizeof(fftwf_complex));
std::memcpy(buffer_tmp, buffer, lWork * sizeof(fftwf_complex));
//
// Hilbert transform
//
int midpt = lWork >> 1;
for(int i = 0; i < lWork; i++)
buffer[i][0] = log(buffer[i][0]);
fp->IFFT(buffer);
b_work[0] = 0.0;
for(int i = 1; i < midpt; i++)
{
b_work[i] /= (float) lWork;
}
b_work[midpt] = 0.0;
for(int i = midpt + 1; i < lWork; i++)
{
b_work[i] /= (-1.0 * lWork);
}
fp->FFT(b_work);
// Exp
for(int i = 0; i < lWork; i++)
{
float base = exp(buffer[i][0]);
buffer[i][0] = base * cos(buffer[i][1]);
buffer[i][1] = base * sin(buffer[i][1]);
complex_prod(buffer_tmp[i][0], buffer_tmp[i][1], buffer[i][0], buffer[i][1], &(buffer[i][0]), &(buffer[i][1]));
}
std::free(buffer_tmp);
fp->IFFT(buffer);
//
// Copy and normalization
//
int n_out = n_half + lOriginal % 2;
std::shared_ptr<Buffer> b_minimum = std::shared_ptr<Buffer>(new Buffer(n_out * sizeof(sample_t)));
std::memcpy(b_minimum->getBuffer(), buffer, n_out * sizeof(sample_t));
sample_t* b_final = (sample_t*) b_minimum->getBuffer();
for(int i = 0; i < n_out; i++)
{
b_final[i] /= (float) lWork;
}
return b_minimum;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,214 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/FFTConvolver.h"
#include <cstring>
#include <cstdlib>
AUD_NAMESPACE_BEGIN
FFTConvolver::FFTConvolver(std::shared_ptr<std::vector<std::complex<sample_t>>> ir, std::shared_ptr<FFTPlan> plan) :
m_plan(plan), m_N(plan->getSize()), m_M(plan->getSize()/2), m_L(plan->getSize()/2), m_irBuffer(ir), m_tailPos(0)
{
m_tail = (float*)calloc(m_M - 1, sizeof(float));
m_realBufLen = ((m_N / 2) + 1) * 2;
m_inBuffer = nullptr;
m_shiftBuffer = (sample_t*)std::calloc(m_N, sizeof(sample_t));
}
FFTConvolver::~FFTConvolver()
{
std::free(m_tail);
std::free(m_shiftBuffer);
if(m_inBuffer != nullptr)
m_plan->freeBuffer(m_inBuffer);
}
void FFTConvolver::getNext(const sample_t* inBuffer, sample_t* outBuffer, int& length)
{
if(length > m_L || length <= 0)
{
length = 0;
return;
}
if(m_inBuffer == nullptr)
m_inBuffer = reinterpret_cast<std::complex<sample_t>*>(m_plan->getBuffer());
std::memset(m_inBuffer, 0, m_realBufLen * sizeof(fftwf_complex));
std::memcpy(m_inBuffer, inBuffer, length*sizeof(sample_t));
m_plan->FFT(m_inBuffer);
for(int i = 0; i < m_realBufLen / 2; i++)
{
m_inBuffer[i] = m_inBuffer[i] * (*m_irBuffer)[i] / sample_t(m_N);
}
m_plan->IFFT(m_inBuffer);
for(int i = 0; i < m_M - 1; i++)
((float*)m_inBuffer)[i] += m_tail[i];
for(int i = 0; i < m_M - 1; i++)
m_tail[i] = ((float*)m_inBuffer)[i + length];
std::memcpy(outBuffer, m_inBuffer, length * sizeof(sample_t));
}
void FFTConvolver::getNext(const sample_t* inBuffer, sample_t* outBuffer, int& length, fftwf_complex* transformedData)
{
if(length > m_L || length <= 0)
{
length = 0;
return;
}
if(m_inBuffer == nullptr)
m_inBuffer = reinterpret_cast<std::complex<sample_t>*>(m_plan->getBuffer());
std::memset(m_inBuffer, 0, m_realBufLen * sizeof(fftwf_complex));
std::memcpy(m_inBuffer, inBuffer, length*sizeof(sample_t));
m_plan->FFT(m_inBuffer);
std::memcpy(transformedData, m_inBuffer, (m_realBufLen / 2)*sizeof(fftwf_complex));
for(int i = 0; i < m_realBufLen / 2; i++)
{
m_inBuffer[i] = m_inBuffer[i] * (*m_irBuffer)[i] / sample_t(m_N);
}
m_plan->IFFT(m_inBuffer);
for(int i = 0; i < m_M - 1; i++)
((float*)m_inBuffer)[i] += m_tail[i];
for(int i = 0; i < m_M - 1; i++)
m_tail[i] = ((float*)m_inBuffer)[i + length];
std::memcpy(outBuffer, m_inBuffer, length * sizeof(sample_t));
}
void FFTConvolver::getNext(const fftwf_complex* inBuffer, sample_t* outBuffer, int& length)
{
if(length > m_L || length <= 0)
{
length = 0;
return;
}
if(m_inBuffer == nullptr)
m_inBuffer = reinterpret_cast<std::complex<sample_t>*>(m_plan->getBuffer());
std::memset(m_inBuffer, 0, m_realBufLen * sizeof(fftwf_complex));
for(int i = 0; i < m_realBufLen / 2; i++)
{
m_inBuffer[i] = m_inBuffer[i] * (*m_irBuffer)[i] / sample_t(m_N);
}
m_plan->IFFT(m_inBuffer);
for(int i = 0; i < m_M - 1; i++)
((float*)m_inBuffer)[i] += m_tail[i];
for(int i = 0; i < m_M - 1; i++)
m_tail[i] = ((float*)m_inBuffer)[i + length];
std::memcpy(outBuffer, m_inBuffer, length * sizeof(sample_t));
}
void FFTConvolver::getTail(int& length, bool& eos, sample_t* buffer)
{
if(length <= 0)
{
length = 0;
eos = m_tailPos >= m_M - 1;
return;
}
eos = false;
if(m_tailPos + length > m_M - 1)
{
length = m_M - 1 - m_tailPos;
if(length < 0)
length = 0;
eos = true;
m_tailPos = m_M - 1;
}
else
m_tailPos += length;
std::memcpy(buffer, m_tail, length*sizeof(sample_t));
}
void FFTConvolver::clear()
{
std::memset(m_shiftBuffer, 0, m_N * sizeof(sample_t));
std::memset(m_tail, 0, m_M - 1);
}
void FFTConvolver::IFFT_FDL(const fftwf_complex* inBuffer, sample_t* outBuffer, int& length)
{
if(length > m_L || length <= 0)
{
length = 0;
return;
}
if(m_inBuffer == nullptr)
m_inBuffer = reinterpret_cast<std::complex<sample_t>*>(m_plan->getBuffer());
std::memset(m_inBuffer, 0, m_realBufLen * sizeof(fftwf_complex));
std::memcpy(m_inBuffer, inBuffer, (m_realBufLen / 2)*sizeof(fftwf_complex));
m_plan->IFFT(m_inBuffer);
std::memcpy(outBuffer, ((sample_t*)m_inBuffer)+m_L, length*sizeof(sample_t));
}
void FFTConvolver::getNextFDL(const std::complex<sample_t>* inBuffer, std::complex<sample_t>* accBuffer)
{
for(int i = 0; i < m_realBufLen / 2; i++)
{
accBuffer[i] += (inBuffer[i] * (*m_irBuffer)[i]) / sample_t(m_N);
}
}
void FFTConvolver::getNextFDL(const sample_t* inBuffer, std::complex<sample_t>* accBuffer, int& length, fftwf_complex* transformedData)
{
if(length > m_L || length <= 0)
{
length = 0;
return;
}
if(m_inBuffer == nullptr)
m_inBuffer = reinterpret_cast<std::complex<sample_t>*>(m_plan->getBuffer());
std::memcpy(m_shiftBuffer, m_shiftBuffer + m_L, m_L*sizeof(sample_t));
std::memcpy(m_shiftBuffer + m_L, inBuffer, length*sizeof(sample_t));
std::memset(m_inBuffer, 0, m_realBufLen * sizeof(fftwf_complex));
std::memcpy(m_inBuffer, m_shiftBuffer, (m_L+length)*sizeof(sample_t));
m_plan->FFT(m_inBuffer);
std::memcpy(transformedData, m_inBuffer, (m_realBufLen / 2)*sizeof(fftwf_complex));
for(int i = 0; i < m_realBufLen / 2; i++)
{
accBuffer[i] += (m_inBuffer[i] * (*m_irBuffer)[i]) / sample_t(m_N);
}
}
void FFTConvolver::setImpulseResponse(std::shared_ptr<std::vector<std::complex<sample_t>>> ir)
{
clear();
m_irBuffer = ir;
}
std::shared_ptr<std::vector<std::complex<sample_t>>> FFTConvolver::getImpulseResponse()
{
return m_irBuffer;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Fader.h"
AUD_NAMESPACE_BEGIN
Fader::Fader(std::shared_ptr<ISound> sound, FadeType type, double start, double length) :
Effect(sound),
m_type(type),
m_start(start),
m_length(length)
{
}
FadeType Fader::getType() const
{
return m_type;
}
double Fader::getStart() const
{
return m_start;
}
double Fader::getLength() const
{
return m_length;
}
std::shared_ptr<IReader> Fader::createReader()
{
return std::shared_ptr<IReader>(new FaderReader(getReader(), m_type, m_start, m_length));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/FaderReader.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
FaderReader::FaderReader(std::shared_ptr<IReader> reader, FadeType type, double start, double length) :
EffectReader(reader),
m_type(type),
m_start(start),
m_length(length)
{
}
void FaderReader::read(int& length, bool& eos, sample_t* buffer)
{
int position = m_reader->getPosition();
Specs specs = m_reader->getSpecs();
int samplesize = AUD_SAMPLE_SIZE(specs);
m_reader->read(length, eos, buffer);
if((position + length) / specs.rate <= m_start)
{
if(m_type != FADE_OUT)
{
std::memset(buffer, 0, length * samplesize);
}
}
else if(position / specs.rate >= m_start+m_length)
{
if(m_type == FADE_OUT)
{
std::memset(buffer, 0, length * samplesize);
}
}
else
{
float volume = 1.0f;
for(int i = 0; i < length * specs.channels; i++)
{
if(i % specs.channels == 0)
{
volume = float((((position + i) / specs.rate) - m_start) / m_length);
if(volume > 1.0f)
volume = 1.0f;
else if(volume < 0.0f)
volume = 0.0f;
if(m_type == FADE_OUT)
volume = 1.0f - volume;
}
buffer[i] = buffer[i] * volume;
}
}
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,122 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/HRTF.h"
#include "Exception.h"
#include <cmath>
AUD_NAMESPACE_BEGIN
HRTF::HRTF() :
HRTF(std::make_shared<FFTPlan>(0.0))
{
}
HRTF::HRTF(std::shared_ptr<FFTPlan> plan) :
m_plan(plan)
{
m_specs.channels = CHANNELS_INVALID;
m_specs.rate = 0;
m_empty = true;
}
bool HRTF::addImpulseResponse(std::shared_ptr<StreamBuffer> impulseResponse, float azimuth, float elevation)
{
Specs spec = impulseResponse->getSpecs();
azimuth = std::fmod(azimuth, 360);
if(azimuth < 0)
azimuth += 360;
if((spec.channels != CHANNELS_MONO) || (spec.rate != m_specs.rate && m_specs.rate > 0.0))
return false;
m_hrtfs[elevation][azimuth] = std::make_shared<ImpulseResponse>(impulseResponse, m_plan);
m_specs.channels = CHANNELS_MONO;
m_specs.rate = spec.rate;
m_empty = false;
return true;
}
std::pair<std::shared_ptr<ImpulseResponse>, std::shared_ptr<ImpulseResponse>> HRTF::getImpulseResponse(float &azimuth, float &elevation)
{
if(m_hrtfs.empty())
return std::make_pair(nullptr, nullptr);
azimuth = std::fmod(azimuth, 360);
if(azimuth < 0)
azimuth += 360;
std::shared_ptr<ImpulseResponse> R, L;
float az = 0, el = 0, dif=0, minDif=360;
for(auto elem : m_hrtfs)
{
dif = std::fabs(elevation - elem.first);
if(dif < minDif)
{
minDif = dif;
el = elem.first;
}
}
elevation = el;
dif = 0;
minDif = 360;
for(auto elem : m_hrtfs[elevation])
{
dif = std::fabs(azimuth - elem.first);
if(dif < minDif)
{
minDif = dif;
az = elem.first;
R = elem.second;
}
}
azimuth = az;
float azL = 360 - azimuth;
if(azL == 360)
azL = 0;
auto iter = m_hrtfs[elevation].find(azL);
if(iter != m_hrtfs[elevation].end())
L = iter->second;
else
{
dif = 0;
minDif = 360;
for(auto elem : m_hrtfs[elevation])
{
dif = std::fabs(azL - elem.first);
if(dif < minDif)
{
minDif = dif;
L = elem.second;
}
}
}
return std::make_pair(L, R);
}
Specs HRTF::getSpecs()
{
return m_specs;
}
bool HRTF::isEmpty()
{
return m_empty;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/HRTFLoader.h"
#include "file/File.h"
#include "Exception.h"
#include <dirent.h>
#include <exception>
AUD_NAMESPACE_BEGIN
std::shared_ptr<HRTF> HRTFLoader::loadLeftHRTFs(std::shared_ptr<FFTPlan> plan, const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>(plan));
loadHRTFs(hrtfs, 'L', fileExtension, path);
return hrtfs;
}
std::shared_ptr<HRTF> HRTFLoader::loadRightHRTFs(std::shared_ptr<FFTPlan> plan, const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>(plan));
loadHRTFs(hrtfs, 'R', fileExtension, path);
return hrtfs;
}
std::shared_ptr<HRTF> HRTFLoader::loadLeftHRTFs(const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>());
loadHRTFs(hrtfs, 'L', fileExtension, path);
return hrtfs;
}
std::shared_ptr<HRTF> HRTFLoader::loadRightHRTFs(const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>());
loadHRTFs(hrtfs, 'R', fileExtension, path);
return hrtfs;
}
void HRTFLoader::loadHRTFs(std::shared_ptr<HRTF> hrtfs, char ear, const std::string& fileExtension, const std::string& path)
{
std::string readpath = path;
if(path == "")
readpath = ".";
DIR* dir = opendir(path.c_str());
if(!dir)
return;
float azim, elev;
while(dirent* entry = readdir(dir))
{
std::string filename = entry->d_name;
if(filename.front() == ear && filename.length() >= fileExtension.length() && filename.substr(filename.length() - fileExtension.length()) == fileExtension)
{
try
{
elev = std::stof(filename.substr(1, filename.find("e") - 1));
azim = std::stof(filename.substr(filename.find("e") + 1, filename.find("a") - filename.find("e") - 1));
if(ear == 'L')
azim = 360 - azim;
}
catch(...)
{
AUD_THROW(FileException, "The HRTF name doesn't follow the naming scheme: " + filename);
}
hrtfs->addImpulseResponse(std::make_shared<StreamBuffer>(std::make_shared<File>(readpath + "/" + filename)), azim, elev);
}
}
closedir(dir);
return;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/HRTFLoader.h"
#include "file/File.h"
#include "Exception.h"
#include <windows.h>
#include <exception>
AUD_NAMESPACE_BEGIN
std::shared_ptr<HRTF> HRTFLoader::loadLeftHRTFs(std::shared_ptr<FFTPlan> plan, const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>(plan));
loadHRTFs(hrtfs, 'L', fileExtension, path);
return hrtfs;
}
std::shared_ptr<HRTF> HRTFLoader::loadRightHRTFs(std::shared_ptr<FFTPlan> plan, const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>(plan));
loadHRTFs(hrtfs, 'R', fileExtension, path);
return hrtfs;
}
std::shared_ptr<HRTF> HRTFLoader::loadLeftHRTFs(const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>());
loadHRTFs(hrtfs, 'L', fileExtension, path);
return hrtfs;
}
std::shared_ptr<HRTF> HRTFLoader::loadRightHRTFs(const std::string& fileExtension, const std::string& path)
{
std::shared_ptr<HRTF> hrtfs(std::make_shared<HRTF>());
loadHRTFs(hrtfs, 'R', fileExtension, path);
return hrtfs;
}
void HRTFLoader::loadHRTFs(std::shared_ptr<HRTF> hrtfs, char ear, const std::string& fileExtension, const std::string& path)
{
std::string readpath = path;
if(path == "")
readpath = ".";
WIN32_FIND_DATA entry;
bool found_file = true;
std::string search = readpath + "\\*";
HANDLE dir = FindFirstFile(search.c_str(), &entry);
if(dir == INVALID_HANDLE_VALUE)
return;
float azim, elev;
while(found_file)
{
std::string filename = entry.cFileName;
if(filename.front() == ear && filename.length() >= fileExtension.length() && filename.substr(filename.length() - fileExtension.length()) == fileExtension)
{
try
{
elev = std::stof(filename.substr(1, filename.find("e") - 1));
azim = std::stof(filename.substr(filename.find("e") + 1, filename.find("a") - filename.find("e") - 1));
if(ear == 'L')
azim = 360 - azim;
}
catch(...)
{
AUD_THROW(FileException, "The HRTF name doesn't follow the naming scheme: " + filename);
}
hrtfs->addImpulseResponse(std::make_shared<StreamBuffer>(std::make_shared<File>(readpath + "/" + filename)), azim, elev);
}
found_file = FindNextFile(dir, &entry);
}
FindClose(dir);
return;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Highpass.h"
#include "fx/IIRFilterReader.h"
#include "fx/HighpassCalculator.h"
AUD_NAMESPACE_BEGIN
Highpass::Highpass(std::shared_ptr<ISound> sound, float frequency, float Q) :
DynamicIIRFilter(sound, std::shared_ptr<IDynamicIIRFilterCalculator>(new HighpassCalculator(frequency, Q)))
{
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/HighpassCalculator.h"
#include <cmath>
AUD_NAMESPACE_BEGIN
HighpassCalculator::HighpassCalculator(float frequency, float Q) :
m_frequency(frequency),
m_Q(Q)
{
}
void HighpassCalculator::recalculateCoefficients(SampleRate rate, std::vector<float> &b, std::vector<float> &a)
{
float w0 = 2.0 * M_PI * (SampleRate)m_frequency / rate;
float alpha = (float)(std::sin(w0) / (2.0 * (double)m_Q));
float norm = 1 + alpha;
float c = std::cos(w0);
a.push_back(1);
a.push_back(-2 * c / norm);
a.push_back((1 - alpha) / norm);
b.push_back((1 + c) / (2 * norm));
b.push_back((-1 - c) / norm);
b.push_back(b[0]);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/IIRFilter.h"
#include "fx/IIRFilterReader.h"
AUD_NAMESPACE_BEGIN
IIRFilter::IIRFilter(std::shared_ptr<ISound> sound, const std::vector<float>& b, const std::vector<float>& a) :
Effect(sound), m_a(a), m_b(b)
{
}
std::shared_ptr<IReader> IIRFilter::createReader()
{
return std::shared_ptr<IReader>(new IIRFilterReader(getReader(), m_b, m_a));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/IIRFilterReader.h"
AUD_NAMESPACE_BEGIN
IIRFilterReader::IIRFilterReader(std::shared_ptr<IReader> reader, const std::vector<float>& b, const std::vector<float>& a) :
BaseIIRFilterReader(reader, b.size(), a.size()), m_a(a), m_b(b)
{
if(m_a.empty() == false)
{
for(int i = 1; i < m_a.size(); i++)
m_a[i] /= m_a[0];
for(int i = 0; i < m_b.size(); i++)
m_b[i] /= m_a[0];
m_a[0] = 1;
}
}
sample_t IIRFilterReader::filter()
{
sample_t out = 0;
for(int i = 1; i < m_a.size(); i++)
out -= y(-i) * m_a[i];
for(int i = 0; i < m_b.size(); i++)
out += x(-i) * m_b[i];
return out;
}
void IIRFilterReader::setCoefficients(const std::vector<float>& b, const std::vector<float>& a)
{
setLengths(b.size(), a.size());
m_a = a;
m_b = b;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ImpulseResponse.h"
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
AUD_NAMESPACE_BEGIN
ImpulseResponse::ImpulseResponse(std::shared_ptr<StreamBuffer> impulseResponse) :
ImpulseResponse(impulseResponse, std::make_shared<FFTPlan>(0.0))
{
}
ImpulseResponse::ImpulseResponse(std::shared_ptr<StreamBuffer> impulseResponse, std::shared_ptr<FFTPlan> plan)
{
auto reader = impulseResponse->createReader();
m_length = reader->getLength();
processImpulseResponse(impulseResponse->createReader(), plan);
}
Specs ImpulseResponse::getSpecs()
{
return m_specs;
}
int ImpulseResponse::getLength()
{
return m_length;
}
std::shared_ptr<std::vector<std::shared_ptr<std::vector<std::complex<sample_t>>>>> ImpulseResponse::getChannel(int n)
{
return m_processedIR[n];
}
void ImpulseResponse::processImpulseResponse(std::shared_ptr<IReader> reader, std::shared_ptr<FFTPlan> plan)
{
m_specs.channels = reader->getSpecs().channels;
m_specs.rate = reader->getSpecs().rate;
int N = plan->getSize();
bool eos = false;
int length = reader->getLength();
sample_t* buffer = (sample_t*)std::malloc(length * m_specs.channels * sizeof(sample_t));
int numParts = std::ceil((float)length / (plan->getSize() / 2));
for(int i = 0; i < m_specs.channels; i++)
{
m_processedIR.push_back(std::make_shared<std::vector<std::shared_ptr<std::vector<std::complex<sample_t>>>>>());
for(int j = 0; j < numParts; j++)
(*m_processedIR[i]).push_back(std::make_shared<std::vector<std::complex<sample_t>>>((N / 2) + 1));
}
length += reader->getSpecs().rate;
reader->read(length, eos, buffer);
void* bufferFFT = plan->getBuffer();
for(int i = 0; i < m_specs.channels; i++)
{
int partStart = 0;
for(int h = 0; h < numParts; h++)
{
int k = 0;
int len = std::min(partStart + ((N / 2)*m_specs.channels), length*m_specs.channels);
std::memset(bufferFFT, 0, ((N / 2) + 1) * 2 * sizeof(fftwf_complex));
for(int j = partStart; j < len; j += m_specs.channels)
{
((float*)bufferFFT)[k] = buffer[j + i];
k++;
}
plan->FFT(bufferFFT);
for(int j = 0; j < (N / 2) + 1; j++)
{
(*(*m_processedIR[i])[h])[j] = reinterpret_cast<std::complex<sample_t>*>(bufferFFT)[j];
}
partStart += N / 2 * m_specs.channels;
}
}
plan->freeBuffer(bufferFFT);
std::free(buffer);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Limiter.h"
#include "fx/LimiterReader.h"
AUD_NAMESPACE_BEGIN
Limiter::Limiter(std::shared_ptr<ISound> sound,
double start, double end) :
Effect(sound),
m_start(start),
m_end(end)
{
}
double Limiter::getStart() const
{
return m_start;
}
double Limiter::getEnd() const
{
return m_end;
}
std::shared_ptr<IReader> Limiter::createReader()
{
return std::shared_ptr<IReader>(new LimiterReader(getReader(), m_start, m_end));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,139 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/LimiterReader.h"
#include "util/Buffer.h"
#include <algorithm>
AUD_NAMESPACE_BEGIN
LimiterReader::LimiterReader(std::shared_ptr<IReader> reader, double start, double end) :
EffectReader(reader),
m_start(start),
m_end(end)
{
if(m_start > 0)
{
Specs specs = m_reader->getSpecs();
Specs specs2;
if(m_reader->isSeekable())
m_reader->seek(m_start * specs.rate);
else
{
// skip first m_start samples by reading them
int length = AUD_DEFAULT_BUFFER_SIZE;
Buffer buffer(AUD_DEFAULT_BUFFER_SIZE * AUD_SAMPLE_SIZE(specs));
bool eos = false;
for(int len = m_start * specs.rate;
length > 0 && !eos;
len -= length)
{
if(len < AUD_DEFAULT_BUFFER_SIZE)
length = len;
m_reader->read(length, eos, buffer.getBuffer());
specs2 = m_reader->getSpecs();
if(specs2.rate != specs.rate)
{
len = len * specs2.rate / specs.rate;
specs.rate = specs2.rate;
}
if(specs2.channels != specs.channels)
{
specs = specs2;
buffer.assureSize(AUD_DEFAULT_BUFFER_SIZE * AUD_SAMPLE_SIZE(specs));
}
}
}
}
}
void LimiterReader::seek(int position)
{
m_reader->seek(position + m_start * m_reader->getSpecs().rate);
}
int LimiterReader::getLength() const
{
int len = m_reader->getLength();
SampleRate rate = m_reader->getSpecs().rate;
if(len < 0 || (len > m_end * rate && m_end >= 0))
len = m_end * rate;
return len - m_start * rate;
}
int LimiterReader::getPosition() const
{
int pos = m_reader->getPosition();
SampleRate rate = m_reader->getSpecs().rate;
return std::min(pos, int(m_end * rate)) - m_start * rate;
}
void LimiterReader::read(int& length, bool& eos, sample_t* buffer)
{
eos = false;
if(m_end >= 0)
{
int position = m_reader->getPosition();
SampleRate rate = m_reader->getSpecs().rate;
if(position + length > m_end * rate)
{
length = m_end * rate - position;
eos = true;
}
if(position < int(m_start * rate))
{
int len2 = length;
for(int len = int(m_start * rate) - position;
len2 == length && !eos;
len -= length)
{
if(len < length)
len2 = len;
m_reader->read(len2, eos, buffer);
position += len2;
}
if(position < m_start * rate)
{
length = 0;
return;
}
}
if(length < 0)
{
length = 0;
return;
}
}
if(eos)
{
m_reader->read(length, eos, buffer);
eos = true;
}
else
m_reader->read(length, eos, buffer);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Loop.h"
#include "fx/LoopReader.h"
AUD_NAMESPACE_BEGIN
Loop::Loop(std::shared_ptr<ISound> sound, int loop) :
Effect(sound),
m_loop(loop)
{
}
int Loop::getLoop() const
{
return m_loop;
}
std::shared_ptr<IReader> Loop::createReader()
{
return std::shared_ptr<IReader>(new LoopReader(getReader(), m_loop));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/LoopReader.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
LoopReader::LoopReader(std::shared_ptr<IReader> reader, int loop) :
EffectReader(reader), m_count(loop), m_left(loop)
{
}
void LoopReader::seek(int position)
{
int len = m_reader->getLength();
if(len < 0)
m_reader->seek(position);
else
{
if(m_count >= 0)
{
m_left = m_count - (position / len);
if(m_left < 0)
m_left = 0;
}
m_reader->seek(position % len);
}
}
int LoopReader::getLength() const
{
if(m_count < 0)
return -1;
return m_reader->getLength() * m_count;
}
int LoopReader::getPosition() const
{
return m_reader->getPosition() * (m_count < 0 ? 1 : m_count);
}
void LoopReader::read(int& length, bool& eos, sample_t* buffer)
{
const Specs specs = m_reader->getSpecs();
int len = length;
m_reader->read(length, eos, buffer);
if(length < len && eos && m_left)
{
int pos = length;
length = len;
while(pos < length && eos && m_left)
{
if(m_left > 0)
m_left--;
m_reader->seek(0);
len = length - pos;
m_reader->read(len, eos, buffer + pos * specs.channels);
// prevent endless loop
if(!len)
break;
pos += len;
}
length = pos;
}
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Lowpass.h"
#include "fx/LowpassCalculator.h"
AUD_NAMESPACE_BEGIN
Lowpass::Lowpass(std::shared_ptr<ISound> sound, float frequency, float Q) :
DynamicIIRFilter(sound, std::shared_ptr<IDynamicIIRFilterCalculator>(new LowpassCalculator(frequency, Q)))
{
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/LowpassCalculator.h"
#include <cmath>
AUD_NAMESPACE_BEGIN
LowpassCalculator::LowpassCalculator(float frequency, float Q) :
m_frequency(frequency),
m_Q(Q)
{
}
void LowpassCalculator::recalculateCoefficients(SampleRate rate, std::vector<float> &b, std::vector<float> &a)
{
float w0 = 2 * M_PI * m_frequency / rate;
float alpha = std::sin(w0) / (2 * m_Q);
float norm = 1 + alpha;
float c = std::cos(w0);
a.push_back(1);
a.push_back(-2 * c / norm);
a.push_back((1 - alpha) / norm);
b.push_back((1 - c) / (2 * norm));
b.push_back((1 - c) / norm);
b.push_back(b[0]);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Modulator.h"
#include "fx/ModulatorReader.h"
AUD_NAMESPACE_BEGIN
Modulator::Modulator(std::shared_ptr<ISound> sound1, std::shared_ptr<ISound> sound2) :
m_sound1(sound1), m_sound2(sound2)
{
}
std::shared_ptr<IReader> Modulator::createReader()
{
std::shared_ptr<IReader> reader1 = m_sound1->createReader();
std::shared_ptr<IReader> reader2 = m_sound2->createReader();
return std::shared_ptr<IReader>(new ModulatorReader(reader1, reader2));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ModulatorReader.h"
#include "Exception.h"
#include <algorithm>
#include <cstring>
AUD_NAMESPACE_BEGIN
ModulatorReader::ModulatorReader(std::shared_ptr<IReader> reader1, std::shared_ptr<IReader> reader2) :
m_reader1(reader1), m_reader2(reader2)
{
}
ModulatorReader::~ModulatorReader()
{
}
bool ModulatorReader::isSeekable() const
{
return m_reader1->isSeekable() && m_reader2->isSeekable();
}
void ModulatorReader::seek(int position)
{
m_reader1->seek(position);
m_reader2->seek(position);
}
int ModulatorReader::getLength() const
{
int len1 = m_reader1->getLength();
int len2 = m_reader2->getLength();
if((len1 < 0) || (len2 < 0))
return -1;
return std::max(len1, len2);
}
int ModulatorReader::getPosition() const
{
int pos1 = m_reader1->getPosition();
int pos2 = m_reader2->getPosition();
return std::max(pos1, pos2);
}
Specs ModulatorReader::getSpecs() const
{
return m_reader1->getSpecs();
}
void ModulatorReader::read(int& length, bool& eos, sample_t* buffer)
{
Specs specs = m_reader1->getSpecs();
Specs s2 = m_reader2->getSpecs();
if(!AUD_COMPARE_SPECS(specs, s2))
AUD_THROW(StateException, "Two readers with different specifiactions cannot be modulated.");
int samplesize = AUD_SAMPLE_SIZE(specs);
m_buffer.assureSize(length * samplesize);
int len1 = length;
m_reader1->read(len1, eos, buffer);
if(len1 < length)
std::memset(buffer + len1 * specs.channels, 0, (length - len1) * samplesize);
int len2 = length;
bool eos2;
sample_t* buf = m_buffer.getBuffer();
m_reader2->read(len2, eos2, buf);
for(int i = 0; i < len2 * specs.channels; i++)
buffer[i] *= buf[i];
length = std::max(len1, len2);
eos &= eos2;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/MutableReader.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
MutableReader::MutableReader(std::shared_ptr<ISound> sound) :
m_sound(sound)
{
m_reader = m_sound->createReader();
}
bool MutableReader::isSeekable() const
{
return m_reader->isSeekable();
}
void MutableReader::seek(int position)
{
if(position < m_reader->getPosition())
{
m_reader = m_sound->createReader();
}
else
m_reader->seek(position);
}
int MutableReader::getLength() const
{
return m_reader->getLength();
}
int MutableReader::getPosition() const
{
return m_reader->getPosition();
}
Specs MutableReader::getSpecs() const
{
return m_reader->getSpecs();
}
void MutableReader::read(int& length, bool& eos, sample_t* buffer)
{
m_reader->read(length, eos, buffer);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/MutableSound.h"
#include "fx/MutableReader.h"
#include "Exception.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
MutableSound::MutableSound(std::shared_ptr<ISound> sound) :
m_sound(sound)
{
}
std::shared_ptr<IReader> MutableSound::createReader()
{
return std::make_shared<MutableReader>(m_sound);
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Pitch.h"
#include "fx/PitchReader.h"
AUD_NAMESPACE_BEGIN
Pitch::Pitch(std::shared_ptr<ISound> sound, float pitch) :
Effect(sound),
m_pitch(pitch)
{
}
std::shared_ptr<IReader> Pitch::createReader()
{
return std::shared_ptr<IReader>(new PitchReader(getReader(), m_pitch));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/PitchReader.h"
AUD_NAMESPACE_BEGIN
PitchReader::PitchReader(std::shared_ptr<IReader> reader, float pitch) :
EffectReader(reader), m_pitch(pitch)
{
}
Specs PitchReader::getSpecs() const
{
Specs specs = m_reader->getSpecs();
specs.rate *= m_pitch;
return specs;
}
float PitchReader::getPitch() const
{
return m_pitch;
}
void PitchReader::setPitch(float pitch)
{
if(pitch > 0.0f)
m_pitch = pitch;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,144 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/PlaybackCategory.h"
#include "fx/VolumeSound.h"
AUD_NAMESPACE_BEGIN
struct HandleData {
unsigned int id;
PlaybackCategory* category;
};
PlaybackCategory::PlaybackCategory(std::shared_ptr<IDevice> device) :
m_currentID(0), m_device(device), m_status(STATUS_PLAYING), m_volumeStorage(std::make_shared<VolumeStorage>(1.0f))
{
}
PlaybackCategory::~PlaybackCategory()
{
stop();
}
std::shared_ptr<IHandle> PlaybackCategory::play(std::shared_ptr<ISound> sound)
{
std::shared_ptr<ISound> vs(std::make_shared<VolumeSound>(sound, m_volumeStorage));
m_device->lock();
auto handle = m_device->play(vs);
if(handle == nullptr)
return nullptr;
switch (m_status)
{
case STATUS_PAUSED:
handle->pause();
break;
default:
m_status = STATUS_PLAYING;
};
m_handles[m_currentID] = handle;
HandleData* data = new HandleData;
data->category = this;
data->id = m_currentID;
handle->setStopCallback(cleanHandleCallback, data);
m_device->unlock();
m_currentID++;
return handle;
}
void PlaybackCategory::resume()
{
m_device->lock();
for(auto i = m_handles.begin(); i != m_handles.end();)
{
if(i->second->getStatus() == STATUS_INVALID)
i = m_handles.erase(i);
else
{
i->second->resume();
i++;
}
}
m_device->unlock();
m_status = STATUS_PLAYING;
}
void PlaybackCategory::pause()
{
m_device->lock();
for(auto i = m_handles.begin(); i != m_handles.end();)
{
if(i->second->getStatus() == STATUS_INVALID)
i = m_handles.erase(i);
else
{
i->second->pause();
i++;
}
}
m_device->unlock();
m_status = STATUS_PAUSED;
}
float PlaybackCategory::getVolume()
{
return m_volumeStorage->getVolume();
}
void PlaybackCategory::setVolume(float volume)
{
m_volumeStorage->setVolume(volume);
}
void PlaybackCategory::stop()
{
m_device->lock();
for(auto i = m_handles.begin(); i != m_handles.end();)
{
i->second->stop();
if(i->second->getStatus() == STATUS_INVALID)
i = m_handles.erase(i);
else
i++;
}
m_device->unlock();
m_status = STATUS_STOPPED;
}
std::shared_ptr<VolumeStorage> PlaybackCategory::getSharedVolume()
{
return m_volumeStorage;
}
void PlaybackCategory::cleanHandles()
{
for(auto i = m_handles.begin(); i != m_handles.end();)
{
if(i->second->getStatus() == STATUS_INVALID)
i = m_handles.erase(i);
else
i++;
}
}
void PlaybackCategory::cleanHandleCallback(void* data)
{
auto dat = reinterpret_cast<HandleData*>(data);
dat->category->m_handles.erase(dat->id);
delete dat;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,186 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/PlaybackManager.h"
#include "fx/VolumeSound.h"
#include <stdexcept>
AUD_NAMESPACE_BEGIN
PlaybackManager::PlaybackManager(std::shared_ptr<IDevice> device) :
m_device(device), m_currentKey(0)
{
}
unsigned int PlaybackManager::addCategory(std::shared_ptr<PlaybackCategory> category)
{
bool flag = true;
unsigned int k = -1;
do {
auto iter = m_categories.find(m_currentKey);
if(iter == m_categories.end())
{
m_categories[m_currentKey] = category;
k = m_currentKey;
m_currentKey++;
flag = false;
}
else
m_currentKey++;
} while(flag);
return k;
}
unsigned int PlaybackManager::addCategory(float volume)
{
std::shared_ptr<PlaybackCategory> category = std::make_shared<PlaybackCategory>(m_device);
category->setVolume(volume);
bool flag = true;
unsigned int k = -1;
do {
auto iter = m_categories.find(m_currentKey);
if(iter == m_categories.end())
{
m_categories[m_currentKey] = category;
k = m_currentKey;
m_currentKey++;
flag = false;
}
else
m_currentKey++;
} while(flag);
return k;
}
std::shared_ptr<IHandle> PlaybackManager::play(std::shared_ptr<ISound> sound, unsigned int catKey)
{
auto iter = m_categories.find(catKey);
std::shared_ptr<PlaybackCategory> category;
if(iter != m_categories.end())
{
category = iter->second;
}
else
{
category = std::make_shared<PlaybackCategory>(m_device);
m_categories[catKey] = category;
}
return category->play(sound);
}
bool PlaybackManager::resume(unsigned int catKey)
{
auto iter = m_categories.find(catKey);
if(iter != m_categories.end())
{
iter->second->resume();
return true;
}
else
{
return false;
}
}
bool PlaybackManager::pause(unsigned int catKey)
{
auto iter = m_categories.find(catKey);
if(iter != m_categories.end())
{
iter->second->pause();
return true;
}
else
{
return false;
}
}
float PlaybackManager::getVolume(unsigned int catKey)
{
auto iter = m_categories.find(catKey);
if(iter != m_categories.end())
{
return iter->second->getVolume();
}
else
{
return -1.0;
}
}
bool PlaybackManager::setVolume(float volume, unsigned int catKey)
{
auto iter = m_categories.find(catKey);
if(iter != m_categories.end())
{
iter->second->setVolume(volume);
return true;
}
else
{
return false;
}
}
bool PlaybackManager::stop(unsigned int catKey)
{
auto iter = m_categories.find(catKey);
if(iter != m_categories.end())
{
iter->second->stop();
return true;
}
else
{
return false;
}
}
void PlaybackManager::clean()
{
for(auto cat : m_categories)
cat.second->cleanHandles();
}
bool PlaybackManager::clean(unsigned int catKey)
{
auto iter = m_categories.find(catKey);
if(iter != m_categories.end())
{
iter->second->cleanHandles();
return true;
}
else
{
return false;
}
}
std::shared_ptr<IDevice> PlaybackManager::getDevice()
{
return m_device;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Reverse.h"
#include "fx/ReverseReader.h"
AUD_NAMESPACE_BEGIN
Reverse::Reverse(std::shared_ptr<ISound> sound) :
Effect(sound)
{
}
std::shared_ptr<IReader> Reverse::createReader()
{
return std::shared_ptr<IReader>(new ReverseReader(getReader()));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,88 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/ReverseReader.h"
#include "Exception.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
ReverseReader::ReverseReader(std::shared_ptr<IReader> reader) :
EffectReader(reader),
m_length(reader->getLength()),
m_position(0)
{
if(m_length < 0 || !reader->isSeekable())
AUD_THROW(StateException, "A reader has to be seekable and have finite length to be reversible.");
}
void ReverseReader::seek(int position)
{
m_position = position;
}
int ReverseReader::getLength() const
{
return m_length;
}
int ReverseReader::getPosition() const
{
return m_position;
}
void ReverseReader::read(int& length, bool& eos, sample_t* buffer)
{
// first correct the length
if(m_position + length > m_length)
length = m_length - m_position;
if(length <= 0)
{
length = 0;
eos = true;
return;
}
const Specs specs = getSpecs();
const int samplesize = AUD_SAMPLE_SIZE(specs);
sample_t temp[CHANNEL_MAX];
int len = length;
// read from reader
m_reader->seek(m_length - m_position - len);
m_reader->read(len, eos, buffer);
// set null if reader didn't give enough data
if(len < length)
std::memset(buffer, 0, (length - len) * samplesize);
// copy the samples reverted
for(int i = 0; i < length / 2; i++)
{
std::memcpy(temp, buffer + (len - 1 - i) * specs.channels, samplesize);
std::memcpy(buffer + (len - 1 - i) * specs.channels, buffer + i * specs.channels, samplesize);
std::memcpy(buffer + i * specs.channels, temp, samplesize);
}
m_position += length;
eos = false;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,84 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/SoundList.h"
#include "Exception.h"
#include <cstring>
#include <cstdlib>
#include <chrono>
AUD_NAMESPACE_BEGIN
SoundList::SoundList(bool random) :
m_random(random)
{
std::srand(time(NULL));
}
SoundList::SoundList(std::vector<std::shared_ptr<ISound>>& list, bool random) :
m_list(list), m_random(random)
{
std::srand(time(NULL));
}
std::shared_ptr<IReader> SoundList::createReader()
{
if(m_list.size() > 0)
{
m_mutex.lock();
if(!m_random){
m_index++;
if(m_index >= m_list.size())
m_index = 0;
}
else
{
int temp;
do{
temp = std::rand() % m_list.size();
} while(temp == m_index && m_list.size()>1);
m_index = temp;
}
auto reader = m_list[m_index]->createReader();
m_mutex.unlock();
return reader;
}
else
AUD_THROW(FileException, "The sound list is empty");
}
void SoundList::addSound(std::shared_ptr<ISound> sound)
{
m_list.push_back(sound);
}
void SoundList::setRandomMode(bool random)
{
m_random = random;
}
bool SoundList::getRandomMode()
{
return m_random;
}
int SoundList::getSize()
{
return m_list.size();
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Source.h"
#include <cmath>
AUD_NAMESPACE_BEGIN
Source::Source(float azimuth, float elevation, float distance) :
m_elevation(elevation), m_distance(distance)
{
azimuth = std::fmod(azimuth, 360);
if(azimuth < 0)
azimuth += 360;
m_azimuth = azimuth;
}
float Source::getAzimuth()
{
return m_azimuth;
}
float Source::getElevation()
{
return m_elevation;
}
float Source::getDistance()
{
return m_distance;
}
float Source::getVolume()
{
float volume = 1.0f - m_distance;
if(volume < 0.0f)
volume = 0.0f;
return volume;
}
void Source::setAzimuth(float azimuth)
{
azimuth = std::fmod(azimuth, 360);
if(azimuth < 0)
azimuth += 360;
m_azimuth = azimuth;
}
void Source::setElevation(float elevation)
{
m_elevation = elevation;
}
void Source::setDistance(float distance)
{
m_distance = distance;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Sum.h"
#include "fx/IIRFilterReader.h"
AUD_NAMESPACE_BEGIN
Sum::Sum(std::shared_ptr<ISound> sound) :
Effect(sound)
{
}
std::shared_ptr<IReader> Sum::createReader()
{
std::vector<float> a, b;
a.push_back(1);
a.push_back(-1);
b.push_back(1);
return std::shared_ptr<IReader>(new IIRFilterReader(getReader(), b, a));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Threshold.h"
#include "fx/CallbackIIRFilterReader.h"
AUD_NAMESPACE_BEGIN
sample_t Threshold::thresholdFilter(CallbackIIRFilterReader* reader, float* threshold)
{
float in = reader->x(0);
if(in >= *threshold)
return 1;
else if(in <= -*threshold)
return -1;
else
return 0;
}
void Threshold::endThresholdFilter(float* threshold)
{
delete threshold;
}
Threshold::Threshold(std::shared_ptr<ISound> sound, float threshold) :
Effect(sound),
m_threshold(threshold)
{
}
float Threshold::getThreshold() const
{
return m_threshold;
}
std::shared_ptr<IReader> Threshold::createReader()
{
return std::shared_ptr<IReader>(new CallbackIIRFilterReader(getReader(), 1, 0, doFilterIIR(thresholdFilter), endFilterIIR(endThresholdFilter), new float(m_threshold)));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright 2009-2025 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/TimeStretchPitchScale.h"
#include "fx/TimeStretchPitchScaleReader.h"
AUD_NAMESPACE_BEGIN
TimeStretchPitchScale::TimeStretchPitchScale(std::shared_ptr<ISound> sound, double timeRatio, double pitchScale, StretcherQuality quality, bool preserveFormant) :
Effect(sound), m_timeRatio(timeRatio), m_pitchScale(pitchScale), m_quality(quality), m_preserveFormant(preserveFormant)
{
}
std::shared_ptr<IReader> TimeStretchPitchScale::createReader()
{
return std::shared_ptr<IReader>(new TimeStretchPitchScaleReader(getReader(), m_timeRatio, m_pitchScale, m_quality, m_preserveFormant));
}
double TimeStretchPitchScale::getTimeRatio() const
{
return m_timeRatio;
}
double TimeStretchPitchScale::getPitchScale() const
{
return m_pitchScale;
}
bool TimeStretchPitchScale::getPreserveFormant() const
{
return m_preserveFormant;
}
StretcherQuality TimeStretchPitchScale::getStretcherQuality() const
{
return m_quality;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,205 @@
/*******************************************************************************
* Copyright 2009-2025 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/TimeStretchPitchScaleReader.h"
#include <cstring>
#include "Exception.h"
#include "IReader.h"
#include "util/Buffer.h"
using namespace RubberBand;
AUD_NAMESPACE_BEGIN
void TimeStretchPitchScaleReader::reset()
{
auto startPad{m_stretcher->getPreferredStartPad()};
m_samplesToDrop = m_stretcher->getStartDelay();
m_deinterleaved[0].assureSize(startPad * sizeof(sample_t));
std::memset(m_deinterleaved[0].getBuffer(), 0, startPad * sizeof(sample_t));
for(auto& channel : m_channelData)
channel = m_deinterleaved[0].getBuffer();
m_stretcher->process(m_channelData.data(), startPad, m_finishedReader);
}
TimeStretchPitchScaleReader::TimeStretchPitchScaleReader(std::shared_ptr<IReader> reader, double timeRatio, double pitchScale, StretcherQuality quality, bool preserveFormant) :
EffectReader(reader), m_position(0), m_finishedReader(false), m_deinterleaved(reader->getSpecs().channels), m_channelData(reader->getSpecs().channels)
{
if (pitchScale < 1.0 / 256.0 || pitchScale > 256.0)
AUD_THROW(StateException, "The pitch scale must be between 1/256 and 256");
if (timeRatio < 1.0 / 256.0 || timeRatio > 256.0)
AUD_THROW(StateException, "The time-stretch ratio must be between 1/256 and 256");
RubberBandStretcher::Options options = RubberBandStretcher::OptionProcessRealTime | RubberBandStretcher::OptionEngineFiner | RubberBandStretcher::OptionChannelsTogether;
switch(quality)
{
case StretcherQuality::HIGH:
options |= RubberBandStretcher::OptionPitchHighQuality;
break;
case StretcherQuality::FAST:
options |= RubberBandStretcher::OptionPitchHighSpeed;
options |= RubberBandStretcher::OptionWindowShort;
break;
case StretcherQuality::CONSISTENT:
options |= RubberBandStretcher::OptionPitchHighConsistency;
break;
default:
break;
}
options |= preserveFormant ? RubberBandStretcher::OptionFormantPreserved : RubberBandStretcher::OptionFormantShifted;
m_stretcher = std::make_unique<RubberBandStretcher>(m_reader->getSpecs().rate, m_reader->getSpecs().channels, options, timeRatio, pitchScale);
reset();
}
void TimeStretchPitchScaleReader::read(int& length, bool& eos, sample_t* buffer)
{
if(length == 0)
return;
int samplesize = AUD_SAMPLE_SIZE(m_reader->getSpecs());
int channels = m_reader->getSpecs().channels;
int samplesRead = 0;
eos = false;
while(samplesRead < length)
{
int len = m_stretcher->getSamplesRequired();
if(!m_finishedReader && len != 0)
{
m_buffer.assureSize(len * samplesize);
sample_t* buf = m_buffer.getBuffer();
m_reader->read(len, m_finishedReader, buf);
// Deinterleave the input reader buffer for processing
for(int channel = 0; channel < channels; channel++)
{
m_deinterleaved[channel].assureSize(len * sizeof(sample_t));
sample_t* channelBuf = m_deinterleaved[channel].getBuffer();
for(int i = 0; i < len; i++)
{
channelBuf[i] = buf[i * channels + channel];
}
m_channelData[channel] = channelBuf;
}
m_stretcher->process(m_channelData.data(), len, m_finishedReader);
}
int available = m_stretcher->available();
if(available == -1)
{
eos = true;
break;
}
if(available == 0)
continue;
available = std::min(m_samplesToDrop ? m_samplesToDrop : length - samplesRead, available);
for(int channel = 0; channel < channels; channel++)
{
m_deinterleaved[channel].assureSize(available * sizeof(sample_t));
m_channelData[channel] = m_deinterleaved[channel].getBuffer();
}
m_stretcher->retrieve(m_channelData.data(), available);
if(m_samplesToDrop)
{
m_samplesToDrop -= available;
}
else
{
// Interleave the retrieved data into the buffer
for(int channel = 0; channel < channels; channel++)
{
sample_t* outputBuf = m_deinterleaved[channel].getBuffer();
for(int i = 0; i < available; i++)
{
buffer[(samplesRead + i) * channels + channel] = outputBuf[i];
}
}
samplesRead += available;
}
}
length = samplesRead;
m_position += length;
eos = m_stretcher->available() == -1;
}
double TimeStretchPitchScaleReader::getTimeRatio() const
{
return m_stretcher->getTimeRatio();
}
void TimeStretchPitchScaleReader::setTimeRatio(double timeRatio)
{
if(timeRatio >= 1.0 / 256.0 && timeRatio <= 256.0)
{
m_stretcher->setTimeRatio(timeRatio);
}
}
double TimeStretchPitchScaleReader::getPitchScale() const
{
return m_stretcher->getPitchScale();
}
void TimeStretchPitchScaleReader::setPitchScale(double pitchScale)
{
if(pitchScale >= 1.0 / 256.0 && pitchScale <= 256.0)
{
m_stretcher->setPitchScale(pitchScale);
}
}
void TimeStretchPitchScaleReader::seek(int position)
{
m_reader->seek(int(position / getTimeRatio()));
m_finishedReader = false;
m_stretcher->reset();
reset();
m_position = position;
}
int TimeStretchPitchScaleReader::getLength() const
{
return m_reader->getLength() * getTimeRatio();
}
int TimeStretchPitchScaleReader::getPosition() const
{
return m_position;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/Volume.h"
#include "fx/IIRFilterReader.h"
AUD_NAMESPACE_BEGIN
Volume::Volume(std::shared_ptr<ISound> sound, float volume) :
Effect(sound),
m_volume(volume)
{
}
float Volume::getVolume() const
{
return m_volume;
}
std::shared_ptr<IReader> Volume::createReader()
{
std::vector<float> a, b;
a.push_back(1);
b.push_back(m_volume);
return std::shared_ptr<IReader>(new IIRFilterReader(getReader(), b, a));
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/VolumeReader.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
VolumeReader::VolumeReader(std::shared_ptr<IReader> reader, std::shared_ptr<VolumeStorage> volumeStorage) :
m_reader(reader), m_volumeStorage(volumeStorage)
{
}
bool VolumeReader::isSeekable() const
{
return m_reader->isSeekable();
}
void VolumeReader::seek(int position)
{
m_reader->seek(position);
}
int VolumeReader::getLength() const
{
return m_reader->getLength();
}
int VolumeReader::getPosition() const
{
return m_reader->getPosition();
}
Specs VolumeReader::getSpecs() const
{
return m_reader->getSpecs();
}
void VolumeReader::read(int& length, bool& eos, sample_t* buffer)
{
m_reader->read(length, eos, buffer);
for(int i = 0; i < length * m_reader->getSpecs().channels; i++)
buffer[i] = buffer[i] * m_volumeStorage->getVolume();
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/VolumeSound.h"
#include "fx/VolumeReader.h"
#include "Exception.h"
#include <cstring>
AUD_NAMESPACE_BEGIN
VolumeSound::VolumeSound(std::shared_ptr<ISound> sound, std::shared_ptr<VolumeStorage> volumeStorage) :
m_sound(sound), m_volumeStorage(volumeStorage)
{
}
std::shared_ptr<IReader> VolumeSound::createReader()
{
return std::make_shared<VolumeReader>(m_sound->createReader(), m_volumeStorage);
}
std::shared_ptr<VolumeStorage> VolumeSound::getSharedVolume()
{
return m_volumeStorage;
}
void VolumeSound::setSharedVolume(std::shared_ptr<VolumeStorage> volumeStorage)
{
m_volumeStorage = volumeStorage;
}
AUD_NAMESPACE_END

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright 2015-2016 Juan Francisco Crespo Galán
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
#include "fx/VolumeStorage.h"
AUD_NAMESPACE_BEGIN
VolumeStorage::VolumeStorage() :
m_volume(1.0f)
{
}
VolumeStorage::VolumeStorage(float volume) :
m_volume(volume)
{
}
float VolumeStorage::getVolume()
{
return m_volume;
}
void VolumeStorage::setVolume(float volume)
{
m_volume = volume;
}
AUD_NAMESPACE_END