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

Adding RadioLanewayCrossfade transition #13750

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
118 changes: 106 additions & 12 deletions src/analyzer/analyzersilence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,56 @@ namespace {
// verify that the track samples have not changed since the last analysis
constexpr CSAMPLE kSilenceThreshold = 0.001f; // -60 dB
// TODO: Change the above line to:
//constexpr CSAMPLE kSilenceThreshold = db2ratio(-60.0f);
// constexpr CSAMPLE kSilenceThreshold = db2ratio(-60.0f);

// This comment can be deleted for full release.
// Some other values in case. These are in dBV expressed as Volts RMS
// (which seems, sensibly, the way Mixxx works).
// N_10DB_FADE_THRESHOLD 0.3162f
// N_12DB_FADE_THRESHOLD 0.2511f
// N_15DB_FADE_THRESHOLD 0.1778f
// N_18DB_FADE_THRESHOLD 0.1259f
// N_20DB_FADE_THRESHOLD 0.1f
// N_24DB_FADE_THRESHOLD 0.0631f
// N_25DB_FADE_THRESHOLD 0.0562f
// N_27DB_FADE_THRESHOLD 0.0447f
// N_30DB_FADE_THRESHOLD 0.0316f
// N_40DB_FADE_THRESHOLD 0.01f

constexpr CSAMPLE kFadeInThreshold = 0.0447f; // -27 dBV
constexpr CSAMPLE kFadeOutThreshold = 0.2511f; // -12 dBV

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;
}

template<typename Iterator>
Iterator first_sound(Iterator begin, Iterator end) {
return std::find_if(begin, end, [](const auto elem) {
return fabs(elem) >= kSilenceThreshold;
template<typename Iterator, typename Threshold>
Iterator find_first_above_threshold(Iterator begin,
Iterator end,
Threshold threshold) {
return std::find_if(begin, end, [threshold](const auto elem) {
return fabs(elem) >= threshold;
});
}

} // 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,20 +76,47 @@ 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;
}

// static
SINT AnalyzerSilence::findFirstSoundInChunk(std::span<const CSAMPLE> samples) {
return std::distance(samples.begin(), first_sound(samples.begin(), samples.end()));
return std::distance(samples.begin(),
find_first_above_threshold(
samples.begin(), samples.end(), kSilenceThreshold));
}

// static
SINT AnalyzerSilence::findLastSoundInChunk(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_sound(samples.rbegin(), samples.rend()), samples.rend()) - 1;
SINT ret = std::distance(find_first_above_threshold(samples.rbegin(),
samples.rend(),
kSilenceThreshold),
samples.rend()) -
1;
return ret;
}

// Find the index of first sound sample where the sound is above kFadeInThreshold (-27db)
SINT AnalyzerSilence::findLastFadeInChunk(std::span<const CSAMPLE> samples) {
SINT ret = std::distance(samples.begin(),
find_first_above_threshold(
samples.begin(), samples.end(), kFadeInThreshold));
return ret;
}

// Find the index of last sound sample where the sound is above kFadeOutThreshold (-12db)
SINT AnalyzerSilence::findFirstFadeOutChunk(std::span<const CSAMPLE> samples) {
// Note we are searching backwards from the end here.
SINT ret = std::distance(find_first_above_threshold(samples.rbegin(),
samples.rend(),
kFadeOutThreshold),
samples.rend()) -
1;
return ret;
}

Expand All @@ -93,10 +143,24 @@ 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 >= 0) {
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;
m_signalEnd = m_framesProcessed + (lastSoundSample / m_channelCount) + 1;
}
}

Expand Down Expand Up @@ -136,6 +200,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 in threshold (e.g. -27 dB).
static SINT findLastFadeInChunk(std::span<const CSAMPLE> samples);

/// returns the index of the last sample in the buffer that is
/// above the fade out threshold (e.g. -12 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