Skip to content

Commit

Permalink
Avoid unneccessary playMonoSamplesWithGain with empty spans
Browse files Browse the repository at this point in the history
  • Loading branch information
daschuer committed Nov 8, 2024
1 parent 4850f4e commit b1a5637
Showing 1 changed file with 29 additions and 47 deletions.
76 changes: 29 additions & 47 deletions src/effects/backends/builtin/metronomeeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,11 @@ std::size_t playMonoSamplesWithGain(std::span<const CSAMPLE> monoSource,
return framesPlayed;
}

template<class T>
std::span<T> subspan_clamped(std::span<T> in, typename std::span<T>::size_type offset) {
// TODO (Swiftb0y): should we instead create a wrapper type that implements
// UB-free "clamped" operations?
return in.subspan(std::min(offset, in.size()));
}

constexpr std::size_t framesPerBeat(
mixxx::audio::SampleRate framesPerSecond, double beatsPerMinute) {
double framesPerMinute = framesPerSecond * 60;
return static_cast<std::size_t>(framesPerMinute / beatsPerMinute);
}
// returns where in the output buffer to start playing the next click.
// If there the span is empty, no click should be played yet.
std::span<CSAMPLE> syncedClickOutput(double beatFractionBufferEnd,
std::optional<GroupFeatureBeatLength> beatLengthAndScratch,
std::span<CSAMPLE> output) {
if (!beatLengthAndScratch.has_value() || beatLengthAndScratch->scratch_rate == 0.0) {
return {};
}
double beatLength = beatLengthAndScratch->frames / beatLengthAndScratch->scratch_rate;

const bool needsPreviousBeat = beatLength < 0;
double beatToBufferEndFrames = std::abs(beatLength) *
(needsPreviousBeat ? (1 - beatFractionBufferEnd)
: beatFractionBufferEnd);
std::size_t beatToBufferEndSamples =
static_cast<std::size_t>(beatToBufferEndFrames) *
mixxx::kEngineChannelOutputCount;

if (beatToBufferEndSamples <= output.size()) {
return output.last(beatToBufferEndSamples);
}
return {};
}

} // namespace

Expand Down Expand Up @@ -160,25 +130,37 @@ void MetronomeEffect::processChannel(

const CSAMPLE_GAIN gain = db2ratio(static_cast<float>(m_pGainParameter->value()));

playMonoSamplesWithGain(subspan_clamped(click, gs->framesSinceLastClick), output, gain);
if (gs->framesSinceLastClick < click.size()) {
playMonoSamplesWithGain(click.subspan(gs->framesSinceLastClick), output, gain);
}
gs->framesSinceLastClick += engineParameters.framesPerBuffer();

std::span<CSAMPLE> outputBufferOffset = [&] {
if (shouldSync && hasBeatInfo) {
return syncedClickOutput(*groupFeatures.beat_fraction_buffer_end,
groupFeatures.beat_length,
output);
} else {
// EngineParameters::sampleRate in reality returns the framerate.
mixxx::audio::SampleRate framesPerSecond = engineParameters.sampleRate();
std::size_t offset = gs->framesSinceLastClick %
framesPerBeat(framesPerSecond,
m_pBpmParameter->value());
return subspan_clamped(output, offset * mixxx::kEngineChannelOutputCount);
if (shouldSync && hasBeatInfo) {
double beatFractionBufferEnd = *groupFeatures.beat_fraction_buffer_end;
GroupFeatureBeatLength beatLengthAndScratch = *groupFeatures.beat_length;
if (beatLengthAndScratch.scratch_rate == 0.0) {
return;
}
}();

if (!outputBufferOffset.empty()) {
gs->framesSinceLastClick = playMonoSamplesWithGain(click, outputBufferOffset, gain);
double beatLength = beatLengthAndScratch.frames / beatLengthAndScratch.scratch_rate;
const bool needsPreviousBeat = beatLength < 0;
double beatToBufferEndFrames = std::abs(beatLength) *
(needsPreviousBeat ? (1 - beatFractionBufferEnd)
: beatFractionBufferEnd);
std::size_t beatToBufferEndSamples =
static_cast<std::size_t>(beatToBufferEndFrames) *
mixxx::kEngineChannelOutputCount;

if (beatToBufferEndSamples <= output.size()) {
gs->framesSinceLastClick = playMonoSamplesWithGain(click, output.last(beatToBufferEndSamples), gain);
}
} else {
// EngineParameters::sampleRate in reality returns the framerate.
mixxx::audio::SampleRate framesPerSecond = engineParameters.sampleRate();
std::size_t offset = (gs->framesSinceLastClick %
framesPerBeat(framesPerSecond, m_pBpmParameter->value())) *
mixxx::kEngineChannelOutputCount;
if (offset < output.size()) {
gs->framesSinceLastClick = playMonoSamplesWithGain(click, output.subspan(offset), gain);
}
}
}

0 comments on commit b1a5637

Please sign in to comment.