Skip to content

Commit

Permalink
improve documentation and add example code (part 1)
Browse files Browse the repository at this point in the history
  • Loading branch information
fdetro committed Nov 15, 2024
1 parent 05ce9b1 commit e6b99bd
Show file tree
Hide file tree
Showing 33 changed files with 2,726 additions and 7 deletions.
28 changes: 27 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ endif()

option( NIMIDI2_TREAT_WARNINGS_AS_ERRORS "Treat compile warnings as errors" OFF )
option( NIMIDI2_UNITY_BUILDS "Build ni-midi2 with unity builds" ON )
option( NIMIDI2_TESTS "Build ni-midi2 Tests" ${IS_NIMIDI2} )
option( NIMIDI2_TESTS "Build ni-midi2 tests" ${IS_NIMIDI2} )
option( NIMIDI2_EXAMPLES "Build ni-midi2 examples" ${IS_NIMIDI2} )

option( NIMIDI2_CUSTOM_SYSEX_DATA_ALLOCATOR "Build with custom sysex data allocator" OFF )
option( NIMIDI2_PMR_SYSEX_DATA "Build with sysex data use pmr" OFF )
Expand Down Expand Up @@ -127,3 +128,28 @@ if( NIMIDI2_TESTS )

nimidi2_add_test(ni-midi2-tests GTEST)
endif(NIMIDI2_TESTS)

if( NIMIDI2_EXAMPLES )
set(ExampleSources
docs/docs.cpp
docs/channel_voice_message.examples.cpp
docs/data_message.examples.cpp
docs/extended_data_message.examples.cpp
docs/flex_data_message.examples.cpp
docs/midi1_byte_stream.examples.cpp
docs/midi1_channel_voice_message.examples.cpp
docs/midi2_channel_voice_message.examples.cpp
docs/stream_message.examples.cpp
docs/sysex_collector.examples.cpp
docs/sysex.examples.cpp
docs/system_message.examples.cpp
docs/types.examples.cpp
docs/universal_sysex.examples.cpp
docs/utility_message.examples.cpp
)

source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${ExampleSources})

add_executable(ni-midi2-examples ${ExampleSources})
target_link_libraries(ni-midi2-examples PRIVATE ni::midi2)
endif(NIMIDI2_EXAMPLES)
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Mathematical operators allow to do integer / fixed point math on pitches and con
auto from32bit = controller_value{ uint32_t{ 0x45883312 } };
auto fromFloat = pitch_7_25 { 66.1f };

For more information see [Common Types and Scaling Helpers](docs/types.md).

Conrete instances of packets or messages are created using factory functions.

Incoming packets and messages are inspected using data views.
Expand All @@ -75,8 +77,8 @@ Examples for UMP factory functions:
midi1_channel_voice_message make_midi1_pitch_bend_message(group_t, channel_t, pitch_bend);
...

midi2_channel_voice_message make_midi2_note_on_message(group_t, status_t, note_nr_t, velocity);
midi2_channel_voice_message make_midi2_note_on_message(group_t, status_t, note_nr_t, velocity, pitch_7_9);
midi2_channel_voice_message make_midi2_note_on_message(group_t, channel_t, note_nr_t, velocity);
midi2_channel_voice_message make_midi2_note_on_message(group_t, channel_t, note_nr_t, velocity, pitch_7_9);
midi2_channel_voice_message make_registered_controller_message(
group_t, channel_t, uint7_t bank, uint7_t index, controller_value);
...
Expand Down Expand Up @@ -125,9 +127,9 @@ Alternatively one can use `std::optional` to check and cast in a single statemen
if (auto m = as_system_message_view(packet))
{
// access message data
if (m.status() == system_status::song_position)
if (m->status() == system_status::song_position)
{
auto pos = m.get_song_position();
auto pos = m->get_song_position();
}
}

Expand Down Expand Up @@ -178,6 +180,8 @@ The library provides a `midi1_byte_stream_parser` class and also free helper fun

The `midi1_byte_stream_parser` can be configured to automatically parse and collect Sysex7 messages.

For more information see [midi1_byte_stream.md](docs/midi1_byte_stream.md).

### Sysex collectors

The `sysex7_collector` class allows to easily collect System Exclusive messages (like MIDI-CI 1.2).
Expand All @@ -196,7 +200,7 @@ The `sysex7_collector` class allows to easily collect System Exclusive messages
`sysex8_collector` does the same for System Exclusive 8 messages.

sysex8_collector c {
[](const sysex8 &s)
[](const sysex8 &s, uint8_t stream_id)
{
// do something with message
...
Expand All @@ -206,6 +210,7 @@ The `sysex7_collector` class allows to easily collect System Exclusive messages
if (is_sysex8_packet(p))
c.feed(p);

For more information see [sysex_collector.md](docs/sysex_collector.md).

### Jitter Reduction Timestamps

Expand All @@ -220,6 +225,25 @@ There are experimental implementations for a Jitter Reduction Timestamps clock g

Please be aware that these classes only demonstrate the general concept of Jitter Reduction Timestamps, they are not intended as a ready-to-use production solution.

## Detail documentation and example files

There is a growing number of code examples and more detailed documentation available in the [docs](docs) folder. Enable the `cmake` option `NIMIDI2_EXAMPLES` to build the example code.

* Universal MIDI Packet [documentation](docs/universal_packet.md)
* Common Types and Scaling Helpers [documentation](docs/types.md) and [examples](docs/types.examples.cpp)
* Protocol Agnostic Channel Voice Message helper [documentation](docs/channel_voice_message.md) and [examples](docs/channel_voice_message.examples.cpp)
* MIDI 1 Channel Voice Message [documentation](docs/midi1_channel_voice_message.md) and [examples](docs/midi1_channel_voice_message.examples.cpp)
* MIDI 2 Channel Voice Message [documentation](docs/midi2_channel_voice_message.md) and [examples](docs/midi2_channel_voice_message.examples.cpp)
* System Message [documentation](docs/system_message.md) and [examples](docs/system_message.examples.cpp)
* Data Message [documentation](docs/data_message.md) and [examples](docs/data_message.examples.cpp)
* Extended Data Message [documentation](docs/extended_data_message.md) and [examples](docs/extended_data_message.examples.cpp)
* Flex Data Message [WIP documentation](docs/flex_data_message.md)
* Stream Message [WIP documentation](docs/stream_message.md)
* Utility Message [documentation](docs/utility_message.md)
* MIDI 1 Byte Stream Helper [WIP documentation](docs/midi1_byte_stream.md) and [examples](docs/midi1_byte_stream.examples.cpp)

Until documentation is completed you may also find the [unit test code](tests) helpful.

## Building and Testing

The library uses `cmake` as its build system. Simply call `cmake` on the top level source folder to generate a build recipe for your preferred compiler and IDE / `make`.
Expand All @@ -229,6 +253,7 @@ There are some `cmake options` that allow customization of what is build and how
option( NIMIDI2_TREAT_WARNINGS_AS_ERRORS "Treat compile warnings as errors" OFF )
option( NIMIDI2_UNITY_BUILDS "Build ni-midi2 with unity builds" ON )
option( NIMIDI2_TESTS "Build ni-midi2 Tests" ${IS_NIMIDI2} )
option( NIMIDI2_EXAMPLES "Build ni-midi2 examples" ${IS_NIMIDI2} )

If you do not need to build unit tests, specify `-DNIMIDI2_TESTS=OFF` on the `cmake` command line. This is the default if this project is included via `add_subdirectory` into your project.

Expand Down
201 changes: 201 additions & 0 deletions docs/channel_voice_message.examples.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#include <midi/channel_voice_message.h>

#include <cassert>

void note_on_examples()
{
using namespace midi;

const note_nr_t note_nr{ 99 };
const velocity vel{ 0.9876 };
const pitch_7_9 default_pitch{ note_nr };
const pitch_7_9 custom_pitch{ 99.02 };

auto m1 = make_midi1_note_on_message(0, 0, note_nr, vel);
auto m2 = make_midi2_note_on_message(0, 0, note_nr, vel);
auto m3 = make_midi2_note_on_message(0, 0, note_nr, vel, custom_pitch);
auto m4 = make_midi2_note_on_message(0, 0, note_nr, vel, uint8_t{ 42 }, uint16_t{ 1234 });

assert(is_note_on_message(m1));
assert(is_note_on_message(m2));
assert(is_note_on_message(m3));
assert(is_note_on_message(m4));

assert(get_note_nr(m1) == note_nr);
assert(get_note_nr(m2) == note_nr);
assert(get_note_nr(m3) == note_nr);
assert(get_note_nr(m4) == note_nr);
assert(get_note_velocity(m1) == velocity{ vel.as_uint7() });
assert(get_note_velocity(m2) == vel);
assert(get_note_velocity(m3) == vel);
assert(get_note_velocity(m4) == vel);
assert(get_note_pitch(m1) == default_pitch);
assert(get_note_pitch(m2) == default_pitch);
assert(get_note_pitch(m3) == custom_pitch);
assert(get_note_pitch(m4) == default_pitch);
}

void note_off_examples()
{
using namespace midi;

const note_nr_t note_nr{ 71 };
const velocity vel{ 0.1 };

auto m1 = make_midi1_note_off_message(0, 0, note_nr, vel);
auto m2 = make_midi2_note_off_message(0, 0, note_nr, vel);
auto m3 = make_midi1_note_on_message(0, 0, note_nr, velocity{ uint7_t{ 0 } });

assert(is_note_off_message(m1));
assert(is_note_off_message(m2));
assert(is_note_off_message(m3));
assert(get_note_nr(m1) == note_nr);
assert(get_note_nr(m2) == note_nr);
assert(get_note_nr(m3) == note_nr);
assert(get_note_velocity(m1) == velocity{ vel.as_uint7() });
assert(get_note_velocity(m2) == vel);
assert(get_note_velocity(m3) == velocity{ uint7_t{ 64 } });
}

void poly_pressure_examples()
{
using namespace midi;

note_nr_t note_nr{ 34 };
controller_value pressure{ 0.75 };

auto m1 = make_midi1_poly_pressure_message(0, 0, note_nr, pressure);
auto m2 = make_midi2_poly_pressure_message(0, 0, note_nr, pressure);

assert(is_poly_pressure_message(m1));
assert(is_poly_pressure_message(m2));

assert(get_note_nr(m1) == note_nr);
assert(get_note_nr(m2) == note_nr);

assert(get_poly_pressure_value(m2) == pressure);
assert(get_poly_pressure_value(m1) == controller_value{ pressure.as_uint7() });
}

void control_change_examples()
{
using namespace midi;

controller_t ctrl{ control_change::brightness };
controller_value val{ uint32_t{ 0x41221892 } };

auto m1 = make_midi1_control_change_message(0, 0, ctrl, val);
auto m2 = make_midi2_control_change_message(0, 0, ctrl, val);

assert(is_control_change_message(m1));
assert(is_control_change_message(m2));

assert(get_controller_nr(m1) == ctrl);
assert(get_controller_nr(m2) == ctrl);

assert(get_controller_value(m2) == val);
assert(get_controller_value(m1) == controller_value{ val.as_uint7() });
}

void program_change_examples()
{
using namespace midi;

program_t program{ 7 };

// TODO: demonstrate MIDI2 bank
auto m1 = make_midi1_program_change_message(0, 0, program);
auto m2 = make_midi2_program_change_message(0, 0, program);

assert(is_program_change_message(m1));
assert(is_program_change_message(m2));

assert(get_program_value(m1) == program);
assert(get_program_value(m2) == program);
}

void channel_pressure_examples()
{
using namespace midi;

controller_value pressure{ 0.75 };

auto m1 = make_midi1_channel_pressure_message(0, 0, pressure);
auto m2 = make_midi2_channel_pressure_message(0, 0, pressure);

assert(is_channel_pressure_message(m1));
assert(is_channel_pressure_message(m2));

assert(get_channel_pressure_value(m2) == pressure);
assert(get_channel_pressure_value(m1) == controller_value{ pressure.as_uint7() });
}

void channel_pitch_bend_examples()
{
using namespace midi;

pitch_bend pb{ -0.04 };

auto m1 = make_midi1_pitch_bend_message(0, 0, pb);
auto m2 = make_midi2_pitch_bend_message(0, 0, pb);

assert(is_channel_pitch_bend_message(m1));
assert(is_channel_pitch_bend_message(m2));

assert(get_channel_pitch_bend_value(m2) == pb);
assert(get_channel_pitch_bend_value(m1) == pitch_bend{ pb.as_uint14() });
}

void channel_voice_message_processor(const midi::universal_packet& p)
{
using namespace midi;

auto allocate_voice = [](note_nr_t, velocity, pitch_7_9) {};
auto release_voice = [](note_nr_t, velocity) {};
auto voice_pressure = [](note_nr_t, controller_value) {};
auto voice_controller = [](note_nr_t, uint8_t, controller_value) {};
auto voice_pitch_bend = [](note_nr_t, pitch_bend) {};
auto channel_controller = [](controller_t, controller_value) {};
auto channel_pitch_bend = [](pitch_bend) {};

if (is_note_on_message(p))
{
allocate_voice(get_note_nr(p), get_note_velocity(p), get_note_pitch(p));
}
else if (is_note_off_message(p))
{
release_voice(get_note_nr(p), get_note_velocity(p));
}
else if (is_poly_pressure_message(p))
{
voice_pressure(get_note_nr(p), get_poly_pressure_value(p));
}
else if (is_registered_per_note_controller_message(p))
{
voice_controller(get_note_nr(p), get_per_note_controller_index(p), get_controller_value(p));
}
else if (is_per_note_pitch_bend_message(p))
{
voice_pitch_bend(get_note_nr(p), get_per_note_pitch_bend_value(p));
}
else if (is_control_change_message(p))
{
channel_controller(get_controller_nr(p), get_controller_value(p));
}
else if (is_channel_pitch_bend_message(p))
{
channel_pitch_bend(get_channel_pitch_bend_value(p));
}
}

void run_channel_voice_message_examples()
{
note_on_examples();
note_off_examples();
poly_pressure_examples();
control_change_examples();
program_change_examples();
channel_pressure_examples();
channel_pitch_bend_examples();
channel_voice_message_processor(midi::universal_packet{ 0x20901234 });
}
Loading

0 comments on commit e6b99bd

Please sign in to comment.