Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to mute mixer channels with Inf/NaN output #7323

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
acfd40a
Remove mixer sanitation
sakertooth Jun 16, 2024
6394b15
Expose nanhandler setting
sakertooth Jun 23, 2024
b8ce1af
Simplify sanitize function
sakertooth Jun 23, 2024
fe6eeec
Only sanitize the mixer channel
sakertooth Jun 24, 2024
2cdaa31
Generalize to muting invalid output, not just inf/nan
sakertooth Jun 24, 2024
dd1ac4b
Move mute invalid output boolean into MixerChannel
sakertooth Jun 24, 2024
a83dbb2
Set peaks to 0 when invalid output is found
sakertooth Jun 24, 2024
9496b01
Connect checkbox to stateChanged signal
sakertooth Jun 24, 2024
714cdf3
Remove invalid output error message and getter function
sakertooth Jun 24, 2024
ae9b51e
Emit Qt signal when invalid output is found
sakertooth Jun 24, 2024
bd73232
Color the mute checkbox red when invalid output is found
sakertooth Jun 24, 2024
0f96c66
Do not consider values outside the range [-1.0, 1.0] invalid
sakertooth Jun 24, 2024
186c21d
Simplify implementation
sakertooth Jun 27, 2024
5a6e20c
Change wording
sakertooth Jun 27, 2024
04f52a7
Remove unused include
sakertooth Jun 27, 2024
c99dae1
Merge remote-tracking branch 'upstream/master' into remove-mixer-sani…
sakertooth Jul 4, 2024
ea5722a
Change sampleFrame to SampleFrame
sakertooth Jul 4, 2024
d9c5710
Log invalid output message to standard error
sakertooth Jul 17, 2024
ad2fc14
Move std::cerr call into MixerChannelView::onInvalidOutput for realti…
sakertooth Jul 18, 2024
6bd25b3
Merge remote-tracking branch 'upstream/master' into remove-mixer-sani…
sakertooth Sep 20, 2024
4aa36d4
Fix invalid output state updates
sakertooth Sep 22, 2024
e268947
Queue invalid output state updates
sakertooth Sep 22, 2024
bb809e9
Remove logging again
sakertooth Sep 22, 2024
c01323e
Remove QObject inheritance from MixerChannel
sakertooth Sep 22, 2024
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
15 changes: 0 additions & 15 deletions include/MixHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ namespace MixHelpers

bool isSilent( const SampleFrame* src, int frames );

bool useNaNHandler();

void setNaNHandler( bool use );

bool sanitize( SampleFrame* src, int frames );

/*! \brief Add samples from src to dst */
void add( SampleFrame* dst, const SampleFrame* src, int frames );

Expand All @@ -62,15 +56,6 @@ void addMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coef
/*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst */
void addMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames );

/*! \brief Same as addMultiplied, but sanitize output (strip out infs/nans) */
void addSanitizedMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames );

/*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst - sanitized version */
void addSanitizedMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames );

/*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst - sanitized version */
void addSanitizedMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames );

/*! \brief Add samples from src multiplied by coeffSrcLeft/coeffSrcRight to dst */
void addMultipliedStereo( SampleFrame* dst, const SampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames );

Expand Down
18 changes: 15 additions & 3 deletions include/Mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ namespace lmms
{


class Mixer;
class MixerRoute;
using MixerRouteVector = std::vector<MixerRoute*>;

class MixerChannel : public ThreadableJob
{
public:
MixerChannel( int idx, Model * _parent );
MixerChannel(int idx, Mixer* mixer);
virtual ~MixerChannel();

EffectChain m_fxChain;
Expand Down Expand Up @@ -82,11 +83,17 @@ class MixerChannel : public ThreadableJob
std::atomic_size_t m_dependenciesMet;
void incrementDeps();
void processed();

void sanitizeOutput();

bool hasInvalidOutput() { return m_hasInvalidOutput; }
static void setMuteInvalidOutput(bool mute);

private:
void doProcessing() override;

std::optional<QColor> m_color;
Mixer* m_mixer;
bool m_hasInvalidOutput = false;
inline static bool s_muteInvalidOutput = false;
};

class MixerRoute : public QObject
Expand Down Expand Up @@ -209,6 +216,11 @@ class LMMS_EXPORT Mixer : public Model, public JournallingObject

MixerRouteVector m_mixerRoutes;

void broadcastChannelInvalidOutputState(int index, bool invalid) { emit channelInvalidOutputStateChanged(index, invalid); }

signals:
void channelInvalidOutputStateChanged(int index, bool invalid);

private:
// the mixer channels in the mixer. index 0 is always master.
std::vector<MixerChannel*> m_mixerChannels;
Expand Down
1 change: 1 addition & 0 deletions include/MixerChannelView.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ namespace lmms::gui
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
void updateInvalidOutputState(int index, bool invalid);

private:
bool confirmRemoval(int index);
Expand Down
2 changes: 1 addition & 1 deletion include/SetupDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private slots:
QComboBox * m_audioInterfaces;
AswMap m_audioIfaceSetupWidgets;
trMap m_audioIfaceNames;
bool m_NaNHandler;
bool m_muteInvalidOutput;
int m_bufferSize;
QSlider * m_bufferSizeSlider;
QLabel * m_bufferSizeLbl;
Expand Down
4 changes: 0 additions & 4 deletions src/core/EffectChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#include "EffectChain.h"
#include "Effect.h"
#include "DummyEffect.h"
#include "MixHelpers.h"

namespace lmms
{
Expand Down Expand Up @@ -191,15 +190,12 @@ bool EffectChain::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames, bo
return false;
}

MixHelpers::sanitize( _buf, _frames );

bool moreEffects = false;
for (const auto& effect : m_effects)
{
if (hasInputNoise || effect->isRunning())
{
moreEffects |= effect->processAudioBuffer(_buf, _frames);
MixHelpers::sanitize(_buf, _frames);
}
}

Expand Down
118 changes: 0 additions & 118 deletions src/core/MixHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,12 @@

#include "MixHelpers.h"

#ifdef LMMS_DEBUG
#include <cstdio>
#endif

#include <cmath>
#include <QtGlobal>

#include "ValueBuffer.h"
#include "SampleFrame.h"



static bool s_NaNHandler;


namespace lmms::MixHelpers
{

Expand Down Expand Up @@ -80,51 +71,6 @@ bool isSilent( const SampleFrame* src, int frames )
return true;
}

bool useNaNHandler()
{
return s_NaNHandler;
}

void setNaNHandler( bool use )
{
s_NaNHandler = use;
}

/*! \brief Function for sanitizing a buffer of infs/nans - returns true if those are found */
bool sanitize( SampleFrame* src, int frames )
{
if( !useNaNHandler() )
{
return false;
}

for (int f = 0; f < frames; ++f)
{
auto& currentFrame = src[f];

if (currentFrame.containsInf() || currentFrame.containsNaN())
{
#ifdef LMMS_DEBUG
// TODO don't use printf here
printf("Bad data, clearing buffer. frame: ");
printf("%d: value %f, %f\n", f, currentFrame.left(), currentFrame.right());
#endif

// Clear the whole buffer if a problem is found
zeroSampleFrames(src, frames);

return true;
}
else
{
currentFrame.clamp(sample_t(-1000.0), sample_t(1000.0));
}
};

return false;
}


struct AddOp
{
void operator()( SampleFrame& dst, const SampleFrame& src ) const
Expand Down Expand Up @@ -205,70 +151,6 @@ void addMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuff

}

void addSanitizedMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames )
{
if ( !useNaNHandler() )
{
addMultipliedByBuffer( dst, src, coeffSrc, coeffSrcBuf,
frames );
return;
}

for( int f = 0; f < frames; ++f )
{
dst[f][0] += ( std::isinf( src[f][0] ) || std::isnan( src[f][0] ) ) ? 0.0f : src[f][0] * coeffSrc * coeffSrcBuf->values()[f];
dst[f][1] += ( std::isinf( src[f][1] ) || std::isnan( src[f][1] ) ) ? 0.0f : src[f][1] * coeffSrc * coeffSrcBuf->values()[f];
}
}

void addSanitizedMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames )
{
if ( !useNaNHandler() )
{
addMultipliedByBuffers( dst, src, coeffSrcBuf1, coeffSrcBuf2,
frames );
return;
}

for( int f = 0; f < frames; ++f )
{
dst[f][0] += ( std::isinf( src[f][0] ) || std::isnan( src[f][0] ) )
? 0.0f
: src[f][0] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
dst[f][1] += ( std::isinf( src[f][1] ) || std::isnan( src[f][1] ) )
? 0.0f
: src[f][1] * coeffSrcBuf1->values()[f] * coeffSrcBuf2->values()[f];
}

}


struct AddSanitizedMultipliedOp
{
AddSanitizedMultipliedOp( float coeff ) : m_coeff( coeff ) { }

void operator()( SampleFrame& dst, const SampleFrame& src ) const
{
dst[0] += ( std::isinf( src[0] ) || std::isnan( src[0] ) ) ? 0.0f : src[0] * m_coeff;
dst[1] += ( std::isinf( src[1] ) || std::isnan( src[1] ) ) ? 0.0f : src[1] * m_coeff;
}

const float m_coeff;
};

void addSanitizedMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames )
{
if ( !useNaNHandler() )
{
addMultiplied( dst, src, coeffSrc, frames );
return;
}

run<>( dst, src, frames, AddSanitizedMultipliedOp(coeffSrc) );
}



struct AddMultipliedStereoOp
{
AddMultipliedStereoOp( float coeffLeft, float coeffRight )
Expand Down
Loading
Loading