note: land the programmed capture-signal model — division ladder, tempo resolution, anchored offsets, one record and one resolver
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
// note_program — the programmed capture signal: one note length, one velocity, two anchored
|
||||
// offsets, and the one resolver a preview and a bake must share.
|
||||
//
|
||||
// Resolved times are rate-free SECONDS relative to note-on; the caller converts against the
|
||||
// live sample rate.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/instrument/note/musical_division.h"
|
||||
#include "core/instrument/note/tempo.h"
|
||||
|
||||
namespace reasampler::instrument::note {
|
||||
|
||||
class Velocity {
|
||||
public:
|
||||
static constexpr int kMin = 1; // 0 is note-off in MIDI; a programmed note must sound
|
||||
static constexpr int kMax = 127;
|
||||
|
||||
Velocity() = default;
|
||||
static Velocity of(int value); // clamped into [kMin, kMax]
|
||||
|
||||
std::uint8_t value() const { return value_; }
|
||||
|
||||
private:
|
||||
std::uint8_t value_ = 100;
|
||||
};
|
||||
|
||||
bool operator==(Velocity a, Velocity b);
|
||||
|
||||
enum class Denomination : std::uint8_t { Milliseconds, Beats };
|
||||
|
||||
// One magnitude, in the denomination it was entered in; the other view is derived on demand
|
||||
// and never stored. Which one was entered is itself the intent: a beats offset must follow a
|
||||
// tempo change and a ms offset must hold still, and only a stored denomination says which.
|
||||
struct OffsetAmount {
|
||||
double magnitude = 0.0;
|
||||
Denomination denomination = Denomination::Milliseconds;
|
||||
};
|
||||
|
||||
bool operator==(OffsetAmount a, OffsetAmount b);
|
||||
bool operator!=(OffsetAmount a, OffsetAmount b);
|
||||
|
||||
OffsetAmount offsetFromMs(double ms);
|
||||
OffsetAmount offsetFromBeats(double beats);
|
||||
|
||||
double offsetMs(OffsetAmount amount, Tempo tempo);
|
||||
double offsetBeats(OffsetAmount amount, Tempo tempo);
|
||||
double offsetSeconds(OffsetAmount amount, Tempo tempo);
|
||||
|
||||
// The unit toggle: the same instant restated in the other denomination.
|
||||
OffsetAmount redenominate(OffsetAmount amount, Denomination to, Tempo tempo);
|
||||
|
||||
// Two types rather than one carrying an anchor field: the anchor is then unswappable at
|
||||
// compile time. Sign is uniform — positive is later in time — so a capture that opens before
|
||||
// the note is a negative start offset, and a negative end offset truncates before release.
|
||||
class StartOffset {
|
||||
public:
|
||||
StartOffset() = default;
|
||||
explicit StartOffset(OffsetAmount amount) : amount_(amount) {}
|
||||
OffsetAmount amount() const { return amount_; }
|
||||
|
||||
private:
|
||||
OffsetAmount amount_{};
|
||||
};
|
||||
|
||||
class EndOffset {
|
||||
public:
|
||||
EndOffset() = default;
|
||||
explicit EndOffset(OffsetAmount amount) : amount_(amount) {}
|
||||
OffsetAmount amount() const { return amount_; }
|
||||
|
||||
private:
|
||||
OffsetAmount amount_{};
|
||||
};
|
||||
|
||||
struct NoteProgram {
|
||||
Division length{};
|
||||
StartOffset start{};
|
||||
EndOffset end{};
|
||||
Velocity velocity{};
|
||||
};
|
||||
|
||||
bool operator==(const NoteProgram& a, const NoteProgram& b);
|
||||
bool operator!=(const NoteProgram& a, const NoteProgram& b);
|
||||
|
||||
struct ResolvedNote {
|
||||
double noteOffSeconds = 0.0; // == the note's sounding length, note-on being 0
|
||||
double captureStartSeconds = 0.0; // negative when the capture opens before the note
|
||||
double captureEndSeconds = 0.0;
|
||||
std::uint8_t velocity = 1;
|
||||
|
||||
double captureLengthSeconds() const { return captureEndSeconds - captureStartSeconds; }
|
||||
};
|
||||
|
||||
ResolvedNote resolveNote(const NoteProgram& program, Tempo tempo);
|
||||
|
||||
} // namespace reasampler::instrument::note
|
||||
Reference in New Issue
Block a user