Skip to content

Commit

Permalink
Adding RadioLanewayCrossfade transition
Browse files Browse the repository at this point in the history
Adding RadioLanewayCrossfade transition.
See #13716
  • Loading branch information
davidlmorris committed Oct 10, 2024
1 parent b35739a commit 2eb0ef6
Show file tree
Hide file tree
Showing 7 changed files with 416 additions and 9 deletions.
112 changes: 107 additions & 5 deletions src/analyzer/analyzersilence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,34 @@ constexpr CSAMPLE kSilenceThreshold = 0.001f; // -60 dB
// TODO: Change the above line to:
//constexpr CSAMPLE kSilenceThreshold = db2ratio(-60.0f);

// These are in dBV expressed as Volts RMS (which seems, sensibly,
// the way Mixxx works).
// Don't change these to const as they are used only to feed the
// fade thresholds which are consts below themselves while we work
// out which one is best, and you'll just be adding to exe size.
#define N_10DB_FADEOUT_THRESHOLD 0.3162f
#define N_12DB_FADEOUT_THRESHOLD 0.2511f
#define N_15DB_FADEOUT_THRESHOLD 0.1778f
#define N_18DB_FADEOUT_THRESHOLD 0.1259f
#define N_20DB_FADEOUT_THRESHOLD 0.1f
#define N_24DB_FADEOUT_THRESHOLD 0.0631f
#define N_25DB_FADEOUT_THRESHOLD 0.0562f
#define N_27DB_FADEOUT_THRESHOLD 0.0447f
#define N_30DB_FADEOUT_THRESHOLD 0.0316f
#define N_40DB_FADEOUT_THRESHOLD 0.01f

constexpr CSAMPLE kFadeInThreshold = N_27DB_FADEOUT_THRESHOLD;
constexpr CSAMPLE kFadeOutThreshold = N_12DB_FADEOUT_THRESHOLD;


bool shouldAnalyze(TrackPointer pTrack) {
CuePointer pIntroCue = pTrack->findCueByType(mixxx::CueType::Intro);
CuePointer pOutroCue = pTrack->findCueByType(mixxx::CueType::Outro);
CuePointer pN60dBSound = pTrack->findCueByType(mixxx::CueType::N60dBSound);
CuePointer pFadeIn = pTrack->findCueByType(mixxx::CueType::FadeIn);
CuePointer pFadeOut = pTrack->findCueByType(mixxx::CueType::FadeOut);

if (!pIntroCue || !pOutroCue || !pN60dBSound || pN60dBSound->getLengthFrames() <= 0) {
if (!pFadeIn || !pFadeOut || !pIntroCue || !pOutroCue || !pN60dBSound || pN60dBSound->getLengthFrames() <= 0) {
return true;
}
return false;
Expand All @@ -30,13 +52,29 @@ Iterator first_sound(Iterator begin, Iterator end) {
});
}

template<typename ForwardIterator>
ForwardIterator last_fade_in_sound(ForwardIterator begin, ForwardIterator end) {
return std::find_if(begin, end, [](const auto elem) {
return fabs(elem) >= kFadeInThreshold;
});
}

template<typename Iterator>
Iterator first_fade_out_sound(Iterator begin, Iterator end) {
return std::find_if(begin, end, [](const auto elem) {
return fabs(elem) >= kFadeOutThreshold;
});
}

} // anonymous namespace

AnalyzerSilence::AnalyzerSilence(UserSettingsPointer pConfig)
: m_pConfig(pConfig),
m_framesProcessed(0),
m_signalStart(-1),
m_signalEnd(-1) {
m_signalEnd(-1),
m_fadeThresholdFadeInEnd(-1),
m_fadeThresholdFadeOutStart(-1) {
}

bool AnalyzerSilence::initialize(const AnalyzerTrack& track,
Expand All @@ -53,6 +91,8 @@ bool AnalyzerSilence::initialize(const AnalyzerTrack& track,
m_framesProcessed = 0;
m_signalStart = -1;
m_signalEnd = -1;
m_fadeThresholdFadeInEnd = -1;
m_fadeThresholdFadeOutStart = -1;
m_channelCount = channelCount;

return true;
Expand All @@ -70,6 +110,25 @@ SINT AnalyzerSilence::findLastSoundInChunk(std::span<const CSAMPLE> samples) {
return ret;
}

// static
SINT AnalyzerSilence::findFirstFadeOutChunk(std::span<const CSAMPLE> samples) {
// -1 is required, because the distance from the fist sample index (0) to crend() is 1,
SINT ret = std::distance(first_fade_out_sound(samples.rbegin(), samples.rend()), samples.rend()) - 1;
if (ret == -1) {
ret = samples.size();
}
return ret;
}

// static
SINT AnalyzerSilence::findLastFadeInChunk(std::span<const CSAMPLE> samples) {
SINT ret = std::distance(samples.begin(), last_fade_in_sound(samples.begin(), samples.end()));
// if (ret == samples.size()) {
// ret = 0;
// }
return ret;
}

// static
bool AnalyzerSilence::verifyFirstSound(
std::span<const CSAMPLE> samples,
Expand All @@ -93,10 +152,23 @@ bool AnalyzerSilence::processSamples(const CSAMPLE* pIn, SINT count) {
m_signalStart = m_framesProcessed + firstSoundSample / m_channelCount;
}
}
if (m_signalStart >= 0) {

if (m_fadeThresholdFadeInEnd < 0) {
const SINT lastSampleOfFadeIn = findLastFadeInChunk(samples);
if (lastSampleOfFadeIn < count) {
m_fadeThresholdFadeInEnd = m_framesProcessed + (lastSampleOfFadeIn / m_channelCount);
}
}
if (m_fadeThresholdFadeInEnd >= 0) {
const SINT lasttSampleBeforeFadeOut = findFirstFadeOutChunk(samples);
if (lasttSampleBeforeFadeOut < (count - 1)) {
m_fadeThresholdFadeOutStart = m_framesProcessed + (lasttSampleBeforeFadeOut / m_channelCount) + 1;
}
}
if (m_fadeThresholdFadeOutStart >= 0) {
const SINT lastSoundSample = findLastSoundInChunk(samples);
if (lastSoundSample >= 0) {
m_signalEnd = m_framesProcessed + lastSoundSample / m_channelCount + 1;
if (lastSoundSample < (count - 1)) { // not only sound or silence
m_signalEnd = m_framesProcessed + (lastSoundSample / m_channelCount) + 1;
}
}

Expand Down Expand Up @@ -136,6 +208,36 @@ void AnalyzerSilence::storeResults(TrackPointer pTrack) {

setupMainAndIntroCue(pTrack.get(), firstSoundPosition, m_pConfig.data());
setupOutroCue(pTrack.get(), lastSoundPosition);

if (m_fadeThresholdFadeInEnd < 0) {
m_fadeThresholdFadeInEnd = 0;
}
if (m_fadeThresholdFadeOutStart < 0) {
m_fadeThresholdFadeOutStart = m_framesProcessed;
}
const auto fadeInEndPosition = mixxx::audio::FramePos(m_fadeThresholdFadeInEnd);
CuePointer pFadeIn = pTrack->findCueByType(mixxx::CueType::FadeIn);
if (pFadeIn == nullptr) {
pFadeIn = pTrack->createAndAddCue(
mixxx::CueType::FadeIn,
Cue::kNoHotCue,
firstSoundPosition,
fadeInEndPosition);
} else {
pFadeIn->setStartAndEndPosition(firstSoundPosition, fadeInEndPosition);
}

const auto fadeOutStartPosition = mixxx::audio::FramePos(m_fadeThresholdFadeOutStart);
CuePointer pFadeOut = pTrack->findCueByType(mixxx::CueType::FadeOut);
if (pFadeOut == nullptr) {
pFadeOut = pTrack->createAndAddCue(
mixxx::CueType::FadeOut,
Cue::kNoHotCue,
fadeOutStartPosition,
lastSoundPosition);
} else {
pFadeOut->setStartAndEndPosition(fadeOutStartPosition, lastSoundPosition);
}
}

// static
Expand Down
11 changes: 11 additions & 0 deletions src/analyzer/analyzersilence.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ class AnalyzerSilence : public Analyzer {
UserSettings* pConfig);
static void setupOutroCue(Track* pTrack, mixxx::audio::FramePos lastSoundPosition);

/// returns the index of the first sample in the buffer that is above the fade out threshold (e.g. -20 dB)
/// or samples.size() if no sample is found
/// TODO CHeck if 'samples.size()' is actually meaningful in this context.
static SINT findLastFadeInChunk(std::span<const CSAMPLE> samples);

/// returns the index of the last sample in the buffer that is above that is above the fade out threshold (e.g. -20 dB)
/// or samples.size() if no sample is found
static SINT findFirstFadeOutChunk(std::span<const CSAMPLE> samples);

/// returns the index of the first sample in the buffer that is above -60 dB
/// or samples.size() if no sample is found
static SINT findFirstSoundInChunk(std::span<const CSAMPLE> samples);
Expand All @@ -56,4 +65,6 @@ class AnalyzerSilence : public Analyzer {
SINT m_framesProcessed;
SINT m_signalStart;
SINT m_signalEnd;
SINT m_fadeThresholdFadeInEnd;
SINT m_fadeThresholdFadeOutStart;
};
Loading

0 comments on commit 2eb0ef6

Please sign in to comment.