Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading