summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Szmigin <smidge@xsco.net>2020-06-12 23:10:10 +0100
committerGitHub <noreply@github.com>2020-06-12 23:10:10 +0100
commit8f4c54e27fa1924c15cd3a9e9d300a979123decb (patch)
treefe98633fb333ec544a0899a6964bc7c6898e8a71
parente159163e8ff02dc14a4f26a538ced9b0a483fdb6 (diff)
parent95ca21ee6cc7c58694c64feee27e24d8aed74f78 (diff)
Merge pull request #7 from haslersn/enhancement/allow-reading-malformed-beatgrid
enginelibrary: Allow reading beat data even if the beatgrid is malformed
-rw-r--r--.clang-format2
-rw-r--r--default.nix5
-rw-r--r--include/djinterop/performance_data.hpp42
-rw-r--r--src/djinterop/enginelibrary/el_track_impl.cpp9
-rw-r--r--src/djinterop/enginelibrary/el_track_impl.hpp16
-rw-r--r--src/djinterop/enginelibrary/performance_data_format.cpp17
-rw-r--r--src/djinterop/enginelibrary/performance_data_format.hpp72
7 files changed, 143 insertions, 20 deletions
diff --git a/.clang-format b/.clang-format
index a90ead2..2112752 100644
--- a/.clang-format
+++ b/.clang-format
@@ -94,7 +94,7 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- - Delimiter: 'pb'
+ - Delimiters: [ 'pb' ]
Language: TextProto
BasedOnStyle: google
ReflowComments: true
diff --git a/default.nix b/default.nix
index 88c2fe4..17f27a0 100644
--- a/default.nix
+++ b/default.nix
@@ -6,8 +6,8 @@ let
name = "git-clang-format";
version = "2019-06-21";
src = fetchurl {
- url = "https://raw.githubusercontent.com/llvm-mirror/clang/2bb8e0fe002e8ffaa9ce5fa58034453c94c7e208/tools/clang-format/git-clang-format";
- sha256 = "1kby36i80js6rwi11v3ny4bqsi6i44b9yzs23pdcn9wswffx1nlf";
+ url = "https://raw.githubusercontent.com/llvm-mirror/clang/e8c2d2746b1e718b607b78a830534fd3a981d250/tools/clang-format/git-clang-format";
+ sha256 = "sha256:1wp7zw2jgpgnv9cr648nlk6gs26yadvs14qnll3vnay1y0n79nd7";
executable = true;
};
nativeBuildInputs = [
@@ -42,6 +42,7 @@ stdenv.mkDerivation {
outputs = [ "out" "dev" ];
buildInputs = [
boost
+ sqlite
zlib
];
}
diff --git a/include/djinterop/performance_data.hpp b/include/djinterop/performance_data.hpp
index 67e22e2..620e676 100644
--- a/include/djinterop/performance_data.hpp
+++ b/include/djinterop/performance_data.hpp
@@ -35,12 +35,26 @@ struct sampling_info
{
double sample_rate = 0; // usually 44100.0 or 48000.0
int64_t sample_count = 0;
+
+ friend bool operator==(
+ const sampling_info& first, const sampling_info& second) noexcept
+ {
+ return first.sample_rate == second.sample_rate &&
+ first.sample_count == second.sample_count;
+ }
};
struct beatgrid_marker
{
int32_t index = 0;
double sample_offset = 0;
+
+ friend bool operator==(
+ const beatgrid_marker& first, const beatgrid_marker& second) noexcept
+ {
+ return first.index == second.index &&
+ first.sample_offset == second.sample_offset;
+ }
};
struct hot_cue
@@ -48,6 +62,13 @@ struct hot_cue
std::string label;
double sample_offset = 0;
pad_color color;
+
+ friend bool operator==(const hot_cue& first, const hot_cue& second) noexcept
+ {
+ return first.label == second.label &&
+ first.sample_offset == second.sample_offset &&
+ first.color == second.color;
+ }
};
struct loop
@@ -56,12 +77,26 @@ struct loop
double start_sample_offset = 0;
double end_sample_offset = 0;
pad_color color;
+
+ friend bool operator==(const loop& first, const loop& second) noexcept
+ {
+ return first.label == second.label &&
+ first.start_sample_offset == second.start_sample_offset &&
+ first.end_sample_offset == second.end_sample_offset &&
+ first.color == second.color;
+ }
};
struct waveform_point
{
uint8_t value = 0;
uint8_t opacity = 255;
+
+ friend bool operator==(
+ const waveform_point& first, const waveform_point& second) noexcept
+ {
+ return first.value == second.value && first.opacity == second.opacity;
+ }
};
/**
@@ -82,6 +117,13 @@ struct waveform_entry
waveform_point low;
waveform_point mid;
waveform_point high;
+
+ friend bool operator==(
+ const waveform_entry& first, const waveform_entry& second) noexcept
+ {
+ return first.low == second.low && first.mid == second.mid &&
+ first.high == second.high;
+ }
};
} // namespace djinterop
diff --git a/src/djinterop/enginelibrary/el_track_impl.cpp b/src/djinterop/enginelibrary/el_track_impl.cpp
index fe1ddca..3ab3d7e 100644
--- a/src/djinterop/enginelibrary/el_track_impl.cpp
+++ b/src/djinterop/enginelibrary/el_track_impl.cpp
@@ -201,6 +201,15 @@ overview_waveform_data el_track_impl::get_overview_waveform_data()
void el_track_impl::set_overview_waveform_data(overview_waveform_data data)
{
+ // As the overview waveform does not store opacity, it is defaulted to 255
+ // when read back. If we also set it to 255 here, we can apply a check in
+ // `set_perfdata` that a round-trip encode/decode gives the same data.
+ for (auto&& entry : data.waveform)
+ {
+ entry.low.opacity = 255;
+ entry.mid.opacity = 255;
+ entry.high.opacity = 255;
+ }
set_perfdata("overviewWaveFormData", data);
}
diff --git a/src/djinterop/enginelibrary/el_track_impl.hpp b/src/djinterop/enginelibrary/el_track_impl.hpp
index bd934e1..bf82ee6 100644
--- a/src/djinterop/enginelibrary/el_track_impl.hpp
+++ b/src/djinterop/enginelibrary/el_track_impl.hpp
@@ -135,6 +135,20 @@ public:
template <typename T>
void set_perfdata(const char* column_name, const T& content)
{
+ auto encoded_content = content.encode();
+ // Check that subsequent reads can correctly decode what we are about to
+ // write.
+ if (!(T::decode(encoded_content) == content))
+ {
+ // TODO (haslersn): As soon as warnings are implemented, add the
+ // wording similar to "Either you got a warning above which tells
+ // you what is wrong, or this is a bug in libdjinterop."
+ throw std::logic_error{
+ "Data supplied for column " + std::string(column_name) +
+ " is not invariant under encoding and subsequent decoding. "
+ "This is a bug in libdjinterop."};
+ }
+
bool found = false;
storage_->db << "SELECT COUNT(*) FROM PerformanceData WHERE id = ?"
<< id() >>
@@ -182,7 +196,7 @@ public:
storage_->db << (std::string{"UPDATE PerformanceData SET "} +
column_name + " = ?, isAnalyzed = 1 WHERE id = ?")
- << content.encode() << id();
+ << encoded_content << id();
}
beat_data get_beat_data();
diff --git a/src/djinterop/enginelibrary/performance_data_format.cpp b/src/djinterop/enginelibrary/performance_data_format.cpp
index e346448..58a9a42 100644
--- a/src/djinterop/enginelibrary/performance_data_format.cpp
+++ b/src/djinterop/enginelibrary/performance_data_format.cpp
@@ -193,8 +193,21 @@ beat_data beat_data::decode(const std::vector<char>& compressed_data)
// TODO (haslersn): print a warning that "Is beat data set" is not 1
}
- std::tie(result.default_beatgrid, ptr) = decode_beatgrid(ptr, end);
- std::tie(result.adjusted_beatgrid, ptr) = decode_beatgrid(ptr, end);
+ try
+ {
+ std::vector<beatgrid_marker> default_beatgrid;
+ std::vector<beatgrid_marker> adjusted_beatgrid;
+ std::tie(default_beatgrid, ptr) = decode_beatgrid(ptr, end);
+ std::tie(adjusted_beatgrid, ptr) = decode_beatgrid(ptr, end);
+ // If there's an exception, then the following will intentially not be
+ // executed.
+ result.default_beatgrid = std::move(default_beatgrid);
+ result.adjusted_beatgrid = std::move(adjusted_beatgrid);
+ }
+ catch (const std::invalid_argument& e)
+ {
+ // TODO (haslersn): print a warning with e.what().
+ }
if (ptr != end)
{
diff --git a/src/djinterop/enginelibrary/performance_data_format.hpp b/src/djinterop/enginelibrary/performance_data_format.hpp
index e96fb71..acf1cb6 100644
--- a/src/djinterop/enginelibrary/performance_data_format.hpp
+++ b/src/djinterop/enginelibrary/performance_data_format.hpp
@@ -33,23 +33,39 @@ namespace enginelibrary
{
struct beat_data
{
- beat_data() noexcept = default;
-
boost::optional<sampling_info> sampling;
std::vector<beatgrid_marker> default_beatgrid;
std::vector<beatgrid_marker> adjusted_beatgrid;
+ beat_data() noexcept = default;
+
+ friend bool operator==(
+ const beat_data& first, const beat_data& second) noexcept
+ {
+ return first.sampling == second.sampling &&
+ first.default_beatgrid == second.default_beatgrid &&
+ first.adjusted_beatgrid == second.adjusted_beatgrid;
+ }
+
std::vector<char> encode() const;
static beat_data decode(const std::vector<char>& compressed_data);
};
struct high_res_waveform_data
{
- high_res_waveform_data() noexcept = default;
-
double samples_per_entry;
std::vector<waveform_entry> waveform;
+ high_res_waveform_data() noexcept = default;
+
+ friend bool operator==(
+ const high_res_waveform_data& first,
+ const high_res_waveform_data& second) noexcept
+ {
+ return first.samples_per_entry == second.samples_per_entry &&
+ first.waveform == second.waveform;
+ }
+
std::vector<char> encode() const;
static high_res_waveform_data decode(
const std::vector<char>& compressed_data);
@@ -57,10 +73,16 @@ struct high_res_waveform_data
struct loops_data
{
- loops_data() = default;
-
std::array<boost::optional<loop>, 8> loops; // Don't use curly braces here!
+ loops_data() noexcept = default;
+
+ friend bool operator==(
+ const loops_data& first, const loops_data& second) noexcept
+ {
+ return first.loops == second.loops;
+ }
+
std::vector<char> encode() const;
static loops_data decode(
const std::vector<char>& raw_data); // not compressed
@@ -68,11 +90,19 @@ struct loops_data
struct overview_waveform_data
{
- overview_waveform_data() noexcept = default;
-
double samples_per_entry;
std::vector<waveform_entry> waveform;
+ overview_waveform_data() noexcept = default;
+
+ friend bool operator==(
+ const overview_waveform_data& first,
+ const overview_waveform_data& second) noexcept
+ {
+ return first.samples_per_entry == second.samples_per_entry &&
+ first.waveform == second.waveform;
+ }
+
std::vector<char> encode() const;
static overview_waveform_data decode(
const std::vector<char>& compressed_data);
@@ -80,27 +110,41 @@ struct overview_waveform_data
struct quick_cues_data
{
- quick_cues_data() = default;
-
std::array<boost::optional<hot_cue>, 8> hot_cues;
double adjusted_main_cue = 0;
double default_main_cue = 0;
- std::vector<char> encode() const;
+ quick_cues_data() noexcept = default;
+
+ friend bool operator==(
+ const quick_cues_data& first, const quick_cues_data& second) noexcept
+ {
+ return first.hot_cues == second.hot_cues &&
+ first.adjusted_main_cue == second.adjusted_main_cue &&
+ first.default_main_cue == second.default_main_cue;
+ }
+ std::vector<char> encode() const;
static quick_cues_data decode(const std::vector<char>& compressed_data);
};
struct track_data
{
- track_data() noexcept = default;
-
boost::optional<sampling_info> sampling;
boost::optional<double> average_loudness; // range (0, 1]
boost::optional<musical_key> key;
- std::vector<char> encode() const;
+ track_data() noexcept = default;
+
+ friend bool operator==(
+ const track_data& first, const track_data& second) noexcept
+ {
+ return first.sampling == second.sampling &&
+ first.average_loudness == second.average_loudness &&
+ first.key == second.key;
+ }
+ std::vector<char> encode() const;
static track_data decode(const std::vector<char>& compressed_data);
};