Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion sources/Application/Audio/DummyAudioOut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ void DummyAudioOut::Stop() {
SAFE_DELETE(thread_) ;
} ;

void DummyAudioOut::SetSoftclip(int clip, int gain) {}
void DummyAudioOut::SetMasterVolume(int volume) {}

void DummyAudioOut::SendPulse()
Expand Down
5 changes: 1 addition & 4 deletions sources/Application/Audio/DummyAudioOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ class DummyAudioOut: public AudioOut {

virtual void Trigger();

virtual bool Clipped() { return false; };

virtual int GetPlayedBufferPercentage() { return 0; };

void SendPulse();
Expand All @@ -39,8 +37,7 @@ class DummyAudioOut: public AudioOut {
virtual int GetAudioBufferSize() ;
virtual int GetAudioRequestedBufferSize() ;
virtual int GetAudioPreBufferCount() ;
virtual double GetStreamTime() ;
virtual void SetSoftclip(int, int);
virtual double GetStreamTime();
virtual void SetMasterVolume(int);

private:
Expand Down
98 changes: 92 additions & 6 deletions sources/Services/Audio/AudioMixer.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "AudioMixer.h"
#include "System/System/System.h"
#include <math.h>

#define MAX_POSITIVE_FIXED i2fp(32767)
#define MAX_NEGATIVE_FIXED i2fp(-32768)

AudioMixer::AudioMixer(const char *name):
T_SimpleList<AudioModule>(false),
Expand All @@ -8,6 +12,32 @@ AudioMixer::AudioMixer(const char *name):
name_(name)
{
volume_=(i2fp(1)) ;
softclip_ = -1;
softclipGain_ = 0 ;
masterVolume_ = 100 ;
clipped_ = false ;

// Precalculate constant values for softclipping algorithm
softClipData_[0].alpha = 1.45f; // -1.5db (approx.)
softClipData_[1].alpha = 1.07f; // -3db (approx.)
softClipData_[2].alpha = 0.75f; // -6db (approx.)
softClipData_[3].alpha = 0.53f; // -9db (approx.)

for (int i = 0; i < 4; i++) {
softClipData_[i].alpha23 = softClipData_[i].alpha * (2.0f / 3.0f);
softClipData_[i].alphaInv = 1.0f / softClipData_[i].alpha;

if (softClipData_[i].alpha > 1.0f) {
/* calculates gain compensation differently for
* modes with alpha > 1, so there's no drop in loudness
* and we can still drive the hard clipper when the input
* goes over 1.0
*/
softClipData_[i].gainCmp = 1.0f / (1.0f - (pow(softClipData_[i].alphaInv, 2.0f) / 3.0f));
} else {
softClipData_[i].gainCmp = 1.0f / softClipData_[i].alpha23;
}
}
} ;

AudioMixer::~AudioMixer() {
Expand Down Expand Up @@ -35,13 +65,14 @@ void AudioMixer::EnableRendering(bool enable) {
} ;

bool AudioMixer::Render(fixed *buffer,int samplecount) {
clipped_ = false;

fixed *mixBuffer=0 ;
bool gotData=false ;
IteratorPtr<AudioModule>it(GetIterator()) ;
for (it->Begin();!it->IsDone();it->Next()) {
AudioModule &current=it->CurrentItem() ;
if (!gotData) {
fixed *mixBuffer = 0;
bool gotData = false;
IteratorPtr<AudioModule> it(GetIterator());
for (it->Begin(); !it->IsDone(); it->Next()) {
AudioModule &current = it->CurrentItem();
if (!gotData) {
gotData=current.Render(buffer,samplecount) ;
} else {
if (!mixBuffer) {
Expand All @@ -64,12 +95,22 @@ bool AudioMixer::Render(fixed *buffer,int samplecount) {

if (gotData) {
fixed *c = buffer;
float damp = pow((float)masterVolume_ / 100, 4.0f);

if (volume_ != i2fp(1)) {
for (int i = 0; i < samplecount * 2; i++) {
fixed v = fp_mul(*c, volume_);
*c++ = v;
}
}

// Apply soft/hard clipping before recording
c = buffer;
for (int i = 0; i < samplecount * 2; i++) {
fixed sample = *c;
sample = fl2fp(damp * fp2fl(hardClip(softClip(sample))));
*c++ = sample;
}
}
if (enableRendering_&&writer_) {
if (!gotData) {
Expand All @@ -82,3 +123,48 @@ bool AudioMixer::Render(fixed *buffer,int samplecount) {
} ;

void AudioMixer::SetVolume(fixed volume) { volume_ = volume; }

void AudioMixer::SetSoftclip(int clip, int gain) {
softclip_ = clip - 1;
softclipGain_ = gain;
}

void AudioMixer::SetMasterVolume(int volume) {
masterVolume_ = volume;
}

bool AudioMixer::Clipped() { return clipped_; }

fixed AudioMixer::hardClip(fixed sample) {
if (sample > MAX_POSITIVE_FIXED || sample < MAX_NEGATIVE_FIXED) {
clipped_ = true;
return sample > 0 ? MAX_POSITIVE_FIXED : MAX_NEGATIVE_FIXED;
}
return sample;
}

/* Implements standard cubic algorithm
* https://wiki.analog.com/resources/tools-software/sigmastudio/toolbox/nonlinearprocessors/standardcubic
*/
fixed AudioMixer::softClip(fixed sample) {
if (softclip_ == -1 || sample == 0)
return sample;

float x;
float sampleFloat = fp2fl(sample);
float maxFloat = fp2fl(sampleFloat > 0 ? MAX_POSITIVE_FIXED : MAX_NEGATIVE_FIXED);
SoftClipData* data = &softClipData_[softclip_];

x = data->alphaInv * (sampleFloat / maxFloat);
if (x > -1.0f && x < 1.0f) {
sampleFloat = maxFloat * (data->alpha * (x - (pow(x, 3.0f) / 3.0f)));
} else {
sampleFloat = maxFloat * data->alpha23;
}

if (softclipGain_) {
sampleFloat = sampleFloat * data->gainCmp;
}

return fl2fp(sampleFloat);
}
28 changes: 23 additions & 5 deletions sources/Services/Audio/AudioMixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
#include "Application/Instruments/WavFileWriter.h"
#include <string>

struct SoftClipData {
float alpha;
float alpha23;
float alphaInv;
float gainCmp;
};

class AudioMixer: public AudioModule,public T_SimpleList<AudioModule> {
public:
AudioMixer(const char *name) ;
Expand All @@ -14,11 +21,22 @@ class AudioMixer: public AudioModule,public T_SimpleList<AudioModule> {
void SetFileRenderer(const char *path) ;
void EnableRendering(bool enable) ;
void SetVolume(fixed volume) ;
virtual void SetSoftclip(int clip, int gain);
virtual void SetMasterVolume(int volume) ;
virtual bool Clipped() ;

private:
bool enableRendering_ ;
std::string renderPath_ ;
WavFileWriter *writer_ ;
fixed volume_ ;
std::string name_ ;
fixed hardClip(fixed sample);
fixed softClip(fixed sample);
bool enableRendering_;
std::string renderPath_;
WavFileWriter *writer_;
fixed volume_;
std::string name_;
SoftClipData softClipData_[4];
int softclip_;
int softclipGain_;
int masterVolume_;
bool clipped_;
} ;
#endif
3 changes: 0 additions & 3 deletions sources/Services/Audio/AudioOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,10 @@ class AudioOut: public AudioMixer,public Observable {
virtual bool Start()=0 ;
virtual void Stop()=0 ;

virtual void SetSoftclip(int clip, int gain) = 0;
virtual void SetMasterVolume(int volume) = 0;

virtual void Trigger()=0 ;

virtual bool Clipped()=0 ;

virtual int GetPlayedBufferPercentage()=0 ;

virtual std::string GetAudioAPI()=0 ;
Expand Down
85 changes: 7 additions & 78 deletions sources/Services/Audio/AudioOutDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,9 @@
#include <math.h>

AudioOutDriver::AudioOutDriver(AudioDriver &driver) {
// Precalculate constant values for softclipping algorithm
softClipData_[0].alpha = 1.45f; // -1.5db (approx.)
softClipData_[1].alpha = 1.07f; // -3db (approx.)
softClipData_[2].alpha = 0.75f; // -6db (approx.)
softClipData_[3].alpha = 0.53f; // -9db (approx.)

for (int i = 0; i < 4; i++) {
softClipData_[i].alpha23 = softClipData_[i].alpha * (2.0f / 3.0f);
softClipData_[i].alphaInv = 1.0f / softClipData_[i].alpha;

if (softClipData_[i].alpha > 1.0f) {
/* calculates gain compensation differently for
* modes with alpha > 1, so there's no drop in loudness
* and we can still drive the hard clipper when the input
* goes over 1.0
*/
softClipData_[i].gainCmp = 1.0f / (1.0f - (pow(softClipData_[i].alphaInv, 2.0f) / 3.0f));
} else {
softClipData_[i].gainCmp = 1.0f / softClipData_[i].alpha23;
}
}

driver_=&driver ;
driver.AddObserver(*this) ;
primarySoundBuffer_=0 ;
driver_ = &driver;
driver.AddObserver(*this) ;
primarySoundBuffer_=0 ;
mixBuffer_ = 0;
SetOwnership(false);
}
Expand All @@ -56,7 +34,6 @@ void AudioOutDriver::Close() {
}

bool AudioOutDriver::Start() {
clipped_ = false;
sampleCount_=0 ;
return driver_->Start() ;
}
Expand All @@ -65,8 +42,6 @@ void AudioOutDriver::Stop() {
driver_->Stop() ;
}

bool AudioOutDriver::Clipped() { return clipped_; };

void AudioOutDriver::Trigger() {

TimeService *ts=TimeService::GetInstance() ;
Expand All @@ -84,58 +59,15 @@ void AudioOutDriver::Update(Observable &o,I_ObservableData *d)
}

void AudioOutDriver::prepareMixBuffers() {
sampleCount_=getPlaySampleCount() ;
clipped_=false ;
sampleCount_ = getPlaySampleCount();
} ;

void AudioOutDriver::SetSoftclip(int clip, int gain) {
softclip_ = clip - 1;
softclipGain_ = gain;
}

void AudioOutDriver::SetMasterVolume(int volume) {
masterVolume_ = volume;
}

fixed AudioOutDriver::hardClip(fixed sample) {

if (sample > MAX_POSITIVE_FIXED || sample < MAX_NEGATIVE_FIXED) {
clipped_ = true;
return sample > 0 ? MAX_POSITIVE_FIXED : MAX_NEGATIVE_FIXED;
}

return sample;
}

/* Implements standard cubic algorithm
* https://wiki.analog.com/resources/tools-software/sigmastudio/toolbox/nonlinearprocessors/standardcubic
*/
fixed AudioOutDriver::softClip(fixed sample) {
if (softclip_ == -1 || sample == 0)
return sample;

float x;
float sampleFloat = fp2fl(sample);
float maxFloat = fp2fl(sampleFloat > 0 ? MAX_POSITIVE_FIXED : MAX_NEGATIVE_FIXED);
SoftClipData* data = &softClipData_[softclip_];

x = data->alphaInv * (sampleFloat / maxFloat);
if (x > -1.0f && x < 1.0f) {
sampleFloat = maxFloat * (data->alpha * (x - (pow(x, 3.0f) / 3.0f)));
} else {
sampleFloat = maxFloat * data->alpha23;
}

if (softclipGain_) {
sampleFloat = sampleFloat * data->gainCmp;
}

return fl2fp(sampleFloat);
AudioMixer::SetMasterVolume(volume);
}

void AudioOutDriver::clipToMix() {

float damp = pow((float)masterVolume_ / 100, 4.0f);
bool interlaced = driver_->Interlaced();

if (!hasSound_) {
Expand All @@ -147,13 +79,10 @@ void AudioOutDriver::clipToMix() {

fixed *p = primarySoundBuffer_;

fixed leftSample;
fixed rightSample;

for (int i = 0; i < sampleCount_; i++) {

leftSample = damp * hardClip(softClip(*p++));
rightSample = damp * hardClip(softClip(*p++));
fixed leftSample = *p++;
fixed rightSample = *p++;

*s1 = short(fp2i(leftSample));
s1 += offset;
Expand Down
Loading