diff options
author | haslersn <sebastian.hasler@gmx.net> | 2019-06-27 16:37:33 +0200 |
---|---|---|
committer | haslersn <sebastian.hasler@gmx.net> | 2019-07-08 00:37:28 +0200 |
commit | d246cfdb8e7bc75761c7972b4e1203bdbc6ca44b (patch) | |
tree | 96ec21997eab43829d223f8fffc91b833dd490d8 | |
parent | f7939f56d650ed999172050fe58093c0b24e15b4 (diff) |
treewide: Rework API
The API methods now operate directly on the database. Therefore, the
classes don't have a `save()` method anymore.
Also, the distinction between tracks and their performance data has
been hidden from the API user. That is, the performance data attributes
are now accessible directly through the `track` API.
This is WORK IN PROGRESS. See the TODOs.
27 files changed, 3235 insertions, 4361 deletions
@@ -38,121 +38,65 @@ How Do I Use It? The library is not ready for prime-time yet, but if you are willing to read the source code, you can get an example application up and running using code similar to the following: ```c++ -#include <chrono> -#include <iostream> #include <djinterop/enginelibrary.hpp> -namespace c = std::chrono; namespace el = djinterop::enginelibrary; int main(int argc, char **argv) { - auto db_dir = "Engine Library"; - auto db = el::create_database(db_dir, el::version_1_7_1); - - // Set track data - el::track t; - t.set_track_number(1); - t.set_duration(std::chrono::seconds{366}); - t.set_bpm(120); - t.set_year(1970); - t.set_title("Some Song"); - t.set_artist("Some Artist"); - t.set_key(el::musical_key::a_minor); - t.set_path("../01 - Some Artist - Some Song.mp3"); - t.set_filename("01 - Some Artist - Some Song.mp3"); - t.set_file_extension("mp3"); - t.set_bitrate(320); - t.save(db); - - // Set core performance data - el::performance_data p{t.id()}; - p.set_sample_rate(44100); - p.set_total_samples(16140600); - p.set_key(el::musical_key::a_minor); - p.set_average_loudness(0.5); - - // Set beat grid - p.set_default_beat_grid(el::track_beat_grid{ - -4, - -83316.78, - 812, - 17470734.439}); - - // Set cues - std::vector<el::track_hot_cue_point> cues; - cues.emplace_back(true, "Cue 1", 1377924.5, el::standard_pad_colours::pad_1); - cues.emplace_back(); - cues.emplace_back(true, "Cue 3", 5508265.964, el::standard_pad_colours::pad_3); - p.set_hot_cues(std::begin(cues), std::end(cues)); - p.set_adjusted_main_cue_sample_offset(2732); - p.set_default_main_cue_sample_offset(2732); - - // Set loops - std::vector<el::track_loop> loops; - loops.emplace_back( - true, true, "Loop 1", - 1144.012, 345339.134, el::standard_pad_colours::pad_1); - p.set_loops(std::begin(loops), std::end(loops)); - - // Set overview waveform - std::vector<el::overview_waveform_entry> ov_waveform_entries; - uint_least64_t ov_adjusted_total_samples; - uint_least64_t ov_num_entries; - double ov_samples_per_entry; - el::calculate_overview_waveform_details( - p.total_samples(), - p.sample_rate(), - ov_adjusted_total_samples, - ov_num_entries, - ov_samples_per_entry); - for (auto i = 0; i < ov_num_entries; ++i) - { - ov_waveform_entries.emplace_back( - i * 255 / ov_num_entries, - i * 127 / ov_num_entries, - i * 63 / ov_num_entries); - } - p.set_overview_waveform_entries( - ov_num_entries, - ov_samples_per_entry, - std::begin(ov_waveform_entries), - std::end(ov_waveform_entries)); + auto db = el::make_database("Engine Library"); + + auto tr = db.create_track("../01 - Some Artist - Some Song.mp3"); + + tr.set_track_number(1); + tr.set_bpm(120); + tr.set_year(1970); + tr.set_title("Some Song"); + tr.set_artist("Some Artist"); + tr.set_publisher(boost::none); // boost::none indicates missing metadata + tr.set_key(el::musical_key::a_minor); + tr.set_bitrate(320); + tr.set_average_loudness(0.5); // loudness range (0, 1] + tr.set_sampling({44100, // sample rate + 16140600}); // sample count + std::vector<el::beatgrid_marker> beatgrid{ + {-4, -83316.78}, // 1st marker + {812, 17470734.439}}; // 2nd marker + tr.set_default_beatgrid(beatgrid); // as analyzed + tr.set_adjusted_beatgrid(beatgrid); // manually adjusted + + // The main cue concerns the cue button + tr.set_default_main_cue(2732); // as analyzed + tr.set_adjusted_main_cue(2732); // manually adjusted + + // There are always 8 hot cues, whereby each can optionally be set + std::array<boost::optional<el::hot_cue>, 8> cues; + cues[0] = el::hot_cue{"Cue 1", 1377924.5, // position in number of samples + el::standard_pad_colors::pad_1}; + tr.set_hot_cues(cues); + + // Setting a single hot cue can also be done like this + tr.set_hot_cue_at(3, {"Cue 4", 5508265.96, el::standard_pad_colors::pad_4}); + + // The loop API works like the hot cue API + tr.set_loop_at( + 0, {"Loop 1", 1144.012, 345339.134, el::standard_pad_colors::pad_1}); // Set high-resolution waveform - std::vector<el::high_res_waveform_entry> hr_waveform_entries; - uint_least64_t hr_adjusted_total_samples; - uint_least64_t hr_num_entries; - double hr_samples_per_entry; - el::calculate_high_res_waveform_details( - p.total_samples(), - p.sample_rate(), - hr_adjusted_total_samples, - hr_num_entries, - hr_samples_per_entry); - for (auto i = 0; i < hr_num_entries; ++i) + int64_t waveform_size = tr.recommended_waveform_size(); + std::vector<el::waveform_entry> waveform; + waveform.reserve(waveform_size); + for (int64_t i = 0; i < waveform_size; ++i) { - hr_waveform_entries.emplace_back( - 255, - (i * 127 / hr_num_entries) + 128, - i * 255 / hr_num_entries, - i * 255 / hr_num_entries, - i * 255 / hr_num_entries, - i * 255 / hr_num_entries); + waveform.push_back( // VALUE and OPACITY for each band (low/mid/high) + {{0, 255}, // low + {42, 255}, // mid + {255, 255}}); // high } - p.set_high_res_waveform_entries( - hr_num_entries, - hr_samples_per_entry, - std::begin(hr_waveform_entries), - std::end(hr_waveform_entries)); - p.save(db); - - el::crate cr; - cr.set_name("My Example Crate"); - cr.add_track(t.id()); - cr.save(db); - - return 0; + tr.set_waveform(std::move(waveform)); + + auto cr = db.create_crate("My Example Crate"); + cr.add_track(tr); } ``` diff --git a/include/djinterop/enginelibrary.hpp b/include/djinterop/enginelibrary.hpp index 6a7c33c..07e0b9c 100644 --- a/include/djinterop/enginelibrary.hpp +++ b/include/djinterop/enginelibrary.hpp @@ -22,9 +22,7 @@ #ifndef DJINTEROP_ENGINELIBRARY_HPP #define DJINTEROP_ENGINELIBRARY_HPP -#include <djinterop/enginelibrary/crate.hpp> #include <djinterop/enginelibrary/database.hpp> #include <djinterop/enginelibrary/performance_data.hpp> -#include <djinterop/enginelibrary/track.hpp> #endif // DJINTEROP_ENGINELIBRARY_HPP diff --git a/include/djinterop/enginelibrary/crate.hpp b/include/djinterop/enginelibrary/crate.hpp index d935906..512600f 100644 --- a/include/djinterop/enginelibrary/crate.hpp +++ b/include/djinterop/enginelibrary/crate.hpp @@ -22,232 +22,150 @@ #ifndef DJINTEROP_ENGINELIBRARY_CRATE_HPP #define DJINTEROP_ENGINELIBRARY_CRATE_HPP -#include <chrono> -#include <cstdint> #include <memory> #include <stdexcept> #include <string> #include <unordered_set> #include <vector> -#include "djinterop/enginelibrary/database.hpp" + +#include <boost/optional.hpp> + +#include <djinterop/enginelibrary/schema_version.hpp> namespace djinterop { namespace enginelibrary { -/** - * The `nonexistent_crate` exception is thrown when a request is made for - * a crate using an identifier that does not exist in a given database - */ -class nonexistent_crate : public std::invalid_argument +class database; +class track; + +/// The `crate_deleted` exception is thrown when an invalid `crate` object is +/// used, i.e. one that does not exist in the database anymore. +class crate_deleted : public std::runtime_error { public: - /** - * \brief Construct the exception for a given crate id - */ - explicit nonexistent_crate(int id) noexcept - : invalid_argument{"Crate does not exist in database"}, id_{id} + /// Constructs the exception for a given crate ID + explicit crate_deleted(int64_t id) noexcept + : runtime_error{"Crate does not exist in database anymore"}, id_{id} { } - /** - * \brief Destructor - */ - virtual ~nonexistent_crate() = default; - - /** - * \brief Get the crate id that was deemed non-existent - */ - int id() const noexcept { return id_; } + /// Returns the crate ID that was deemed non-existent + int64_t id() const noexcept { return id_; } private: - int id_; + int64_t id_; }; -/** - * The `crate_database_inconsistency` exception is thrown when there is an - * internal database inconsistency with regard to how a crate is stored - */ -class crate_database_inconsistency : public std::logic_error +/// The `crate_database_inconsistency` exception is thrown when a database +/// inconsistency is found that correlates to a crate. +class crate_database_inconsistency : public database_inconsistency { public: - /** - * \brief Construct the exception for a given crate id and custom message - */ + /// Construct the exception for a given crate ID explicit crate_database_inconsistency( - const std::string &what_arg, int id) noexcept - : logic_error{what_arg.c_str()}, id_{id} - { - } - - /** - * \brief Construct the exception for a given crate id - */ - explicit crate_database_inconsistency(int id) noexcept - : logic_error{"Crate has internal database inconsistency"}, id_{id} + const std::string& what_arg, int64_t id) noexcept + : database_inconsistency{what_arg.c_str()}, id_{id} { } - /** - * \brief Destructor - */ - virtual ~crate_database_inconsistency() = default; - - /** - * \brief Get the crate id that was deemed inconsistent - */ - int id() const noexcept { return id_; } + /// Get the crate ID that was deemed inconsistent + int64_t id() const noexcept { return id_; } private: - int id_; + int64_t id_; }; -/** - * The `crate` class represents a record of a given crate of tracks (or indeed - * other crates) in the database - */ +/// A `crate` object is a handle to a crate stored in a database. As long as it +/// lives, the corresponding database connection is kept open. +/// +/// `crate` objects can be copied and assigned cheaply, resulting in multiple +/// handles to the same actual crate. +/// +/// The read/write operations provided by this class directly access the +/// database. +/// +/// A `crate` object becomes invalid if the crate gets deleted by +/// `database::remove_crate()`. After that, you must not call any methods on the +/// `crate` object, except for destructing it, or assigning to it. class crate { public: - typedef std::vector<int>::const_iterator crate_id_iterator; - typedef std::unordered_set<int>::const_iterator track_id_iterator; - - /** - * \brief Construct a new crate, not yet saved in any database - */ - crate(); - - /** - * \brief Copy a crate from another - * - * Note that the new copied crate will not be considered to belong in any - * database, and hence will have its `id` set to zero. - */ - crate(const crate &other); - - /** - * \brief Construct a crate, loaded from a given database by its id - */ - crate(const database &database, int id); - - /** - * \brief Destructor - */ + /// Copy constructor + crate(const crate& other) noexcept; + + /// Destructor ~crate(); - /** - * \brief Copy from another crate into this crate - * - * Note that this crate will not be considered to belong in any database - * after copy assignment, and hence will have its `id` set to zero. - */ - crate &operator=(const crate &other); - - /** - * \brief Get the id of this crate, as stored in the database. - * - * If the crate is not yet stored in any database, the id will be zero. - */ - int id() const; - - /** - * \brief Get the crate's name - */ + /// Copy assignment operator + crate& operator=(const crate& other) noexcept; + + /// Adds a track to the crate + /// + /// A track can be contained in arbitrarily many (including zero) crates. + void add_track(track tr) const; + + /// Returns the (direct) children of this crate + std::vector<crate> children() const; + + /// Removes all tracks from the crate + /// + /// Note that the tracks stay in the database even if they're contained in + /// zero crates. + void clear_tracks() const; + + /// Returns the database containing the crate + database db() const; + + /// Returns the descendants of this crate + /// + /// A descendant is a direct or indirect child of this crate. + std::vector<crate> descendants() const; + + /// Returns the ID of this crate + /// + /// The ID is used internally in the database and is unique for crates + /// contained in the same database. + int64_t id() const; + + /// Returns `true` iff `*this` is valid as described in the class comment + bool is_valid() const; + + /// Returns the crate's name std::string name() const; - /** - * \brief Get a `bool` indicating if this crate has a parent crate - */ - bool has_parent() const; - - /** - * \brief Get the parent crate id for this crate, if it has one - * - * If the crate does not have a parent, the return value of this method is - * undefined. - */ - int parent_id() const; - - /** - * \brief Get iterator to the start of a list of child crates of this one - * - * Note that if child crates are simultaneously modified in the background, - * this list may no longer reflect the current reality in the database. - */ - crate_id_iterator child_crates_begin() const; - - /** - * \brief Get iterator beyond the last element of a list of child crates of - * this one - * - * Note that if child crates are simultaneously modified in the background, - * this list may no longer reflect the current reality in the database. - */ - crate_id_iterator child_crates_end() const; - - /** - * \brief Get iterator to the beginning of a list of track ids that are part - * of this crate - */ - track_id_iterator tracks_begin() const; - - /** - * \brief Get iterator beyond the last element of a list of track ids that - * are part of this crate - */ - track_id_iterator tracks_end() const; - - void set_name(const std::string &name); - void set_parent_id(int parent_crate_id); - void set_no_parent(); - void add_tracks(track_id_iterator begin, track_id_iterator end); - void add_track(int track_id); - void set_tracks(track_id_iterator begin, track_id_iterator end); - void clear_tracks(); - - /** - * \brief Save a crate to a given database - * - * If the crate came from the supplied database originally, it is updated - * in-place. If the crate does not already exist in the supplied database, - * then it will be saved as a new track there, and the `id()` method will - * return a new value after a new track is saved. - * - * If you are moving a crate to a new database, you may wish to double-check - * the validity of the parent crate id, if one has been set. If a parent - * crate id is taken from a previous database and accidentally left on a - * crate that is to be saved in a different database, the id may be invalid - * in the context of the new database! - * - * The same argument applies for the list of tracks in the crate: a crate - * may only contain tracks from the same database in which the crate lives. - */ - void save(const database &database); + /// Returns the parent crate, if this crate has one + /// + /// If the crate doesn't have a parent, then an `boost::none` is returned. + boost::optional<crate> parent() const; -private: - struct impl; - std::unique_ptr<impl> pimpl_; -}; + /// Removes a track from the crate + /// + /// Note that the track stays in the database even if it's contained in zero + /// crates. + void remove_track(track tr) const; -/** - * \brief Get a list of all crate ids in a given database - */ -std::vector<int> all_crate_ids(const database &database); + /// Sets the crate's name + void set_name(const std::string& name) const; -/** - * \brief Get a list of all "root" crate ids in a given database, i.e. those - * that are not a sub-crate of another - */ -std::vector<int> all_root_crate_ids(const database &database); + /// Sets this crate's parent + /// + /// If an empty `boost::optional` is given, then this crate will have no + /// parent. That is, it becomes a root crate. + void set_parent(boost::optional<crate> parent) const; -/** - * \brief Try to find a crate by its (unique) name - * - * If the crate is found, its id will be written to the provided reference - * variable. - */ -bool find_crate_by_name( - const database &database, const std::string &name, int &crate_id); + /// Returns the crate's contained tracks + std::vector<track> tracks() const; + +private: + crate(database db, int64_t id); + + struct impl; + std::shared_ptr<impl> pimpl_; + + friend class database; + friend class track; +}; } // namespace enginelibrary } // namespace djinterop diff --git a/include/djinterop/enginelibrary/database.hpp b/include/djinterop/enginelibrary/database.hpp index b26a875..6ed414c 100644 --- a/include/djinterop/enginelibrary/database.hpp +++ b/include/djinterop/enginelibrary/database.hpp @@ -23,12 +23,15 @@ #define DJINTEROP_ENGINELIBRARY_DATABASE_HPP #include <memory> -#include <ostream> #include <stdexcept> #include <string> -#include "sqlite_modern_cpp.h" +#include <vector> -#include "schema_version.hpp" +#include <boost/optional.hpp> + +#include <djinterop/enginelibrary/crate.hpp> +#include <djinterop/enginelibrary/schema_version.hpp> +#include <djinterop/enginelibrary/track.hpp> namespace sqlite { @@ -39,91 +42,118 @@ namespace djinterop { namespace enginelibrary { +class database_impl; + class database_not_found : public std::runtime_error { public: - explicit database_not_found(const std::string &what_arg) noexcept + explicit database_not_found(const std::string& what_arg) noexcept : runtime_error{what_arg} { } - virtual ~database_not_found() = default; }; class database { public: - /** - * \brief Construct an Engine Prime database, specifying the path to the - * Engine library directory - */ - explicit database(const std::string &dir_path); - - /** - * \brief Copying is disallowed - */ - database(const database &db); - - /** - * \brief Destructor - */ + /// Constructs an Engine Prime database, given the path to the Engine + /// library directory + explicit database(const std::string& directory); + + /// Copy constructor + database(const database& db); + + /// Destructor ~database(); - /** - * \brief Copy assignment is disallowed - */ - database &operator=(const database &db); + /// Copy assignment operator + database& operator=(const database& db); - /** - * \brief Returns a `bool` indicating whether the database version is - * supported by this version of libdjinterop or not - */ - bool is_supported() const; + /// Returns the crate with the given ID + /// + /// If no such crate exists in the database, then boost::none is returned. + boost::optional<crate> crate_by_id(int64_t id) const; - /** - * \brief Verify the schema of an Engine Prime database, throwing an - * exception if there is any kind of inconsistency - */ - void verify() const; + /// Returns all crates contained in the database + std::vector<crate> crates() const; + + /// Returns all crates with the given name + std::vector<crate> crates_by_name(const std::string& name) const; + + /// Creates a new crate with the given name + /// + /// The created crate has no parent. + crate create_crate(const std::string& name) const; + + /// Creates a new track associated to a given music file + /// + /// The music file is given by its relative path from the Engine library + /// directory. The created track is not contained in any crates. + track create_track(const std::string& relative_path) const; + + /// Returns the path to the Engine library directory of the database + std::string directory() const; - /** - * \brief Get the directory path on which this database is based - */ - std::string directory_path() const; + /// Returns true iff the database version is supported by this version of + /// `libdjinterop` or not + bool is_supported() const; - /** - * \brief Get the path to the music database, i.e. m.db - */ + /// Returns the path to the music database, i.e. m.db std::string music_db_path() const; - /** - * \brief Get the path to the performance data database, i.e. p.db - */ - std::string performance_db_path() const; + /// Returns the path to the performance data database, i.e. p.db + std::string perfdata_db_path() const; - /** - * \brief Get the UUID of this database - */ + /// Returns the UUID of the database std::string uuid() const; - /** - * \brief Get the schema version of this database - */ + /// Verifies the schema of an Engine Prime database and throws an exception + /// if there is any kind of inconsistency + void verify() const; + + /// Returns the schema version of the database schema_version version() const; + /// Removes a crate from the database + /// + /// All handles to that crate become invalid. + void remove_crate(crate cr) const; + + /// Removes a track from the database + /// + /// All handles to that track become invalid. + void remove_track(track tr) const; + + /// Returns all root crates contained in the database + /// + /// A root crate is a crate that has no parent. + std::vector<crate> root_crates() const; + + /// Returns the track with the given id + /// + /// If no such track exists in the database, then boost::none is returned. + boost::optional<track> track_by_id(int64_t id) const; + + /// Returns all tracks whose `relative_path` attribute in the database + /// matches the given string + std::vector<track> tracks_by_relative_path( + const std::string& relative_path) const; + + /// Returns all tracks contained in the database + std::vector<track> tracks() const; + private: - sqlite::database &music_db(); - sqlite::database &performance_db(); + std::shared_ptr<database_impl> pimpl_; - struct impl; - std::shared_ptr<impl> pimpl_; + friend class crate; + friend class track; }; -/** - * \brief Create a new, empty database in a given directory and at a specified - * schema version - */ -database create_database( - const std::string &dir_path, const schema_version &version); +/// Creates a new, empty database in a given directory and using a specified +/// schema version +database make_database( + const std::string& dir_path, + const schema_version& default_version = version_latest); } // namespace enginelibrary } // namespace djinterop diff --git a/include/djinterop/enginelibrary/pad_colour.hpp b/include/djinterop/enginelibrary/pad_color.hpp index 539cfb2..72a8657 100644 --- a/include/djinterop/enginelibrary/pad_colour.hpp +++ b/include/djinterop/enginelibrary/pad_color.hpp @@ -19,83 +19,83 @@ #error This library needs at least a C++11 compliant compiler #endif -#ifndef DJINTEROP_ENGINELIBRARY_PAD_COLOUR_HPP -#define DJINTEROP_ENGINELIBRARY_PAD_COLOUR_HPP +#ifndef DJINTEROP_ENGINELIBRARY_PAD_COLOR_HPP +#define DJINTEROP_ENGINELIBRARY_PAD_COLOR_HPP namespace djinterop { namespace enginelibrary { /** - * The `pad_colour` struct holds information about the colour that a given + * The `pad_color` struct holds information about the color that a given * hot cue / loop / etc. pad on the Denon SC5000 prime deck may be lit up as. * * Note that the alpha channel is typically not used, and is usually set to * full brightness. */ -struct pad_colour +struct pad_color { /** - * \brief Construct a `pad_colour` with a default black colour + * \brief Construct a `pad_color` with a default black color */ - constexpr pad_colour() : r{0x00}, g{0x00}, b{0x00}, a{0x00} {} + constexpr pad_color() : r{0x00}, g{0x00}, b{0x00}, a{0x00} {} /** - * \brief Construct a `pad_colour` from RGBA values + * \brief Construct a `pad_color` from RGBA values * - * Values for each colour range from 0 (full darkness), to 255 (full + * Values for each color range from 0 (full darkness), to 255 (full * brightness). */ - constexpr pad_colour( + constexpr pad_color( uint_least8_t r, uint_least8_t g, uint_least8_t b, uint_least8_t a) : r{r}, g{g}, b{b}, a{a} { } /** - * \brief The red component of this pad colour, from 0 to 255 + * \brief The red component of this pad color, from 0 to 255 */ uint_least8_t r; /** - * \brief The green component of this pad colour, from 0 to 255 + * \brief The green component of this pad color, from 0 to 255 */ uint_least8_t g; /** - * \brief The blue component of this pad colour, from 0 to 255 + * \brief The blue component of this pad color, from 0 to 255 */ uint_least8_t b; /** - * \brief The alpha component of this pad colour, from 0 to 255 + * \brief The alpha component of this pad color, from 0 to 255 * - * For most pad colours, this is usually set to full opaqueness, 255. + * For most pad colors, this is usually set to full opaqueness, 255. */ uint_least8_t a; }; -inline bool operator==(const pad_colour &x, const pad_colour &y) +inline bool operator==(const pad_color &x, const pad_color &y) { return x.r == y.r && x.g == y.g && x.b == y.b && x.a == y.a; } -namespace standard_pad_colours +namespace standard_pad_colors { -constexpr pad_colour pad_1{0xEA, 0xC5, 0x32, 0xFF}; -constexpr pad_colour pad_2{0xEA, 0x8F, 0x32, 0xFF}; -constexpr pad_colour pad_3{0xB8, 0x55, 0xBF, 0xFF}; -constexpr pad_colour pad_4{0xBA, 0x2A, 0x41, 0xFF}; -constexpr pad_colour pad_5{0x86, 0xC6, 0x4B, 0xFF}; -constexpr pad_colour pad_6{0x20, 0xC6, 0x7C, 0xFF}; -constexpr pad_colour pad_7{0x00, 0xA8, 0xB1, 0xFF}; -constexpr pad_colour pad_8{0x15, 0x8E, 0xE2, 0xFF}; - -const pad_colour pads[] = {pad_1, pad_2, pad_3, pad_4, - pad_5, pad_6, pad_7, pad_8}; -} // namespace standard_pad_colours +constexpr pad_color pad_1{0xEA, 0xC5, 0x32, 0xFF}; +constexpr pad_color pad_2{0xEA, 0x8F, 0x32, 0xFF}; +constexpr pad_color pad_3{0xB8, 0x55, 0xBF, 0xFF}; +constexpr pad_color pad_4{0xBA, 0x2A, 0x41, 0xFF}; +constexpr pad_color pad_5{0x86, 0xC6, 0x4B, 0xFF}; +constexpr pad_color pad_6{0x20, 0xC6, 0x7C, 0xFF}; +constexpr pad_color pad_7{0x00, 0xA8, 0xB1, 0xFF}; +constexpr pad_color pad_8{0x15, 0x8E, 0xE2, 0xFF}; + +const pad_color pads[] = {pad_1, pad_2, pad_3, pad_4, + pad_5, pad_6, pad_7, pad_8}; +} // namespace standard_pad_colors } // namespace enginelibrary } // namespace djinterop -#endif // DJINTEROP_ENGINELIBRARY_PAD_COLOUR_HPP +#endif // DJINTEROP_ENGINELIBRARY_PAD_COLOR_HPP diff --git a/include |