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 support for stem in the engine #13070

Merged
merged 17 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
5 changes: 3 additions & 2 deletions src/analyzer/analyzersilence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ SINT AnalyzerSilence::findLastSoundInChunk(std::span<const CSAMPLE> samples) {
// static
bool AnalyzerSilence::verifyFirstSound(
std::span<const CSAMPLE> samples,
mixxx::audio::FramePos firstSoundFrame) {
mixxx::audio::FramePos firstSoundFrame,
mixxx::audio::ChannelCount channelCount) {
const SINT firstSoundSample = findFirstSoundInChunk(samples);
if (firstSoundSample < static_cast<SINT>(samples.size())) {
return mixxx::audio::FramePos::fromEngineSamplePos(firstSoundSample)
return mixxx::audio::FramePos::fromSamplePos(firstSoundSample, channelCount)
.toLowerFrameBoundary() == firstSoundFrame.toLowerFrameBoundary();
}
return false;
Expand Down
3 changes: 2 additions & 1 deletion src/analyzer/analyzersilence.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class AnalyzerSilence : public Analyzer {
/// last analysis run and is an indicator for file edits or decoder
/// changes/issues
static bool verifyFirstSound(std::span<const CSAMPLE> samples,
mixxx::audio::FramePos firstSoundFrame);
mixxx::audio::FramePos firstSoundFrame,
mixxx::audio::ChannelCount channelCount);

private:
UserSettingsPointer m_pConfig;
Expand Down
2 changes: 1 addition & 1 deletion src/analyzer/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace mixxx {
// depending on the track length. A block size of 4096 frames per block
// seems to do fine. Signal processing during analysis uses the same,
// fixed number of channels like the engine does, usually 2 = stereo.
constexpr audio::ChannelCount kAnalysisChannels = mixxx::kEngineChannelCount;
constexpr audio::ChannelCount kAnalysisChannels = mixxx::kEngineChannelOutputCount;
constexpr SINT kAnalysisFramesPerChunk = 4096;
constexpr SINT kAnalysisSamplesPerChunk =
kAnalysisFramesPerChunk * kAnalysisChannels;
Expand Down
33 changes: 31 additions & 2 deletions src/audio/frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,25 @@ class FramePos final {
/// "invalid" positions (e.g. when parsing values from control objects),
/// use `FramePos::fromEngineSamplePosMaybeInvalid` instead.
static constexpr FramePos fromEngineSamplePos(double engineSamplePos) {
return FramePos(engineSamplePos / mixxx::kEngineChannelCount);
return FramePos(engineSamplePos / mixxx::kEngineChannelOutputCount);
}

static constexpr FramePos fromSamplePos(double samplePos,
mixxx::audio::ChannelCount channelCount) {
return FramePos(static_cast<double>(samplePos) / channelCount);
}

static constexpr FramePos fromSamplePos(double samplePos,
const mixxx::audio::SignalInfo& signalInfo) {
return FramePos(static_cast<double>(samplePos) / signalInfo.getChannelCount());
}

/// Return an engine sample position. The `FramePos` is expected to be
/// valid. If invalid positions are possible (e.g. for control object
/// values), use `FramePos::toEngineSamplePosMaybeInvalid` instead.
double toEngineSamplePos() const {
DEBUG_ASSERT(isValid());
double engineSamplePos = value() * mixxx::kEngineChannelCount;
double engineSamplePos = value() * mixxx::kEngineChannelOutputCount;
// In the rare but possible instance that the position is valid but
// the engine sample position is exactly -1.0, we nudge the position
// because otherwise fromEngineSamplePosMaybeInvalid() will think
Expand All @@ -55,6 +65,10 @@ class FramePos final {
return engineSamplePos;
}

double toSamplePos(mixxx::audio::ChannelCount channelCount) const {
DEBUG_ASSERT(isValid());
return value() * channelCount;
}
/// Return a `FramePos` from a given engine sample position. Sample
/// positions that equal `kLegacyInvalidEnginePosition` are considered
/// invalid and result in an invalid `FramePos` instead.
Expand All @@ -70,6 +84,14 @@ class FramePos final {
return fromEngineSamplePos(engineSamplePos);
}

static constexpr FramePos fromSamplePosMaybeInvalid(
double samplePos, mixxx::audio::ChannelCount channelCount) {
if (samplePos == kLegacyInvalidEnginePosition) {
return {};
}
return fromSamplePos(samplePos, channelCount);
}

/// Return an engine sample position. If the `FramePos` is invalid,
/// `kLegacyInvalidEnginePosition` is returned instead.
///
Expand All @@ -84,6 +106,13 @@ class FramePos final {
return toEngineSamplePos();
}

double toSamplePosMaybeInvalid(mixxx::audio::ChannelCount channelCount) const {
if (!isValid()) {
return kLegacyInvalidEnginePosition;
}
return toSamplePos(channelCount);
}

/// Return true if the frame position is valid. Any finite value is
/// considered valid, i.e. any value except NaN and negative/positive
/// infinity.
Expand Down
17 changes: 17 additions & 0 deletions src/audio/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ class ChannelCount {
return ChannelCount(valueFromInt(value));
}

static ChannelCount fromDouble(double value) {
const auto channelCount = ChannelCount(static_cast<value_t>(value));
// The channel count should always be an integer value
// and this conversion is supposed to be lossless.
DEBUG_ASSERT(channelCount.toDouble() == value);
return channelCount;
}

static constexpr ChannelCount mono() {
return ChannelCount(static_cast<value_t>(1));
}
Expand All @@ -88,6 +96,10 @@ class ChannelCount {
return ChannelCount(static_cast<value_t>(2));
}

static constexpr ChannelCount stem() {
return ChannelCount(static_cast<value_t>(8)); // 4 stereo channels
}

explicit constexpr ChannelCount(
value_t value = kValueDefault)
: m_value(value) {
Expand Down Expand Up @@ -115,6 +127,11 @@ class ChannelCount {
return value();
}

// Helper cast for COs
constexpr double toDouble() const {
return static_cast<double>(value());
}

private:
value_t m_value;
};
Expand Down
29 changes: 20 additions & 9 deletions src/engine/bufferscalers/enginebufferscale.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,37 @@

#include "engine/engine.h"
#include "moc_enginebufferscale.cpp"
#include "soundio/soundmanagerconfig.h"

EngineBufferScale::EngineBufferScale()
: m_outputSignal(
: m_signal(
mixxx::audio::SignalInfo(
mixxx::kEngineChannelCount,
mixxx::kEngineChannelOutputCount,
mixxx::audio::SampleRate())),
m_dBaseRate(1.0),
m_bSpeedAffectsPitch(false),
m_dTempoRatio(1.0),
m_dPitchRatio(1.0),
m_effectiveRate(1.0) {
DEBUG_ASSERT(!m_outputSignal.isValid());
DEBUG_ASSERT(!m_signal.isValid());
}

void EngineBufferScale::setSampleRate(
mixxx::audio::SampleRate sampleRate) {
void EngineBufferScale::setSignal(
mixxx::audio::SampleRate sampleRate,
mixxx::audio::ChannelCount channelCount) {
DEBUG_ASSERT(sampleRate.isValid());
if (sampleRate != m_outputSignal.getSampleRate()) {
m_outputSignal.setSampleRate(sampleRate);
onSampleRateChanged();
DEBUG_ASSERT(channelCount.isValid());
bool changed = false;
if (sampleRate != m_signal.getSampleRate()) {
m_signal.setSampleRate(sampleRate);
changed = true;
}
DEBUG_ASSERT(m_outputSignal.isValid());
if (channelCount != m_signal.getChannelCount()) {
m_signal.setChannelCount(channelCount);
changed = true;
}
if (changed) {
onSignalChanged();
}
DEBUG_ASSERT(m_signal.isValid());
}
13 changes: 7 additions & 6 deletions src/engine/bufferscalers/enginebufferscale.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,13 @@ class EngineBufferScale : public QObject {
m_dPitchRatio = *pPitchRatio;
}

// Set the desired output sample rate.
void setSampleRate(
mixxx::audio::SampleRate sampleRate);
// Set the desired output signal.
void setSignal(
mixxx::audio::SampleRate sampleRate,
mixxx::audio::ChannelCount channelCout);

const mixxx::audio::SignalInfo& getOutputSignal() const {
return m_outputSignal;
return m_signal;
}

// Called from EngineBuffer when seeking, to ensure the buffers are flushed */
Expand All @@ -64,9 +65,9 @@ class EngineBufferScale : public QObject {
SINT iOutputBufferSize) = 0;

private:
mixxx::audio::SignalInfo m_outputSignal;
mixxx::audio::SignalInfo m_signal;

virtual void onSampleRateChanged() = 0;
virtual void onSignalChanged() = 0;

protected:
double m_dBaseRate;
Expand Down
Loading