Skip to content

Commit

Permalink
Improve the unit tests for patternal
Browse files Browse the repository at this point in the history
Add test_quantification_date

Add this-> to make the code easier to read

WIP fix errors in test

WIP

Improve the unit test a lot

Improve the unit test

Add some doc
  • Loading branch information
aalex committed Dec 19, 2024
1 parent 5c059c1 commit cc63e6d
Show file tree
Hide file tree
Showing 6 changed files with 325 additions and 30 deletions.
1 change: 1 addition & 0 deletions cmake/avendish.tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ if(BUILD_TESTING)

avnd_add_catch_test(test_gain tests/objects/gain.cpp)
avnd_add_catch_test(test_patternal tests/objects/patternal.cpp)
avnd_add_catch_test(test_quantification_date tests/test_quantification_date.cpp)
endif()

27 changes: 24 additions & 3 deletions examples/Advanced/Patternal/Patternal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@
namespace patternal
{

/**
* A sequencer pattern.
* Contains its note and its velocity on each beat.
*/
struct Pattern
{
/**
* A note.
*/
int note;
/**
* Its velocity on each beat.
*/
boost::container::small_vector<uint8_t, 32> pattern;
};

Expand All @@ -30,20 +40,27 @@ struct Processor
"]\n"
"for a very simple drum rythm on kick and snare.")
halp_meta(uuid, "6e89b53a-1645-4a9c-a26e-e6c7870a902c")

struct
{
struct
{
/**
* A list of patterns to play.
* Each note has a velocity that changes for each beat.
*/
std::vector<Pattern> value;
} patterns;
} inputs;

/**
* Its MIDI output.
*/
struct
{
halp::midi_bus<"Out"> midi;
} outputs;

using tick = halp::tick_musical;
void operator()(halp::tick_musical tk)
{
// Find out where we are in the bar
Expand All @@ -56,16 +73,20 @@ struct Processor
auto quants = tk.get_quantification_date(4. / pat.size());
for(auto [pos, q] : quants)
{
// FIXME: The position returned by get_quantification_date is a negative timestamp.
if(pos < tk.frames)
{
auto qq = std::abs(q % std::ssize(pat));
if(uint8_t vel = pat[qq]; vel > 0)
if(uint8_t velocity = pat[qq]; velocity > 0)
{
halp::midi_msg m;
m.bytes = {144, (uint8_t)note, vel};
// Note on:
m.bytes = {144, (uint8_t)note, velocity};
m.timestamp = pos;
outputs.midi.midi_messages.push_back(m);

// FIXME: The note off should not be output right away.
// Note off:
m.bytes = {128, (uint8_t)note, 0};
m.timestamp = pos;
outputs.midi.midi_messages.push_back(m);
Expand Down
64 changes: 56 additions & 8 deletions include/halp/audio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,35 +201,83 @@ struct tick
int frames{};
};

/**
* Contains the info about the current audio buffer and the bar and quarter notes it's
* associated with, so that the musical audio rendering utilities can do their job of
* playing back the musical notes, etc.
* They need to know when we are in the score.
*
* The quarter notes have their index starting at 0.
* The position of the bars are in quarters. (in 4/4, the first bar has position 0.0 and the
* second one has a position of 4.0)
*
* This struct is usually created and destroyed thousands of times per second.
*/
struct tick_musical
{
/**
* How many frames in the buffer.
*/
int frames{};

/**
* The tempo in BPM.
*/
double tempo = 120.;
/**
* Time signature. Example: 4/4
*/
struct
{
int num;
int denom;
} signature;

/**
* The total number of samples since the beginning of the playback of the score.
*/
int64_t position_in_frames{};

/**
* Playback time since the beginning of the score.
*/
double position_in_nanoseconds{};

// Quarter note of the first sample in the buffer
/**
* Quarter note of the first sample in this buffer
* This is a double.
*
* The first quarter note in a score has index 0.
*
* For example:
* - 3.95 would be slightly before the 2nd bar.
* - 4.05 would be slightly after the beginning of the 2nd bar.
*/
quarter_note start_position_in_quarters{};

// Quarter note of the first sample in the next buffer
// (or one past the last sample of this buffer, e.g. a [closed; open) interval like C++ begin / end)
/**
* Quarter note of the first sample in the next buffer
* (or one past the last sample of this buffer, e.g. a [closed; open) interval like C++ begin / end)
*/
quarter_note end_position_in_quarters{};

// Position of the last signature change in quarter notes (at the start of the tick)
/**
* Position of the last signature change in quarter notes (at the start of the tick)
*
* For example: 0.0 if the signature never changes in the score.
* If we change the time signature at some, we'll give it the index (in quarter) of the
* bar when it last changed.
*/
quarter_note last_signature_change{};

// Position of the last bar relative to start in quarter notes
/**
* Position of the last bar relative to start in quarter notes
*/
quarter_note bar_at_start{};

// Position of the last bar relative to end in quarter notes
/**
* Position of the last bar relative to end in quarter notes
*/
quarter_note bar_at_end{};

// If the division falls in the current tick, returns the corresponding frames
Expand All @@ -240,11 +288,11 @@ struct tick_musical
[[nodiscard]] quantification_frames get_quantification_date(double div) const noexcept
{
quantification_frames frames;
double start_in_bar = start_position_in_quarters - bar_at_start;
double start_in_bar = this->start_position_in_quarters - bar_at_start;
double end_in_bar = end_position_in_quarters - bar_at_start;

auto pos_to_frame = [this](double in_bar) {
double start = start_position_in_quarters;
double start = this->start_position_in_quarters;
double musical_pos = in_bar + bar_at_start;
double end = end_position_in_quarters;

Expand Down
22 changes: 22 additions & 0 deletions include/halp/midi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

#include <algorithm>
#include <string_view>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <sstream>

HALP_MODULE_EXPORT
namespace halp
Expand All @@ -17,13 +20,32 @@ struct midi_msg
{
boost::container::small_vector<uint8_t, 15> bytes;
int64_t timestamp{};

// Equality operator.
bool operator==(const midi_msg&) const = default;
// auto operator<=>(const midi_msg&) const = default;
};

std::ostream& operator<<(std::ostream &o, const midi_msg& message)
{
return o << "midi_msg{:bytes=" << fmt::format("{}", message.bytes) <<", :timestamp=" << message.timestamp << "}" << std::endl;
}

struct midi_note_msg
{
uint8_t bytes[8];
int64_t timestamp{};

// Equality operator.
bool operator==(const midi_note_msg&) const = default;
//auto operator<=>(const midi_note_msg&) const = default;
};

std::ostream& operator<<(std::ostream &o, const midi_note_msg& message)
{
return o << "midi_note_msg{:bytes=" << fmt::format("{}", message.bytes) <<", :timestamp=" << message.timestamp << "}" << std::endl;
}

template <static_string lit, typename MessageType = midi_msg>
struct midi_bus
{
Expand Down
Loading

0 comments on commit cc63e6d

Please sign in to comment.