141 lines
5.0 KiB
C++
141 lines
5.0 KiB
C++
#pragma once
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <list>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <array>
|
|
#include <QString>
|
|
|
|
namespace Xybrid::Data {
|
|
class Project;
|
|
struct TimeSignature {
|
|
int beatsPerMeasure = 4;
|
|
int rowsPerBeat = 4;
|
|
int ticksPerRow = 6;
|
|
|
|
TimeSignature() = default;
|
|
TimeSignature(int b, int r, int t) : beatsPerMeasure(b), rowsPerBeat(r), ticksPerRow(t) {}
|
|
constexpr int rowsPerMeasure() const { return beatsPerMeasure * rowsPerBeat; }
|
|
|
|
constexpr bool operator==(const TimeSignature& o) const { return beatsPerMeasure == o.beatsPerMeasure && rowsPerBeat == o.rowsPerBeat && ticksPerRow == o.ticksPerRow; }
|
|
constexpr bool operator!=(const TimeSignature& o) const { return !(*this == o); }
|
|
};
|
|
class Pattern : public std::enable_shared_from_this<Pattern> {
|
|
public:
|
|
class Row { // with std::unique_ptr<std::vector>, each Row is 12 bytes inline on 64-bit (8 bytes on 32)
|
|
public:
|
|
static std::array<unsigned char, 2> fallbackParam;
|
|
|
|
int16_t port = -1;
|
|
int16_t note = -1;
|
|
std::unique_ptr<std::vector<std::array<unsigned char, 2>>> params = nullptr; // empty by default
|
|
|
|
Row() = default;
|
|
Row(const Row&) noexcept;
|
|
Row(Row&&) = default;
|
|
Row(int16_t p, int16_t n) : Row() {
|
|
port = p;
|
|
note = n;
|
|
}
|
|
|
|
Row& operator=(const Row&) noexcept;
|
|
|
|
bool isEmpty() const { return numParams() == 0 && port == -1 && note == -1; }
|
|
|
|
size_t numParams() const {
|
|
if (!this->params) return 0;
|
|
return this->params->size();
|
|
}
|
|
|
|
std::array<unsigned char, 2>& param(size_t index) {
|
|
if (!this->params || this->params->size() <= index) return fallbackParam;
|
|
return this->params->at(index);
|
|
}
|
|
|
|
void addParam(char c = '.', unsigned char v = 0) {
|
|
std::array<unsigned char, 2> p {static_cast<unsigned char>(c), v};
|
|
if (!this->params) this->params.reset(new std::vector<std::array<unsigned char, 2>>({p}));
|
|
else params->push_back(p);
|
|
}
|
|
|
|
void insertParam(size_t index, char c = ' ', unsigned char v = 0) {
|
|
std::array<unsigned char, 2> p {static_cast<unsigned char>(c), v};
|
|
if (!this->params) this->params.reset(new std::vector<std::array<unsigned char, 2>>({p}));
|
|
else {
|
|
if (index > this->params->size()) index = this->params->size();
|
|
params->insert(this->params->begin() + static_cast<ptrdiff_t>(index), p);
|
|
}
|
|
}
|
|
|
|
/// Sets parameter at given index, adding struts before if necessary
|
|
void setParam(size_t index, char c = ' ', unsigned char v = 0) {
|
|
if (!this->params) this->params.reset(new std::vector<std::array<unsigned char, 2>>());
|
|
if (params->size() <= index) params->resize(index+1, {' ', 0});
|
|
(*params)[index] = {static_cast<unsigned char>(c), v};
|
|
}
|
|
|
|
void removeParam(size_t index) {
|
|
if (!this->params || this->params->size() <= index) return; // invalid index
|
|
if (this->params->size() == 1) { // deallocate vector entirely if empty
|
|
this->params.reset(nullptr);
|
|
return;
|
|
}
|
|
this->params->erase(this->params->begin() + static_cast<ptrdiff_t>(index));
|
|
}
|
|
|
|
};
|
|
|
|
class Channel {
|
|
public:
|
|
QString name;
|
|
std::vector<Row> rows;
|
|
|
|
Channel() = default;
|
|
Channel(const Channel&) = default;
|
|
Channel(int numRows, QString name = "");
|
|
};
|
|
private:
|
|
static Row fallbackRow;
|
|
static Channel fallbackChannel;
|
|
|
|
public:
|
|
// raw pointer is fine for now, since a project will never be destroyed without explicitly orphaning patterns
|
|
// (and probably deleting them since basically the only reason one would be kept alive is if it's open in the pattern editor,
|
|
// which would then immediately update with a pattern from opening a new project, or the window would close)
|
|
Project* project;
|
|
size_t index; // index in project's pattern list
|
|
|
|
QString name;
|
|
|
|
int rows = 64;
|
|
double tempo = 0; // don't set playback tempo
|
|
TimeSignature time;
|
|
|
|
int fold = 0;
|
|
|
|
std::vector<Channel> channels;
|
|
|
|
Pattern();
|
|
Pattern(const Pattern&) = default;
|
|
Pattern(int rows, int channels = 0);
|
|
|
|
void setLength(int rows);
|
|
void addChannel(int at = -1);
|
|
void deleteChannel(int at);
|
|
|
|
size_t numChannels() const { return channels.size(); }
|
|
|
|
bool valid() const;
|
|
bool validFor(const Project*) const;
|
|
bool validFor(const std::shared_ptr<Project>&) const;
|
|
|
|
Channel& channel(int channel);
|
|
Row& rowAt(int channel, int row);
|
|
};
|
|
}
|
|
|