summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--build/depends.py4
-rw-r--r--src/audio/signalinfo.cpp27
-rw-r--r--src/audio/signalinfo.h78
-rw-r--r--src/audio/streaminfo.cpp27
-rw-r--r--src/audio/streaminfo.h64
-rw-r--r--src/audio/types.cpp45
-rw-r--r--src/audio/types.h188
-rw-r--r--src/mixxxapplication.cpp12
-rw-r--r--src/util/duration.h39
-rw-r--r--src/util/macros.h4
-rw-r--r--src/util/optional.h11
12 files changed, 478 insertions, 24 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5053489dca..ed9f31a04b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,9 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/analyzer/plugins/analyzersoundtouchbeats.cpp
src/analyzer/plugins/buffering_utils.cpp
src/analyzer/trackanalysisscheduler.cpp
+ src/audio/types.cpp
+ src/audio/signalinfo.cpp
+ src/audio/streaminfo.cpp
src/control/control.cpp
src/control/controlaudiotaperpot.cpp
src/control/controlbehavior.cpp
diff --git a/build/depends.py b/build/depends.py
index cc6572f845..0f11432197 100644
--- a/build/depends.py
+++ b/build/depends.py
@@ -902,6 +902,10 @@ class MixxxCore(Feature):
"src/analyzer/plugins/analyzerqueenmarykey.cpp",
"src/analyzer/plugins/buffering_utils.cpp",
+ "src/audio/types.cpp",
+ "src/audio/signalinfo.cpp",
+ "src/audio/streaminfo.cpp",
+
"src/controllers/controller.cpp",
"src/controllers/controllerdebug.cpp",
"src/controllers/controllerengine.cpp",
diff --git a/src/audio/signalinfo.cpp b/src/audio/signalinfo.cpp
new file mode 100644
index 0000000000..a86f449033
--- /dev/null
+++ b/src/audio/signalinfo.cpp
@@ -0,0 +1,27 @@
+#include "audio/signalinfo.h"
+
+namespace mixxx {
+
+namespace audio {
+
+bool operator==(
+ const SignalInfo& lhs,
+ const SignalInfo& rhs) {
+ return lhs.getChannelCount() == rhs.getChannelCount() &&
+ lhs.getSampleLayout() == rhs.getSampleLayout() &&
+ lhs.getSampleRate() == rhs.getSampleRate();
+}
+
+QDebug
+operator<<(QDebug dbg, const SignalInfo& arg) {
+ dbg << "SignalInfo{";
+ arg.dbgChannelCount(dbg);
+ arg.dbgSampleLayout(dbg);
+ arg.dbgSampleRate(dbg);
+ dbg << '}';
+ return dbg;
+}
+
+} // namespace audio
+
+} // namespace mixxx
diff --git a/src/audio/signalinfo.h b/src/audio/signalinfo.h
new file mode 100644
index 0000000000..d2e8ffeddf
--- /dev/null
+++ b/src/audio/signalinfo.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "audio/types.h"
+#include "util/assert.h"
+#include "util/macros.h"
+#include "util/optional.h"
+
+namespace mixxx {
+
+namespace audio {
+
+// Properties that characterize an uncompressed PCM audio signal.
+class SignalInfo final {
+ // Properties
+ PROPERTY_SET_BYVAL_GET_BYREF(ChannelCount, channelCount, ChannelCount)
+ PROPERTY_SET_BYVAL_GET_BYREF(SampleRate, sampleRate, SampleRate)
+ PROPERTY_SET_BYVAL_GET_BYREF(OptionalSampleLayout, sampleLayout, SampleLayout)
+
+ public:
+ constexpr SignalInfo() = default;
+ constexpr explicit SignalInfo(
+ OptionalSampleLayout sampleLayout)
+ : m_sampleLayout(sampleLayout) {
+ }
+ SignalInfo(
+ ChannelCount channelCount,
+ SampleRate sampleRate,
+ OptionalSampleLayout sampleLayout = std::nullopt)
+ : m_channelCount(channelCount),
+ m_sampleRate(sampleRate),
+ m_sampleLayout(sampleLayout) {
+ }
+ SignalInfo(SignalInfo&&) = default;
+ SignalInfo(const SignalInfo&) = default;
+ /*non-virtual*/ ~SignalInfo() = default;
+
+ constexpr bool isValid() const {
+ return getChannelCount().isValid() &&
+ getSampleLayout() &&
+ getSampleRate().isValid();
+ }
+
+ SignalInfo& operator=(SignalInfo&&) = default;
+ SignalInfo& operator=(const SignalInfo&) = default;
+
+ // Conversion: #samples / sample offset -> #frames / frame offset
+ template<typename T>
+ inline T samples2frames(T samples) const {
+ DEBUG_ASSERT(getChannelCount().isValid());
+ DEBUG_ASSERT(0 == (samples % getChannelCount()));
+ return samples / getChannelCount();
+ }
+
+ // Conversion: #frames / frame offset -> #samples / sample offset
+ template<typename T>
+ inline T frames2samples(T frames) const {
+ DEBUG_ASSERT(getChannelCount().isValid());
+ return frames * getChannelCount();
+ }
+};
+
+bool operator==(
+ const SignalInfo& lhs,
+ const SignalInfo& rhs);
+
+inline bool operator!=(
+ const SignalInfo& lhs,
+ const SignalInfo& rhs) {
+ return !(lhs == rhs);
+}
+
+QDebug operator<<(QDebug dbg, const SignalInfo& arg);
+
+} // namespace audio
+
+} // namespace mixxx
+
+Q_DECLARE_METATYPE(mixxx::audio::SignalInfo)
diff --git a/src/audio/streaminfo.cpp b/src/audio/streaminfo.cpp
new file mode 100644
index 0000000000..d97c2c113c
--- /dev/null
+++ b/src/audio/streaminfo.cpp
@@ -0,0 +1,27 @@
+#include "audio/streaminfo.h"
+
+namespace mixxx {
+
+namespace audio {
+
+bool operator==(
+ const StreamInfo& lhs,
+ const StreamInfo& rhs) {
+ return lhs.getSignalInfo() == rhs.getSignalInfo() &&
+ lhs.getBitrate() == rhs.getBitrate() &&
+ lhs.getDuration() == rhs.getDuration();
+}
+
+QDebug
+operator<<(QDebug dbg, const StreamInfo& arg) {
+ dbg << "StreamInfo{";
+ arg.dbgSignalInfo(dbg);
+ arg.dbgBitrate(dbg);
+ arg.dbgDuration(dbg);
+ dbg << '}';
+ return dbg;
+}
+
+} // namespace audio
+
+} // namespace mixxx
diff --git a/src/audio/streaminfo.h b/src/audio/streaminfo.h
new file mode 100644
index 0000000000..8a70a6bd39
--- /dev/null
+++ b/src/audio/streaminfo.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "audio/signalinfo.h"
+#include "util/duration.h"
+
+namespace mixxx {
+
+namespace audio {
+
+// Properties that characterize a (compressed) PCM audio stream.
+//
+// Currently we assume that every stream has a finite duration
+// that is known upfront!
+class StreamInfo final {
+ // Properties
+ PROPERTY_SET_BYVAL_GET_BYREF(SignalInfo, signalInfo, SignalInfo)
+ PROPERTY_SET_BYVAL_GET_BYREF(Bitrate, bitrate, Bitrate)
+ PROPERTY_SET_BYVAL_GET_BYREF(Duration, duration, Duration)
+
+ public:
+ constexpr StreamInfo() = default;
+ constexpr explicit StreamInfo(
+ const SignalInfo& signalInfo)
+ : m_signalInfo(signalInfo) {
+ }
+ constexpr StreamInfo(
+ const SignalInfo& signalInfo,
+ Bitrate bitrate,
+ Duration duration)
+ : m_signalInfo(signalInfo),
+ m_bitrate(bitrate),
+ m_duration(duration) {
+ }
+ StreamInfo(StreamInfo&&) = default;
+ StreamInfo(const StreamInfo&) = default;
+ /*non-virtual*/ ~StreamInfo() = default;
+
+ constexpr bool isValid() const {
+ return getSignalInfo().isValid() &&
+ getBitrate().isValid() &&
+ (getDuration() > Duration::empty());
+ }
+
+ StreamInfo& operator=(StreamInfo&&) = default;
+ StreamInfo& operator=(const StreamInfo&) = default;
+};
+
+bool operator==(
+ const StreamInfo& lhs,
+ const StreamInfo& rhs);
+
+inline bool operator!=(
+ const StreamInfo& lhs,
+ const StreamInfo& rhs) {
+ return !(lhs == rhs);
+}
+
+QDebug operator<<(QDebug dbg, const StreamInfo& arg);
+
+} // namespace audio
+
+} // namespace mixxx
+
+Q_DECLARE_METATYPE(mixxx::audio::StreamInfo)
diff --git a/src/audio/types.cpp b/src/audio/types.cpp
new file mode 100644
index 0000000000..f7addcc954
--- /dev/null
+++ b/src/audio/types.cpp
@@ -0,0 +1,45 @@
+#include "audio/types.h"
+
+namespace mixxx {
+
+namespace audio {
+
+QDebug operator<<(QDebug dbg, ChannelLayout arg) {
+ switch (arg) {
+ case ChannelLayout::Mono:
+ return dbg << "Mono";
+ case ChannelLayout::DualMono:
+ return dbg << "DualMono";
+ case ChannelLayout::Stereo:
+ return dbg << "Stereo";
+ }
+ DEBUG_ASSERT(!"unreachable code");
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, SampleLayout arg) {
+ switch (arg) {
+ case SampleLayout::Planar:
+ return dbg << "Planar";
+ case SampleLayout::Interleaved:
+ return dbg << "Interleaved";
+ }
+ DEBUG_ASSERT(!"unreachable code");
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, SampleRate arg) {
+ return dbg
+ << QString::number(arg).toLocal8Bit().constData()
+ << SampleRate::unit();
+}
+
+QDebug operator<<(QDebug dbg, Bitrate arg) {
+ return dbg
+ << QString::number(arg).toLocal8Bit().constData()
+ << Bitrate::unit();
+}
+
+} // namespace audio
+
+} // namespace mixxx
diff --git a/src/audio/types.h b/src/audio/types.h
new file mode 100644
index 0000000000..497fa2102e
--- /dev/null
+++ b/src/audio/types.h
@@ -0,0 +1,188 @@
+#pragma once
+
+#include <QtDebug>
+
+#include "util/assert.h"
+#include "util/optional.h"
+#include "util/types.h"
+
+// Various properties of digital PCM audio signals and streams.
+//
+// An audio signal or stream contains samples for multiple
+// channels sampled at discrete times.
+//
+// The channel layout (optional) assigns meaning to the
+// different channels of a signal.
+//
+// The sample layout defines how subsequent samples from
+// different channels are represented and stored in memory.
+
+namespace mixxx {
+
+namespace audio {
+
+enum class ChannelLayout {
+ Mono, // 1 channel
+ DualMono, // 2 channels with identical signals
+ Stereo, // 2 independent channels left/right
+ // ...to be continued...
+};
+
+typedef std::optional<ChannelLayout> OptionalChannelLayout;
+
+QDebug operator<<(QDebug dbg, ChannelLayout arg);
+
+class ChannelCount {
+ private:
+ static constexpr SINT kValueDefault = 0;
+
+ public:
+ static constexpr SINT kValueMin = 1; // lower bound (inclusive)
+ static constexpr SINT kValueMax = 255; // upper bound (inclusive, 8-bit unsigned integer)
+
+ static constexpr ChannelCount min() {
+ return ChannelCount(kValueMin);
+ }
+ static constexpr ChannelCount max() {
+ return ChannelCount(kValueMax);
+ }
+
+ static ChannelCount fromLayout(ChannelLayout layout) {
+ switch (layout) {
+ case ChannelLayout::Mono:
+ return ChannelCount(1);
+ case ChannelLayout::DualMono:
+ return ChannelCount(1);
+ case ChannelLayout::Stereo:
+ return ChannelCount(2);
+ }
+ DEBUG_ASSERT(!"unreachable code");
+ }
+
+ explicit constexpr ChannelCount(SINT value = kValueDefault)
+ : m_value(value) {
+ }
+ explicit ChannelCount(ChannelLayout layout)
+ : m_value(fromLayout(layout).m_value) {
+ }
+
+ constexpr bool isValid() const {
+ return (kValueMin <= m_value) &&
+ (m_value <= kValueMax);
+ }
+
+ /*implicit*/ constexpr operator SINT() const {
+ return m_value;
+ }
+
+ private:
+ SINT m_value;
+};
+
+// Defines the ordering of how samples from multiple channels are
+// stored in contiguous buffers:
+// - Planar: Channel by channel
+// - Interleaved: Frame by frame
+// The samples from all channels that are coincident in time are
+// called a "frame" (or more specific "sample frame").
+//
+// Example: 10 stereo samples from left (L) and right (R) channel
+// Planar layout: LLLLLLLLLLRRRRRRRRRR
+// Interleaved layout: LRLRLRLRLRLRLRLRLRLR
+enum class SampleLayout {
+ Planar,
+ Interleaved
+};
+
+typedef std::optional<SampleLayout> OptionalSampleLayout;
+
+QDebug operator<<(QDebug dbg, SampleLayout arg);
+
+class SampleRate {
+ private:
+ static constexpr SINT kValueDefault = 0;
+
+ public:
+ static constexpr SINT kValueMin = 8000; // lower bound (inclusive, = minimum MP3 sample rate)
+ static constexpr SINT kValueMax = 192000; // upper bound (inclusive)
+
+ static constexpr SampleRate min() {
+ return SampleRate(kValueMin);
+ }
+ static constexpr SampleRate max() {
+ return SampleRate(kValueMax);
+ }
+
+ static constexpr const char* unit() {
+ return "Hz";
+ }
+
+ explicit constexpr SampleRate(SINT value = kValueDefault)
+ : m_value(value) {
+ }
+
+ constexpr bool isValid() const {
+ return (kValueMin <= m_value) &&
+ (m_value <= kValueMax);
+ }
+
+ /*implicit*/ constexpr operator SINT() const {
+ return m_value;
+ }
+
+ private:
+ SINT m_value;
+};
+
+QDebug operator<<(QDebug dbg, SampleRate arg);
+
+// The bitrate is measured in kbit/s (kbps) and provides information
+// about the level of compression for lossily encoded audio streams.
+// It depends on the metadata and decoder if a value for the bitrate
+// is available, i.e. it might be invalid if it cannot be determined.
+class Bitrate {
+ private:
+ static constexpr SINT kValueDefault = 0;
+
+ public:
+ static constexpr const char* unit() {
+ return "kbps";
+ }
+
+ explicit constexpr Bitrate(SINT value = kValueDefault)
+ : m_value(value) {
+ }
+
+ constexpr bool isValid() const {
+ return m_value > kValueDefault;
+ }
+
+ /*implicit*/ operator SINT() const {
+ DEBUG_ASSERT(m_value >= kValueDefault); // unsigned value
+ return m_value;
+ }
+
+ private:
+ SINT m_value;
+};
+
+QDebug operator<<(QDebug dbg, Bitrate arg);
+
+} // namespace audio
+
+} // namespace mixxx
+
+Q_DECLARE_TYPEINFO(mixxx::audio::ChannelCount, Q_PRIMITIVE_TYPE);
+Q_DECLARE_METATYPE(mixxx::audio::ChannelCount)
+
+Q_DECLARE_TYPEINFO(mixxx::audio::OptionalChannelLayout, Q_PRIMITIVE_TYPE);
+Q_DECLARE_METATYPE(mixxx::audio::OptionalChannelLayout)
+
+Q_DECLARE_TYPEINFO(mixxx::audio::OptionalSampleLayout, Q_PRIMITIVE_TYPE);
+Q_DECLARE_METATYPE(mixxx::audio::OptionalSampleLayout)
+
+Q_DECLARE_TYPEINFO(mixxx::audio::SampleRate, Q_PRIMITIVE_TYPE);
+Q_DECLARE_METATYPE(mixxx::audio::SampleRate)
+
+Q_DECLARE_TYPEINFO(mixxx::audio::Bitrate, Q_PRIMITIVE_TYPE);
+Q_DECLARE_METATYPE(mixxx::audio::Bitrate)
diff --git a/src/mixxxapplication.cpp b/src/mixxxapplication.cpp
index 412c342cc1..51349d4ca5 100644
--- a/src/mixxxapplication.cpp
+++ b/src/mixxxapplication.cpp
@@ -4,6 +4,7 @@
#include "mixxxapplication.h"
+#include "audio/types.h"
#include "control/controlproxy.h"
#include "library/crate/crateid.h"
#include "soundio/soundmanagerutil.h"
@@ -55,7 +56,16 @@ MixxxApplication::~MixxxApplication() {
}
void MixxxApplication::registerMetaTypes() {
- // Register custom data types for signal processing
+ // Register custom data types
+
+ // PCM audio types
+ qRegisterMetaType<mixxx::audio::ChannelCount>("mixxx::audio::ChannelCount");
+ qRegisterMetaType<mixxx::audio::OptionalChannelLayout>("mixxx::audio::OptionalChannelLayout");
+ qRegisterMetaType<mixxx::audio::OptionalSampleLayout>("mixxx::audio::OptionalSampleLayout");
+ qRegisterMetaType<mixxx::audio::SampleRate>("mixxx::audio::SampleRate");
+ qRegisterMetaType<mixxx::audio::Bitrate>("mixxx::audio::Bitrate");
+
+ // TrackId
qRegisterMetaType<TrackId>();
qRegisterMetaType<QSet<TrackId>>();
qRegisterMetaType<QList<TrackId>>();
diff --git a/src/util/duration.h b/src/util/duration.h
index 0d933f9032..fae49b50dc 100644
--- a/src/util/duration.h
+++ b/src/util/duration.h
@@ -1,5 +1,4 @@
-#ifndef MIXXX_UTIL_DURATION_H
-#define MIXXX_UTIL_DURATION_H
+#pragma once
#include <QMetaType>
#include <QString>
@@ -23,43 +22,43 @@ class DurationBase {
};
// Returns the duration as an integer number of seconds (rounded-down).
- qint64 toIntegerSeconds() const {
+ constexpr qint64 toIntegerSeconds() const {
return m_durationNanos / kNanosPerSecond;
}
// Returns the duration as a floating point number of seconds.
- double toDoubleSeconds() const {
+ constexpr double toDoubleSeconds() const {
return static_cast<double>(m_durationNanos) / kNanosPerSecond;
}
// Returns the duration as an integer number of milliseconds (rounded-down).
- qint64 toIntegerMillis() const {
+ constexpr qint64 toIntegerMillis() const {
return m_durationNanos / kNanosPerMilli;
}
// Returns the duration as a floating point number of milliseconds.
- double toDoubleMillis() const {
+ constexpr double toDoubleMillis() const {
return static_cast<double>(m_durationNanos) / kNanosPerMilli;
}
// Returns the duration as an integer number of microseconds (rounded-down).
- qint64 toIntegerMicros() const {
+ constexpr qint64 toIntegerMicros() const {
return m_durationNanos / kNanosPerMicro;
}
// Returns the duration as a floating point number of microseconds.
- double toDoubleMicros() const {
+ constexpr double toDoubleMicros() const {
return static_cast<double>(m_durationNanos) / kNanosPerMicro;
}
// Returns the duration as an integer number of nanoseconds. The duration is
// represented internally as nanoseconds so no rounding occurs.
- qint64 toIntegerNanos() const {
+ constexpr qint64 toIntegerNanos() const {
return m_durationNanos;
}
// Returns the duration as an integer number of nanoseconds.
- double toDoubleNanos() const {
+ constexpr double toDoubleNanos() const {
return static_cast<double>(m_durationNanos);
}
@@ -96,7 +95,7 @@ class DurationBase {
static QChar kDecimalSeparator;
protected:
- explicit DurationBase(qint64 durationNanos)
+ explicit constexpr DurationBase(qint64 durationNanos)
: m_durationNanos(durationNanos) {
}
@@ -105,7 +104,7 @@ class DurationBase {
class DurationDebug : public DurationBase {
public:
- DurationDebug(const DurationBase& duration, Units unit)
+ constexpr DurationDebug(const DurationBase& duration, Units unit)
: DurationBase(duration),
m_unit(unit) {
}
@@ -136,30 +135,30 @@ class Duration : public DurationBase {
public:
// Returns a Duration object representing a duration of 'seconds'.
template<typename T>
- static Duration fromSeconds(T seconds) {
+ static constexpr Duration fromSeconds(T seconds) {
return Duration(seconds * kNanosPerSecond);
}
// Returns a Duration object representing a duration of 'millis'.
- static Duration fromMillis(qint64 millis) {
+ static constexpr Duration fromMillis(qint64 millis) {
return Duration(millis * kNanosPerMilli);
}
// Returns a Duration object representing a duration of 'micros'.
- static Duration fromMicros(qint64 micros) {
+ static constexpr Duration fromMicros(qint64 micros) {
return Duration(micros * kNanosPerMicro);
}
// Returns a Duration object representing a duration of 'nanos'.
- static Duration fromNanos(qint64 nanos) {
+ static constexpr Duration fromNanos(qint64 nanos) {
return Duration(nanos);
}
- static Duration empty() {
+ static constexpr Duration empty() {
return Duration();
}
- Duration()
+ constexpr Duration()
: DurationBase(0) {
}
@@ -265,7 +264,7 @@ class Duration : public DurationBase {
}
private:
- explicit Duration(qint64 durationNanos)
+ explicit constexpr Duration(qint64 durationNanos)
: DurationBase(durationNanos) {
}
};
@@ -274,5 +273,3 @@ class Duration : public DurationBase {
Q_DECLARE_TYPEINFO(mixxx::Duration, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(mixxx::Duration)
-
-#endif /* MIXXX_UTIL_DURATION_H */
diff --git a/src/util/macros.h b/src/util/macros.h
index accda62304..080c139184 100644
--- a/src/util/macros.h
+++ b/src/util/macros.h
@@ -14,7 +14,7 @@
// classes.
#define PROPERTY_SET_BYVAL_GET_BYREF(TYPE, NAME, CAP_NAME) \
public: void set##CAP_NAME(TYPE NAME) { m_##NAME = std::move(NAME); } \
-public: TYPE const& get##CAP_NAME() const { return m_##NAME; } \
-public: TYPE& ref##CAP_NAME() { return m_##NAME; } \
+public: constexpr TYPE const& get##CAP_NAME() const { return m_##NAME; } \
+public: constexpr TYPE& ref##CAP_NAME() { return m_##NAME; } \
public: QDebug dbg##CAP_NAME(QDebug dbg) const { return dbg << #NAME ":" << m_##NAME; } \
private: TYPE m_##NAME;
diff --git a/src/util/optional.h b/src/util/optional.h
index 5767794f36..6fe9139b0c 100644
--- a/src/util/optional.h
+++ b/src/util/optional.h
@@ -22,3 +22,14 @@ using std::experimental::optional;
} // namespace std
#endif
+
+#include <QtDebug>
+
+template<typename T>
+QDebug operator<<(QDebug dbg, std::optional<T> arg) {
+ if (arg) {
+ return dbg << *arg;
+ } else {
+ return dbg << "nullopt";
+ }
+}