-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4b0734d
commit 574ee4c
Showing
4 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
#pragma once | ||
#include "HephAudioShared.h" | ||
#include "AudioEffect.h" | ||
|
||
/** @file */ | ||
|
||
namespace HephAudio | ||
{ | ||
/** | ||
* @brief adds echo to the audio data. | ||
* | ||
*/ | ||
class Echo : public AudioEffect | ||
{ | ||
public: | ||
using AudioEffect::Process; | ||
|
||
protected: | ||
/** | ||
* number of times the audio data will be reflected. | ||
* | ||
*/ | ||
size_t reflectionCount; | ||
|
||
/** | ||
* elapsed time, in seconds, between the start of each reflection. | ||
* | ||
*/ | ||
double reflectionDelay; | ||
|
||
/** | ||
* the factor which echo data will be multiplied by between each reflection. | ||
* | ||
*/ | ||
double decayFactor; | ||
|
||
/** | ||
* start time, in seconds, of the audio data that will be used as echo. | ||
* | ||
*/ | ||
double echoStart; | ||
|
||
/** | ||
* duration of the audio data, in seconds, that will be used as echo. | ||
* | ||
*/ | ||
double echoDuration; | ||
|
||
/** | ||
* past samples required for real-time processing. | ||
* | ||
*/ | ||
AudioBuffer pastSamples; | ||
|
||
/** | ||
* for real-time processing. | ||
* | ||
*/ | ||
size_t currentIndex; | ||
|
||
public: | ||
/** @copydoc default_constructor */ | ||
Echo(); | ||
|
||
/** | ||
* @copydoc constructor | ||
* | ||
* @param reflectionCount @copydetails reflectionCount | ||
* @param reflectionDelay @copydetails reflectionDelay | ||
* @param decayFactor @copydetails decayFactor | ||
* @param echoStart @copydetails echoStart | ||
* @param echoDuration @copydetails echoDuration | ||
* | ||
*/ | ||
Echo(size_t reflectionCount, double reflectionDelay, double decayFactor, double echoStart, double echoDuration); | ||
|
||
/** @copydoc destructor */ | ||
virtual ~Echo() = default; | ||
|
||
virtual std::string Name() const override; | ||
virtual void Process(AudioBuffer& buffer, size_t startIndex, size_t frameCount) override; | ||
|
||
/** | ||
* gets the reflection count. | ||
* | ||
*/ | ||
virtual size_t GetReflectionCount() const; | ||
|
||
/** | ||
* sets the reflection count. | ||
* | ||
* @param reflectionCount @copydetails reflectionCount | ||
* | ||
*/ | ||
virtual void SetReflectionCount(size_t reflectionCount); | ||
|
||
/** | ||
* gets the reflection delay. | ||
* | ||
*/ | ||
virtual double GetReflectionDelay() const; | ||
|
||
/** | ||
* sets the reflection delay. | ||
* | ||
* @param reflectionDelay @copydetails reflectionDelay | ||
* | ||
*/ | ||
virtual void SetReflectionDelay(double reflectionDelay); | ||
|
||
/** | ||
* gets the decay factor. | ||
* | ||
*/ | ||
virtual double GetDecayFactor() const; | ||
|
||
/** | ||
* sets the decay factor. | ||
* | ||
* @param decayFactor @copydetails decayFactor | ||
* | ||
*/ | ||
virtual void SetDecayFactor(double decayFactor); | ||
|
||
/** | ||
* gets the echo start. | ||
* | ||
*/ | ||
virtual double GetEchoStart() const; | ||
|
||
/** | ||
* sets the echo start. | ||
* | ||
* @param echoStart @copydetails echoStart | ||
* | ||
*/ | ||
virtual void SetEchoStart(double echoStart); | ||
|
||
/** | ||
* gets the echo duration. | ||
* | ||
*/ | ||
virtual double GetEchoDuration() const; | ||
|
||
/** | ||
* sets the echo duration. | ||
* | ||
* @param echoDuration @copydetails echoDuration | ||
* | ||
*/ | ||
virtual void SetEchoDuration(double echoDuration); | ||
|
||
protected: | ||
void ProcessST(const AudioBuffer& inputBuffer, AudioBuffer& outputBuffer, size_t startIndex, size_t frameCount) override; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
#include "AudioEffects/Echo.h" | ||
#include "Exceptions/InvalidArgumentException.h" | ||
#include "HephMath.h" | ||
|
||
using namespace Heph; | ||
|
||
namespace HephAudio | ||
{ | ||
Echo::Echo() : Echo(0, 0, 0, 0, 0) {} | ||
|
||
Echo::Echo(size_t reflectionCount, double reflectionDelay, double decayFactor, double echoStart, double echoDuration) | ||
: AudioEffect(), currentIndex(0) | ||
{ | ||
this->SetReflectionCount(reflectionCount); | ||
this->SetReflectionDelay(reflectionDelay); | ||
this->SetDecayFactor(decayFactor); | ||
this->SetEchoStart(echoStart); | ||
this->SetEchoDuration(echoDuration); | ||
} | ||
|
||
std::string Echo::Name() const | ||
{ | ||
return "Echo"; | ||
} | ||
|
||
void Echo::Process(AudioBuffer& buffer, size_t startIndex, size_t frameCount) | ||
{ | ||
const AudioFormatInfo& formatInfo = buffer.FormatInfo(); | ||
const size_t pastSamplesSize = this->echoDuration * formatInfo.sampleRate; | ||
|
||
if (pastSamplesSize != this->pastSamples.FrameCount()) | ||
{ | ||
this->pastSamples = AudioBuffer(pastSamplesSize, formatInfo.channelLayout, formatInfo.sampleRate); | ||
} | ||
|
||
const size_t echoStartIndex = this->echoStart * formatInfo.sampleRate; | ||
const size_t echoEndIndex = echoStartIndex + pastSamplesSize; | ||
size_t i1 = this->currentIndex + startIndex; | ||
const size_t i2 = HEPH_MATH_MIN(i1 + frameCount, echoEndIndex); | ||
|
||
if (i1 < echoEndIndex && i2 > echoStartIndex) | ||
{ | ||
i1 = HEPH_MATH_MAX(i1, echoStartIndex); | ||
for (size_t i = i1; i < i2; ++i) | ||
{ | ||
for (size_t j = 0; j < formatInfo.channelLayout.count; ++j) | ||
{ | ||
this->pastSamples[i - echoStartIndex][j] = buffer[i - i1][j]; | ||
} | ||
} | ||
} | ||
|
||
AudioEffect::Process(buffer, startIndex, frameCount); | ||
this->currentIndex += frameCount; | ||
} | ||
|
||
size_t Echo::GetReflectionCount() const | ||
{ | ||
return this->reflectionCount; | ||
} | ||
|
||
void Echo::SetReflectionCount(size_t reflectionCount) | ||
{ | ||
this->reflectionCount = reflectionCount; | ||
} | ||
|
||
double Echo::GetReflectionDelay() const | ||
{ | ||
return this->reflectionDelay; | ||
} | ||
|
||
void Echo::SetReflectionDelay(double reflectionDelay) | ||
{ | ||
if (reflectionDelay < 0) | ||
{ | ||
HEPH_RAISE_AND_THROW_EXCEPTION(this, InvalidArgumentException(HEPH_FUNC, "reflectionDelay cannot be negative.")); | ||
} | ||
|
||
this->reflectionDelay = reflectionDelay; | ||
} | ||
|
||
double Echo::GetDecayFactor() const | ||
{ | ||
return this->decayFactor; | ||
} | ||
|
||
void Echo::SetDecayFactor(double decayFactor) | ||
{ | ||
this->decayFactor = decayFactor; | ||
} | ||
|
||
double Echo::GetEchoStart() const | ||
{ | ||
return this->echoStart; | ||
} | ||
|
||
void Echo::SetEchoStart(double echoStart) | ||
{ | ||
if (echoStart < 0) | ||
{ | ||
HEPH_RAISE_AND_THROW_EXCEPTION(this, InvalidArgumentException(HEPH_FUNC, "echoStart must not be negative.")); | ||
} | ||
|
||
this->echoStart = echoStart; | ||
} | ||
|
||
double Echo::GetEchoDuration() const | ||
{ | ||
return this->echoDuration; | ||
} | ||
|
||
void Echo::SetEchoDuration(double echoDuration) | ||
{ | ||
if (echoDuration < 0) | ||
{ | ||
HEPH_RAISE_AND_THROW_EXCEPTION(this, InvalidArgumentException(HEPH_FUNC, "echoDuration must not be negative.")); | ||
} | ||
|
||
this->echoDuration = echoDuration; | ||
} | ||
|
||
void Echo::ProcessST(const AudioBuffer& inputBuffer, AudioBuffer& outputBuffer, size_t startIndex, size_t frameCount) | ||
{ | ||
const size_t endIndex = startIndex + frameCount; | ||
const AudioFormatInfo& formatInfo = outputBuffer.FormatInfo(); | ||
|
||
for (size_t i = startIndex; i < endIndex; ++i) | ||
{ | ||
for (size_t j = 0; j < formatInfo.channelLayout.count; ++j) | ||
{ | ||
outputBuffer[i][j] /= this->reflectionCount; | ||
} | ||
} | ||
|
||
const size_t echoStartIndex = this->echoStart * formatInfo.sampleRate; | ||
const size_t echoDuration_sample = this->echoDuration * formatInfo.sampleRate; | ||
const size_t reflectionDelay_sample = this->reflectionDelay * formatInfo.sampleRate; | ||
double factor = this->decayFactor / this->reflectionCount; | ||
|
||
for (size_t i = 0; i < this->reflectionCount; ++i, factor *= this->decayFactor) | ||
{ | ||
const size_t reflectionStartIndex = echoStartIndex + (i + 1) * reflectionDelay_sample; | ||
const size_t reflectionEndIndex = reflectionStartIndex + echoDuration_sample; | ||
const size_t j1 = this->currentIndex + startIndex; | ||
const size_t j2 = HEPH_MATH_MIN(j1 + frameCount, reflectionEndIndex); | ||
|
||
if (j1 < reflectionEndIndex && j2 > reflectionStartIndex) | ||
{ | ||
for (size_t j = HEPH_MATH_MAX(j1, reflectionStartIndex); j < j2; ++j) | ||
{ | ||
for (size_t k = 0; k < formatInfo.channelLayout.count; ++k) | ||
{ | ||
outputBuffer[j - this->currentIndex][k] += this->pastSamples[j - reflectionStartIndex][k] * factor; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |