diff --git a/README.md b/README.md index ea222f3..26c971c 100644 --- a/README.md +++ b/README.md @@ -237,8 +237,8 @@ There is a growing number of code examples and more detailed documentation avail * 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) +* Flex Data Message [documentation](docs/flex_data_message.md) and [examples](docs/flex_data_message.examples.cpp) +* Stream Message [documentation](docs/stream_message.md) and [examples](docs/stream_message.examples.cpp) * 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) diff --git a/docs/channel_voice_message.md b/docs/channel_voice_message.md index 30d35f9..ddd0e39 100644 --- a/docs/channel_voice_message.md +++ b/docs/channel_voice_message.md @@ -13,21 +13,25 @@ Code examples can be found in [`channel_voice_message.examples.cpp`](channel_voi Available filter functions are: - bool is_channel_voice_message_with_status(const universal_packet&, status_t); - - bool is_note_on_message(const universal_packet&); - bool is_note_off_message(const universal_packet&); - bool is_poly_pressure_message(const universal_packet&); - bool is_control_change_message(const universal_packet&); - bool is_program_change_message(const universal_packet&); - bool is_channel_pressure_message(const universal_packet&); - bool is_channel_pitch_bend_message(const universal_packet&); +```cpp +bool is_channel_voice_message_with_status(const universal_packet&, status_t); + +bool is_note_on_message(const universal_packet&); +bool is_note_off_message(const universal_packet&); +bool is_poly_pressure_message(const universal_packet&); +bool is_control_change_message(const universal_packet&); +bool is_program_change_message(const universal_packet&); +bool is_channel_pressure_message(const universal_packet&); +bool is_channel_pitch_bend_message(const universal_packet&); +``` Additionally, one may use - bool is_note_on_with_attribute(const universal_packet&, uint8_t); - bool is_note_off_with_attribute(const universal_packet&, uint8_t); - bool is_note_on_with_pitch_7_9(const universal_packet&); +```cpp +bool is_note_on_with_attribute(const universal_packet&, uint8_t); +bool is_note_off_with_attribute(const universal_packet&, uint8_t); +bool is_note_on_with_pitch_7_9(const universal_packet&); +``` to check for MIDI 2 note messages with attributes. @@ -35,16 +39,20 @@ to check for MIDI 2 note messages with attributes. The following functions are available to extract properties from Note messages: - note_nr_t get_note_nr(const universal_packet&); - pitch_7_9 get_note_pitch(const universal_packet&); - velocity get_note_velocity(const universal_packet&); +```cpp +note_nr_t get_note_nr(const universal_packet&); +pitch_7_9 get_note_pitch(const universal_packet&); +velocity get_note_velocity(const universal_packet&); +``` `get_note_nr` is applicable to _Poly Pressure_ and _Per-Note Controller_ messages, too. Additionally, one may use - uint8_t get_midi2_note_attribute(const universal_packet&); - uint16_t get_midi2_note_attribute_data(const universal_packet&); +```cpp +uint8_t get_midi2_note_attribute(const universal_packet&); +uint16_t get_midi2_note_attribute_data(const universal_packet&); +``` to retrieve Per Note Attribute type and data. @@ -52,8 +60,10 @@ to retrieve Per Note Attribute type and data. Use - controller_t get_controller_nr(const universal_packet&); - controller_value get_controller_value(const universal_packet&); +```cpp +controller_t get_controller_nr(const universal_packet&); +controller_value get_controller_value(const universal_packet&); +``` to extract _Control Change_ message properties. @@ -61,23 +71,29 @@ to extract _Control Change_ message properties. Use these functions to retrieve properties of specific messages: - controller_value get_poly_pressure_value(const universal_packet&); - uint7_t get_program_value(const universal_packet&); - controller_value get_channel_pressure_value(const universal_packet&); - pitch_bend get_channel_pitch_bend_value(const universal_packet&); +```cpp +controller_value get_poly_pressure_value(const universal_packet&); +uint7_t get_program_value(const universal_packet&); +controller_value get_channel_pressure_value(const universal_packet&); +pitch_bend get_channel_pitch_bend_value(const universal_packet&); +``` ## Conversion between Protocols One may use - std::optional - as_midi1_channel_voice_message(const midi2_channel_voice_message_view&); +```cpp +std::optional +as_midi1_channel_voice_message(const midi2_channel_voice_message_view&); +``` to convert a MIDI 2 Channel Voice Message to its MIDI 1 counterpart and - std::optional - as_midi2_channel_voice_message(const midi1_channel_voice_message_view&); +```cpp +std::optional +as_midi2_channel_voice_message(const midi1_channel_voice_message_view&); +``` to convert a MIDI 1 Channel Voice Message to its MIDI 2 counterpart. -**Note:** MIDI 1 <-> MIDI 2 translation rules do require some sequences of MIDI 1 messages to be translated to single MIDI 2 messages and vice versa. The above mentioned functions do not follow these rules. Actually they filter some _Control Change_ messages (mostly related to (N)RPNs) and cannot handle MIDI 2 _Program Change_ messages with `bank` data. \ No newline at end of file +**Note:** MIDI 1 <-> MIDI 2 translation rules do require some sequences of MIDI 1 messages to be translated to single MIDI 2 messages and vice versa. The above mentioned functions do not follow these rules. Actually they filter some _Control Change_ messages (mostly related to (N)RPNs) and cannot handle MIDI 2 _Program Change_ messages with `bank` data. diff --git a/docs/data_message.md b/docs/data_message.md index ebbae56..f558e02 100644 --- a/docs/data_message.md +++ b/docs/data_message.md @@ -6,63 +6,75 @@ Code examples can be found in [`data_message.examples.cpp`](data_message.example Data Messages are represented by a - struct data_message : universal_packet - { - explicit data_message(status_t); - }; +```cpp +struct data_message : universal_packet +{ + explicit data_message(status_t); +}; +``` Usually one will not use this class directly, but instead - struct sysex7_packet : data_message - { - sysex7_packet(status_t, group_t); +```cpp +struct sysex7_packet : data_message +{ + sysex7_packet(status_t, group_t); - packet_format format() const; + packet_format format() const; - uint8_t payload_byte(size_t b) const; - void set_payload_byte(size_t, uint8_t); + uint8_t payload_byte(size_t b) const; + void set_payload_byte(size_t, uint8_t); - size_t payload_size() const; - void set_payload_size(size_t); + size_t payload_size() const; + void set_payload_size(size_t); - void add_payload_byte(uint8_t); - }; + void add_payload_byte(uint8_t); +}; +``` A `sysex7_packet` can hold a payload of up to six bytes and provides APIs to add or read the payload bytes. Be aware that the payload shall only be 7 bit data. Instead of using sysex7_packet constructors one can create messages using factory functions: - sysex7_packet make_sysex7_complete_packet(group_t); - sysex7_packet make_sysex7_start_packet(group_t); - sysex7_packet make_sysex7_continue_packet(group_t); - sysex7_packet make_sysex7_end_packet(group_t); +```cpp +sysex7_packet make_sysex7_complete_packet(group_t); +sysex7_packet make_sysex7_start_packet(group_t); +sysex7_packet make_sysex7_continue_packet(group_t); +sysex7_packet make_sysex7_end_packet(group_t); +``` Filtering of Data Messages can be done checking `universal_packet::type()` against `packet_type::data` or use - bool is_data_message(const universal_packet&); - bool is_sysex7_packet(const universal_packet&); +```cpp +bool is_data_message(const universal_packet&); +bool is_sysex7_packet(const universal_packet&); +``` ### SysEx7 Packet View Once validated being a `sysex7` packet one can create a `sysex7_packet_view` for a `universal_packet`: - struct sysex7_packet_view - { - explicit sysex7_packet_view(const universal_packet&); +```cpp +struct sysex7_packet_view +{ + explicit sysex7_packet_view(const universal_packet&); - group_t group() const; - status_t status() const; - packet_format format() const; - size_t payload_size() const; - uint8_t payload_byte(size_t b) const; - }; + group_t group() const; + status_t status() const; + packet_format format() const; + size_t payload_size() const; + uint8_t payload_byte(size_t b) const; +}; +``` View members provide accessors to the properties of the message. The additional - std::optional as_sysex7_packet_view(const universal_packet&); +```cpp +std::optional as_sysex7_packet_view(const universal_packet&); +``` helper allows to combine packet type check and view creation in a single statement. diff --git a/docs/extended_data_message.md b/docs/extended_data_message.md index 08e1a37..e2b784b 100644 --- a/docs/extended_data_message.md +++ b/docs/extended_data_message.md @@ -6,30 +6,34 @@ Code examples can be found in [`extended_data_message.examples.cpp`](extended_da Extended Data Messages are represented by a - struct extended_data_message : universal_packet - { - explicit extended_data_message(status_t); - }; +```cpp +struct extended_data_message : universal_packet +{ + explicit extended_data_message(status_t); +}; +``` Usually one will not use this class directly, but instead - struct sysex8_packet : extended_data_message - { - sysex8_packet(status_t, uint8_t stream_id, group_t); +```cpp +struct sysex8_packet : extended_data_message +{ + sysex8_packet(status_t, uint8_t stream_id, group_t); - packet_format format() const; + packet_format format() const; - uint8_t stream_id() const; - void set_stream_id(uint8_t); + uint8_t stream_id() const; + void set_stream_id(uint8_t); - uint8_t payload_byte(size_t b) const; - void set_payload_byte(size_t, uint8_t); + uint8_t payload_byte(size_t b) const; + void set_payload_byte(size_t, uint8_t); - size_t payload_size() const; - void set_payload_size(size_t); + size_t payload_size() const; + void set_payload_size(size_t); - void add_payload_byte(uint8_t); - }; + void add_payload_byte(uint8_t); +}; +``` A `sysex8_packet` can hold a payload of up to 13 bytes and provides APIs to add or read the payload bytes. System Exclusive 8 packets payload is allowed to be 8 bit, different to traditional MIDI System Exclusive (7 bit). @@ -38,37 +42,45 @@ In MIDI 2 is allowed to have multiple parallel System Exclusive 8 streams runnin Instead of using sysex8_packet constructors one can create messages using factory functions: - sysex8_packet make_sysex8_complete_packet(uint8_t stream_id, group_t); - sysex8_packet make_sysex8_start_packet(uint8_t stream_id, group_t); - sysex8_packet make_sysex8_continue_packet(uint8_t stream_id, group_t); - sysex8_packet make_sysex8_end_packet(uint8_t stream_id, group_t); +```cpp +sysex8_packet make_sysex8_complete_packet(uint8_t stream_id, group_t); +sysex8_packet make_sysex8_start_packet(uint8_t stream_id, group_t); +sysex8_packet make_sysex8_continue_packet(uint8_t stream_id, group_t); +sysex8_packet make_sysex8_end_packet(uint8_t stream_id, group_t); +``` Filtering of Extended Data Messages can be done checking `universal_packet::type()` against `packet_type::extended_data` or use - bool is_extended_data_message(const universal_packet&); - bool is_sysex8_packet(const universal_packet&); +```cpp +bool is_extended_data_message(const universal_packet&); +bool is_sysex8_packet(const universal_packet&); +``` ### SysEx8 Packet View Once validated being a `sysex8` packet one can create a `sysex8_packet_view` for a `universal_packet`: - struct sysex8_packet_view - { - explicit sysex8_packet_view(const universal_packet&); +```cpp +struct sysex8_packet_view +{ + explicit sysex8_packet_view(const universal_packet&); - group_t group() const; - packet_format format() const; - uint8_t stream_id() const; - size_t payload_size() const; - uint8_t payload_byte(size_t b) const; - }; + group_t group() const; + packet_format format() const; + uint8_t stream_id() const; + size_t payload_size() const; + uint8_t payload_byte(size_t b) const; +}; +``` View members provide accessors to the properties of the message. The additional - std::optional as_sysex8_packet_view(const universal_packet&); +```cpp +std::optional as_sysex8_packet_view(const universal_packet&); +``` helper allows to combine packet type check and view creation in a single statement. diff --git a/docs/flex_data_message.examples.cpp b/docs/flex_data_message.examples.cpp index 725adc2..8eefd03 100644 --- a/docs/flex_data_message.examples.cpp +++ b/docs/flex_data_message.examples.cpp @@ -1,3 +1,95 @@ #include -void run_flex_data_message_examples() { /* TODO */ } +void flex_data_message_examples() +{ + using namespace midi; + + auto a = make_flex_data_message(0, packet_format::complete, packet_address::channel, 5, 0x20, 0x01, 0x123456); + assert(is_flex_data_message(a)); + + if (auto m = as_flex_data_message_view(a)) + { + // inspect message fields + assert(m->group() == 0); + assert(m->format() == packet_format::complete); + assert(m->address() == packet_address::channel); + assert(m->channel() == 5); + assert(m->status_bank() == 0x20); + assert(m->status() == 0x01); + assert(m->data1() == 0x123456); + assert(m->data2() == 0); + assert(m->data3() == 0); + } + + auto t = + make_flex_data_text_message(0, packet_format::complete, packet_address::channel, 5, 0x20, 0x01, "Hello World!"); + if (auto m = as_flex_data_message_view(t)) + { + assert(m->payload_as_string() == "Hello World!"); + } + + auto tmpo = make_set_tempo_message(0, 5000000); + if (auto m = as_flex_data_message_view(tmpo)) + { + assert(m->status_bank() == 0x00); + assert(m->status() == 0x00); + assert(m->data1() == 5000000); + } + + constexpr uint8_t numerator = 4; + constexpr uint8_t denominator = 2; // 4 (2^2) denominator + constexpr uint8_t nr_32rd_notes = 8; // 4/4 time signature + + auto tsig = make_set_time_signature_message(0, numerator, denominator, nr_32rd_notes); + if (auto m = as_flex_data_message_view(tsig)) + { + assert(m->status_bank() == 0x00); + assert(m->status() == 0x01); + assert(m->data_byte1() == numerator); + assert(m->data_byte2() == denominator); + assert(m->data_byte3() == nr_32rd_notes); + } + + constexpr uint8_t num_clocks_per_primary_click = 24; + constexpr uint8_t bar_accent_part1 = 3; + constexpr uint8_t bar_accent_part2 = 2; + constexpr uint8_t bar_accent_part3 = 0; + constexpr uint8_t num_subdivision_clicks1 = 2; + constexpr uint8_t num_subdivision_clicks2 = 0; + + auto metro = make_set_metronome_message(0, + num_clocks_per_primary_click, + bar_accent_part1, + bar_accent_part2, + bar_accent_part3, + num_subdivision_clicks1, + num_subdivision_clicks2); + if (auto m = as_flex_data_message_view(metro)) + { + assert(m->status_bank() == 0x00); + assert(m->status() == 0x02); + assert(m->data_byte1() == num_clocks_per_primary_click); + assert(m->data_byte2() == bar_accent_part1); + assert(m->data_byte3() == bar_accent_part2); + assert(m->data_byte4() == bar_accent_part3); + assert(m->data_byte5() == num_subdivision_clicks1); + assert(m->data_byte6() == num_subdivision_clicks2); + } + + constexpr uint4_t sharps_or_flats = 5; // 5 sharps + constexpr uint4_t tonic_note = 0x4; // tonic D + + auto ksig = make_set_key_signature_message(0, packet_address::channel, 5, sharps_or_flats, tonic_note); // D major + if (auto m = as_flex_data_message_view(ksig)) + { + assert(m->status_bank() == 0x00); + assert(m->status() == 0x05); + assert((m->data_byte1() >> 4) == sharps_or_flats); + assert((m->data_byte1() & 0x0F) == tonic_note); + } +} + +void run_flex_data_message_examples() +{ + flex_data_message_examples(); +} diff --git a/docs/flex_data_message.md b/docs/flex_data_message.md index 885e000..6a357f9 100644 --- a/docs/flex_data_message.md +++ b/docs/flex_data_message.md @@ -1,80 +1,113 @@ # Flex Data Messages -_WORK IN PROGRESS_ - -## Base Type - - struct flex_data_message : universal_packet - { - flex_data_message( - group_t, packet_format, packet_address, uint4_t, status_t, status_t, uint32_t = 0, uint32_t = 0, uint32_t = 0); - - packet_format format() const; - packet_address address() const; - status_t status_bank() const; - status_t status() const; - - std::string payload_as_string() const; - static std::string payload_as_string(const universal_packet&); - }; - - bool is_flex_data_message(const universal_packet&); - - -### Factory Functions - - flex_data_message make_flex_data_message(group_t, - packet_format, - packet_address, - uint4_t channel, - status_t status_bank, - status_t status, - uint32_t data1 = 0, - uint32_t data2 = 0, - uint32_t data3 = 0); - flex_data_message make_flex_data_text_message(group_t, - packet_format, - packet_address, - uint4_t channel, - status_t status_bank, - status_t status, - const std::string_view& text); - - flex_data_message make_set_tempo_message(group_t, uint32_t ten_ns_per_quarter_note); - flex_data_message make_set_time_signature_message(group_t, - uint8_t numerator, - uint8_t denominator, - uint8_t nr_32rd_notes); - flex_data_message make_set_metronome_message(group_t, - uint8_t num_clocks_per_primary_click, - uint8_t bar_accent_part1, - uint8_t bar_accent_part2, - uint8_t bar_accent_part3, - uint8_t num_subdivision_clicks1, - uint8_t num_subdivision_clicks2); - flex_data_message make_set_key_signature_message( +Code examples can be found in [`flex_data_message.examples.cpp`](flex_data_message.examples.cpp). + +## Message Creation and Filtering + +_Flex Data Messages_ are represented by a + +```cpp +struct flex_data_message : universal_packet +{ + flex_data_message( + group_t group, packet_format format, packet_address address, + channel_t channel, status_t status_bank, status_t status, + uint32_t data1, uint32_t data2, uint32_t data3); + + packet_format format() const; + packet_address address() const; + status_t status_bank() const; + status_t status() const; + + std::string payload_as_string() const; + static std::string payload_as_string(const universal_packet&); +}; +``` + +`packet_adress` can be `channel (0b00)` or `group (0b01)`. Use the `channel` parameter to specify a channel in `channel` address mode (will be ignored in `group` address mode). +Instead of using `flex_data_message` constructors one can create messages using factory functions: + +```cpp +flex_data_message make_flex_data_message( + group_t, packet_format, packet_address, + uint4_t channel, status_t status_bank, status_t status, + uint32_t data1 = 0, uint32_t data2 = 0, uint32_t data3 = 0); + +flex_data_message make_flex_data_text_message( + group_t, packet_format, packet_address, + uint4_t channel, status_t status_bank, status_t status, + const std::string_view& text); + +flex_data_message make_set_tempo_message(group_t, uint32_t ten_ns_per_quarter_note); + +flex_data_message make_set_time_signature_message( + group_t, uint8_t numerator, uint8_t denominator, uint8_t nr_32rd_notes); + +flex_data_message make_set_metronome_message( + group_t, uint8_t num_clocks_per_primary_click, + uint8_t bar_accent_part1, uint8_t bar_accent_part2, uint8_t bar_accent_part3, + uint8_t num_subdivision_clicks1, uint8_t num_subdivision_clicks2); + +flex_data_message make_set_key_signature_message( group_t, packet_address, uint4_t channel, uint4_t sharps_or_flats, uint4_t tonic_note); - flex_data_message make_set_chord_message( + +flex_data_message make_set_chord_message( group_t, packet_address, uint4_t channel, uint32_t data1, uint32_t data2, uint32_t data3); +``` +Be aware that a single `flex_data_text_message` can hold only 12 characters, if the text is larger you have to send a sequence of packets +using the `packet_format` mechanism (`start`, `cont`, `end`). -### Flex Data Message Data View: +Be also aware that the `denominator` in a _Set Time Signature_ message is expressed as a negative power of two, meaning that +`2` represents a quarter note, `3` an eights note and so on. - struct flex_data_message_view - { - explicit flex_data_message_view(const universal_packet&); +Filtering of _Flex Data Messages_ can be done checking `universal_packet::type()` against +`packet_type::flex_data` or use - group_t group() const; - packet_format format() const; - packet_address address() const; - channel_t channel() const; - status_t status_bank() const; - status_t status() const; - uint32_t data1() const; - uint32_t data2() const; - uint32_t data3() const; +```cpp +bool is_flex_data_message(const universal_packet&); +``` - const std::string payload_as_string() const; - }; +### Flex Data Message Data View: - std::optional as_flex_data_message_view(const universal_packet&); +Once validated being a `flex_data` packet one can create a `flex_data_message_view` +for a `universal_packet`: + +```cpp +struct flex_data_message_view +{ + explicit flex_data_message_view(const universal_packet&); + + group_t group() const; + packet_format format() const; + packet_address address() const; + channel_t channel() const; + status_t status_bank() const; + status_t status() const; + uint32_t data1() const; + uint32_t data2() const; + uint32_t data3() const; + uint8_t data_byte1() const; + uint8_t data_byte2() const; + uint8_t data_byte2() const; + uint8_t data_byte3() const; + uint8_t data_byte2() const; + uint8_t data_byte4() const; + uint8_t data_byte5() const; + uint8_t data_byte6() const; + uint8_t data_byte7() const; + uint8_t data_byte8() const; + + const std::string payload_as_string() const; +}; +``` +View members provide accessors to the properties of the message. +In case of Flex Data Text Messages you can retrieve the message payload using `payload_as_string()`. + +The additional + +```cpp +std::optional as_flex_data_message_view(const universal_packet&); +``` + +helper allows to combine packet type check and view creation in a single statement. diff --git a/docs/midi1_byte_stream.md b/docs/midi1_byte_stream.md index 2fac582..0377228 100644 --- a/docs/midi1_byte_stream.md +++ b/docs/midi1_byte_stream.md @@ -6,32 +6,36 @@ Code examples can be found in [`midi1_byte_stream.examples.cpp`](midi1_byte_stre ## MIDI 1 Byte Stream Parser - class midi1_byte_stream_parser - { - public: - using packet_callback = std::function; - using sysex_callback = std::function; +```cpp +class midi1_byte_stream_parser +{ +public: + using packet_callback = std::function; + using sysex_callback = std::function; - explicit midi1_byte_stream_parser(packet_callback, sysex_callback = {}, bool enable_callbacks = true); - midi1_byte_stream_parser(group_t, packet_callback, sysex_callback = {}, bool enable_callbacks = true); + explicit midi1_byte_stream_parser(packet_callback, sysex_callback = {}, bool enable_callbacks = true); + midi1_byte_stream_parser(group_t, packet_callback, sysex_callback = {}, bool enable_callbacks = true); - bool callbacks_enabled() const; - void enable_callbacks(bool); + bool callbacks_enabled() const; + void enable_callbacks(bool); - group_t group() const; - void set_group(group_t); + group_t group() const; + void set_group(group_t); - void feed(uint8_t); - void feed(const uint8_t* data, size_t num_bytes); - void feed(const uint8_t* begin, const uint8_t* end); + void feed(uint8_t); + void feed(const uint8_t* data, size_t num_bytes); + void feed(const uint8_t* begin, const uint8_t* end); - void reset(); - }; + void reset(); +}; +``` ## MIDI 1 Byte Stream Conversion - universal_packet from_midi1_byte_stream(uint8_t status, uint7_t d1, uint7_t d2); +```cpp +universal_packet from_midi1_byte_stream(uint8_t status, uint7_t d1, uint7_t d2); - size_t midi1_byte_stream_size(const universal_packet&); +size_t midi1_byte_stream_size(const universal_packet&); - size_t to_midi1_byte_stream(const universal_packet&, uint8_t bytes[8]); +size_t to_midi1_byte_stream(const universal_packet&, uint8_t bytes[8]); +``` diff --git a/docs/midi1_channel_voice_message.md b/docs/midi1_channel_voice_message.md index 6fa96f5..d07ebfe 100644 --- a/docs/midi1_channel_voice_message.md +++ b/docs/midi1_channel_voice_message.md @@ -6,60 +6,76 @@ Code examples can be found in [`midi1_channel_voice_message.examples.cpp`](midi1 MIDI 1 Channel Voice Messages are represented by a - struct midi1_channel_voice_message : universal_packet - { - midi1_channel_voice_message(); - midi1_channel_voice_message(group_t, status_t, uint7_t data1 = 0, uint7_t data2 = 0); - midi1_channel_voice_message(const universal_packet&); - }; +```cpp +struct midi1_channel_voice_message : universal_packet +{ + midi1_channel_voice_message(); + midi1_channel_voice_message(group_t, status_t, uint7_t data1 = 0, uint7_t data2 = 0); + midi1_channel_voice_message(const universal_packet&); +}; +``` Instead of using `midi1_channel_voice_message` constructors one can create messages using factory functions: - midi1_channel_voice_message - make_midi1_channel_voice_message(group_t, status_t, channel_t, uint7_t data1, uint7_t data2 = 0); - - midi1_channel_voice_message - make_midi1_note_off_message(group_t, channel_t, note_nr_t, velocity); - midi1_channel_voice_message - make_midi1_note_on_message(group_t, channel_t, note_nr_t, velocity); - midi1_channel_voice_message - make_midi1_poly_pressure_message(group_t, channel_t, note_nr_t, controller_value); - midi1_channel_voice_message - make_midi1_control_change_message(group_t, channel_t, controller_t, controller_value); - midi1_channel_voice_message - make_midi1_program_change_message(group_t, channel_t, program_t); - midi1_channel_voice_message - make_midi1_channel_pressure_message(group_t, channel_t, controller_value); - midi1_channel_voice_message - make_midi1_pitch_bend_message(group_t, channel_t, pitch_bend); +```cpp +midi1_channel_voice_message +make_midi1_channel_voice_message(group_t, status_t, channel_t, uint7_t data1, uint7_t data2 = 0); + +midi1_channel_voice_message +make_midi1_note_off_message(group_t, channel_t, note_nr_t, velocity); + +midi1_channel_voice_message +make_midi1_note_on_message(group_t, channel_t, note_nr_t, velocity); + +midi1_channel_voice_message +make_midi1_poly_pressure_message(group_t, channel_t, note_nr_t, controller_value); + +midi1_channel_voice_message +make_midi1_control_change_message(group_t, channel_t, controller_t, controller_value); + +midi1_channel_voice_message +make_midi1_program_change_message(group_t, channel_t, program_t); + +midi1_channel_voice_message +make_midi1_channel_pressure_message(group_t, channel_t, controller_value); + +midi1_channel_voice_message +make_midi1_pitch_bend_message(group_t, channel_t, pitch_bend); +``` Filtering of MIDI 1 Channel Voice Messages can be done checking `universal_packet::type()` against `packet_type::midi1_channel_voice` or use - bool is_midi1_channel_voice_message(const universal_packet&); +```cpp +bool is_midi1_channel_voice_message(const universal_packet&); +``` ## Message View Once validated against `packet_type::midi1_channel_voice` one can create a `midi1_channel_voice_message_view` for a `universal_packet`: - struct midi1_channel_voice_message_view - { - midi1_channel_voice_message_view(const universal_packet&); +```cpp +struct midi1_channel_voice_message_view +{ + midi1_channel_voice_message_view(const universal_packet&); - group_t group() const; - status_t status() const; - channel_t channel() const; - uint7_t data_byte_1() const; - uint7_t data_byte_2() const; + group_t group() const; + status_t status() const; + channel_t channel() const; + uint7_t data_byte_1() const; + uint7_t data_byte_2() const; - uint14_t get_14bit_value() const; - }; + uint14_t get_14bit_value() const; +}; +``` View members provide accessors to the properties of the message. The additional - std::optional as_midi1_channel_voice_message_view(const universal_packet&); +```cpp +std::optional as_midi1_channel_voice_message_view(const universal_packet&); +``` -helper allows to combine packet type check and view creation in a single statement. \ No newline at end of file +helper allows to combine packet type check and view creation in a single statement. diff --git a/docs/midi2_channel_voice_message.md b/docs/midi2_channel_voice_message.md index cc37736..b17cc30 100644 --- a/docs/midi2_channel_voice_message.md +++ b/docs/midi2_channel_voice_message.md @@ -6,111 +6,135 @@ Code examples can be found in [`midi2_channel_voice_message.examples.cpp`](midi2 MIDI 2 Channel Voice Messages are represented by a - struct midi2_channel_voice_message : universal_packet - { - midi2_channel_voice_message(); - midi2_channel_voice_message(group_t, status_t, channel_t, uint8_t byte3, uint8_t byte4, uint32_t data); - midi2_channel_voice_message(const universal_packet&); - }; +```cpp +struct midi2_channel_voice_message : universal_packet +{ + midi2_channel_voice_message(); + midi2_channel_voice_message(group_t, status_t, channel_t, uint8_t byte3, uint8_t byte4, uint32_t data); + midi2_channel_voice_message(const universal_packet&); +}; +``` Instead of using `midi2_channel_voice_message` constructors one can create messages using factory functions: - midi2_channel_voice_message - make_midi2_channel_voice_message(group_t, status_t, channel_t, uint7_t index1, uint7_t index2, uint32_t data); - - midi2_channel_voice_message - make_midi2_note_off_message(group_t, channel_t, note_nr_t, velocity, uint8_t attribute = 0, uint16_t attribute_data = 0); - 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_midi2_note_on_message(group_t, channel_t, note_nr_t, velocity, uint8_t attribute, uint16_t attribute_data); - - midi2_channel_voice_message - make_midi2_poly_pressure_message(group_t, channel_t, note_nr_t, controller_value); - midi2_channel_voice_message - make_registered_per_note_controller_message(group_t, channel_t, note_nr_t, uint8_t controller, controller_value); - midi2_channel_voice_message - make_assignable_per_note_controller_message(group_t, channel_t, note_nr_t, uint8_t controller, controller_value); - midi2_channel_voice_message - make_per_note_management_message(group_t, channel_t, note_nr_t, note_management_flags); - - midi2_channel_voice_message - make_midi2_control_change_message(group_t, channel_t, uint7_t controller, controller_value); - midi2_channel_voice_message - make_registered_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_value); - midi2_channel_voice_message - make_assignable_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_value); - midi2_channel_voice_message - make_relative_registered_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_increment); - midi2_channel_voice_message - make_relative_assignable_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_increment); - - midi2_channel_voice_message - make_midi2_program_change_message(group_t, channel_t, program_t); - midi2_channel_voice_message - make_midi2_program_change_message(group_t, channel_t, program_t, uint14_t bank); - - midi2_channel_voice_message - make_midi2_channel_pressure_message(group_t, channel_t, controller_value); - - midi2_channel_voice_message - make_midi2_pitch_bend_message(group_t, channel_t, pitch_bend); - midi2_channel_voice_message - make_per_note_pitch_bend_message(group_t, channel_t, note_nr_t, pitch_bend); +```cpp +midi2_channel_voice_message +make_midi2_channel_voice_message(group_t, status_t, channel_t, uint7_t index1, uint7_t index2, uint32_t data); + +midi2_channel_voice_message +make_midi2_note_off_message(group_t, channel_t, note_nr_t, velocity, uint8_t attribute = 0, uint16_t attribute_data = 0); + +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_midi2_note_on_message(group_t, channel_t, note_nr_t, velocity, uint8_t attribute, uint16_t attribute_data); + +midi2_channel_voice_message +make_midi2_poly_pressure_message(group_t, channel_t, note_nr_t, controller_value); + +midi2_channel_voice_message +make_registered_per_note_controller_message(group_t, channel_t, note_nr_t, uint8_t controller, controller_value); + +midi2_channel_voice_message +make_assignable_per_note_controller_message(group_t, channel_t, note_nr_t, uint8_t controller, controller_value); + +midi2_channel_voice_message +make_per_note_management_message(group_t, channel_t, note_nr_t, note_management_flags); + +midi2_channel_voice_message +make_midi2_control_change_message(group_t, channel_t, uint7_t controller, controller_value); + +midi2_channel_voice_message +make_registered_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_value); + +midi2_channel_voice_message +make_assignable_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_value); + +midi2_channel_voice_message +make_relative_registered_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_increment); + +midi2_channel_voice_message +make_relative_assignable_controller_message(group_t, channel_t, uint7_t bank, uint7_t index, controller_increment); + +midi2_channel_voice_message +make_midi2_program_change_message(group_t, channel_t, program_t); + +midi2_channel_voice_message +make_midi2_program_change_message(group_t, channel_t, program_t, uint14_t bank); + +midi2_channel_voice_message +make_midi2_channel_pressure_message(group_t, channel_t, controller_value); + +midi2_channel_voice_message +make_midi2_pitch_bend_message(group_t, channel_t, pitch_bend); + +midi2_channel_voice_message +make_per_note_pitch_bend_message(group_t, channel_t, note_nr_t, pitch_bend); +``` Filtering of MIDI 2 Channel Voice Messages can be done checking `universal_packet::type()` against `packet_type::midi2_channel_voice` or use - bool is_midi2_channel_voice_message(const universal_packet&); - bool is_registered_controller_message(const universal_packet&); - bool is_assignable_controller_message(const universal_packet&); - bool is_registered_per_note_controller_message(const universal_packet&); - bool is_registered_per_note_controller_pitch_message(const universal_packet&); - bool is_assignable_per_note_controller_message(const universal_packet&); - bool is_per_note_pitch_bend_message(const universal_packet&); +```cpp +bool is_midi2_channel_voice_message(const universal_packet&); +bool is_registered_controller_message(const universal_packet&); +bool is_assignable_controller_message(const universal_packet&); +bool is_registered_per_note_controller_message(const universal_packet&); +bool is_registered_per_note_controller_pitch_message(const universal_packet&); +bool is_assignable_per_note_controller_message(const universal_packet&); +bool is_per_note_pitch_bend_message(const universal_packet&); - bool is_note_on_with_attribute(const universal_packet&, uint8_t); - bool is_note_off_with_attribute(const universal_packet&, uint8_t); - bool is_note_on_with_pitch_7_9(const universal_packet&); +bool is_note_on_with_attribute(const universal_packet&, uint8_t); +bool is_note_off_with_attribute(const universal_packet&, uint8_t); +bool is_note_on_with_pitch_7_9(const universal_packet&); - bool is_pitch_bend_sensitivity_message(const universal_packet&); - bool is_per_note_pitch_bend_sensitivity_message(const universal_packet&); +bool is_pitch_bend_sensitivity_message(const universal_packet&); +bool is_per_note_pitch_bend_sensitivity_message(const universal_packet&); +``` -## Message View and Properties +## Message View and Properties Once validated against `packet_type::midi2_channel_voice` one can create a `midi2_channel_voice_message_view` for a `universal_packet`: - struct midi2_channel_voice_message_view - { - midi2_channel_voice_message_view(const universal_packet&); +```cpp +struct midi2_channel_voice_message_view +{ + midi2_channel_voice_message_view(const universal_packet&); - group_t group() const; - status_t status() const; - channel_t channel() const; - uint7_t byte3() const; - uint7_t byte4() const; - uint32_t data() const; - }; + group_t group() const; + status_t status() const; + channel_t channel() const; + uint7_t byte3() const; + uint7_t byte4() const; + uint32_t data() const; +}; +``` View members provide accessors to the properties of the message. The additional - std::optional as_midi2_channel_voice_message_view(const universal_packet&); +```cpp +std::optional as_midi2_channel_voice_message_view(const universal_packet&); +``` helper allows to combine packet type check and view creation in a single statement. There are free functions available to retrieve properties of the specific messages: - uint8_t get_midi2_note_attribute(const universal_packet&); - uint16_t get_midi2_note_attribute_data(const universal_packet&); +```cpp +uint8_t get_midi2_note_attribute(const universal_packet&); +uint16_t get_midi2_note_attribute_data(const universal_packet&); - uint8_t get_per_note_controller_index(const universal_packet&); +uint8_t get_per_note_controller_index(const universal_packet&); - pitch_bend_sensitivity get_pitch_bend_sensitivity_value(const universal_packet&); - pitch_bend_sensitivity get_per_note_pitch_bend_sensitivity_value(const universal_packet&); +pitch_bend_sensitivity get_pitch_bend_sensitivity_value(const universal_packet&); +pitch_bend_sensitivity get_per_note_pitch_bend_sensitivity_value(const universal_packet&); - pitch_bend get_per_note_pitch_bend_value(const universal_packet&); +pitch_bend get_per_note_pitch_bend_value(const universal_packet&); +``` diff --git a/docs/stream_message.examples.cpp b/docs/stream_message.examples.cpp index 47ea2c5..5069c14 100644 --- a/docs/stream_message.examples.cpp +++ b/docs/stream_message.examples.cpp @@ -1,3 +1,169 @@ #include -void run_stream_message_examples() { /* TODO */ } +#include + +/* Most of the code in these examples is taken from the FreeRTOS firmware for ProtoZOA, take a look here: + https://github.com/midi2-dev/AmeNote_Protozoa/blob/master/UUT_FreeRTOS/FreeRTOS_Tasks/UMPProcessing.cpp#L158 +*/ +static void reply_with_packet(const midi::universal_packet& p) +{ + using namespace midi; + + if (is_stream_message(p)) + { + // you may want to inspect the other replies, too + + // Remark: in real world implementations you have to aggregate the name and serial strings in case of multi-part + // replies! + if (auto reply = as_endpoint_name_view(p)) + { + auto name = reply->payload(); + } + else if (auto reply = as_function_block_name_view(p)) + { + auto name = reply->payload(); + } + else if (auto reply = as_product_instance_id_view(p)) + { + auto serial = reply->payload(); + } + } +} + +void endpoint_discovery_example() +{ + using namespace midi; + + auto discovery = make_endpoint_discovery_message(discovery_filter::endpoint_all); + + if (auto m = as_endpoint_discovery_view(discovery)) + { + const auto identity = device_identity{ manufacturer::native_instruments, 0x1730, 49, 0x00010005 }; + const auto ep_name = std::string{ "Kontrol S49" }; + const auto serial = std::string{ "12345678" }; + const auto protocol = protocol::midi1; + const auto extensions = extensions_t{ 0 }; + + if (m->requests_info()) + { + constexpr auto endpoint_info = make_endpoint_info_message(3, true, protocol::midi1 + protocol::midi2, 0); + reply_with_packet(endpoint_info); + } + + if (m->requests_device_identity()) + { + const auto device_identity = make_device_identity_message(identity); + reply_with_packet(device_identity); + } + + if (m->requests_name()) + { + send_endpoint_name(ep_name, reply_with_packet); + } + + if (m->requests_product_instance_id()) + { + send_product_instance_id(serial, reply_with_packet); + } + + if (m->requests_stream_configuration()) + { + reply_with_packet(make_stream_configuration_notification(protocol, extensions)); + } + } +} + +void function_block_discovery_example() +{ + using namespace midi; + + auto discovery = make_function_block_discovery_message(0xFF, discovery_filter::function_block_all); + + if (auto m = as_function_block_discovery_view(discovery)) + { + constexpr uint8_t main = 0; + constexpr uint8_t din_port_out = 1; + constexpr uint8_t din_port_in = 2; + + if (m->requests_info()) + { + if (m->requests_function_block(main)) + { + constexpr auto reply = + midi::make_function_block_info_message(0, midi::function_block_options::bidirectional, 0); + reply_with_packet(reply); + } + + if (m->requests_function_block(din_port_out)) + { + constexpr auto reply = midi::make_function_block_info_message( + din_port_out, + midi::function_block_options{ true, + midi::function_block_options::direction_input, + midi::function_block_options::midi1_31250, + midi::function_block_options::ui_hint_as_direction, + 0x00, + 0 }, + din_port_out); + reply_with_packet(reply); + } + + if (m->requests_function_block(din_port_in)) + { + constexpr auto reply = midi::make_function_block_info_message( + din_port_in, + midi::function_block_options{ true, + midi::function_block_options::direction_output, + midi::function_block_options::midi1_31250, + midi::function_block_options::ui_hint_as_direction, + 0x00, + 0 }, + din_port_in); + reply_with_packet(reply); + } + } + + if (m->requests_name()) + { + if (m->requests_function_block(main)) + { + constexpr auto reply = midi::make_function_block_name_message(midi::packet_format::complete, 0, "Main"); + reply_with_packet(reply); + } + + if (m->requests_function_block(din_port_out)) + { + constexpr auto reply = + midi::make_function_block_name_message(midi::packet_format::complete, din_port_out, "EXT OUT"); + reply_with_packet(reply); + } + + if (m->requests_function_block(din_port_in)) + { + constexpr auto reply = + midi::make_function_block_name_message(midi::packet_format::complete, din_port_in, "EXT IN"); + reply_with_packet(reply); + } + } + } +} + +void stream_configuration_example() +{ + using namespace midi; + + auto r = make_stream_configuration_request(protocol::midi2); + + if (auto m = as_stream_configuration_view(r)) + { + auto new_protocol = m->protocol(); + reply_with_packet(midi::make_stream_configuration_notification(m->protocol(), m->extensions())); + } +} + +void run_stream_message_examples() +{ + endpoint_discovery_example(); + function_block_discovery_example(); + stream_configuration_example(); +} diff --git a/docs/stream_message.md b/docs/stream_message.md index 0984ebd..c913ad5 100644 --- a/docs/stream_message.md +++ b/docs/stream_message.md @@ -1,86 +1,165 @@ # Stream Messages -_WORK IN PROGRESS_ - Code examples can be found in [`stream_message.examples.cpp`](stream_message.examples.cpp). -## Base Type - - struct stream_message : universal_packet - { - stream_message(status_t, packet_format = packet_format::complete); - - packet_format format(); - void set_format(packet_format); - }; - - bool is_stream_message(const universal_packet&); - - -### Factory Functions - - stream_message make_endpoint_discovery_message(uint8_t filter, - uint8_t ump_version_major = 1, - uint8_t ump_version_minor = 1); - stream_message make_endpoint_info_message(uint8_t num_function_blocks, - bool static_function_blocks, - uint8_t protocols, - uint8_t extensions, - uint8_t ump_version_major = 1, - uint8_t ump_version_minor = 1); - stream_message make_device_identity_message(const device_identity&); - stream_message make_endpoint_name_message(packet_format, const std::string_view&); - stream_message make_product_instance_id_message(packet_format, const std::string_view&); - stream_message make_stream_configuration_request(protocol_t, extensions_t = 0); - stream_message make_stream_configuration_notification(protocol_t, extensions_t = 0); - - stream_message make_function_block_discovery_message(uint8_t function_block, uint8_t filter); - stream_message make_function_block_info_message(uint7_t function_block, - uint2_t direction, - group_t first_group, - uint4_t num_groups_spanned = 1); - stream_message make_function_block_info_message(uint7_t function_block, - const function_block_options&, - group_t first_group, - uint4_t num_groups_spanned = 1); - stream_message make_function_block_name_message(packet_format, - uint7_t function_block, - const std::string_view& n); - - - struct function_block_options - { - // active - bool active = true; - - // directions - static constexpr uint2_t direction_input = 0b01; //!< Input, Function Block receives MIDI Messages only - static constexpr uint2_t direction_output = 0b10; //!< Output, Function Block transmits MIDI Messages only - static constexpr uint2_t bidirectional = - 0b11; //!< Bidirectional Connections. Every Input Group member has a matching Output Group. - - uint2_t direction = bidirectional; - - // MIDI 1 - static constexpr uint2_t not_midi1 = 0b00; //!< Not MIDI 1.0 - static constexpr uint2_t midi1_unrestricted = 0b01; //!< MIDI 1.0 - don't restrict Bandwidth - static constexpr uint2_t midi1_31250 = 0b10; //!< Restrict Bandwidth to 31.25Kbps - - uint2_t midi1 = not_midi1; - - // ui hints - static constexpr uint2_t ui_hint_as_direction = 0b00; - static constexpr uint2_t ui_hint_receiver = 0b01; - static constexpr uint2_t ui_hint_sender = 0b10; - - uint2_t ui_hint = ui_hint_as_direction; - - uint8_t ci_message_version = 0x00; - uint8_t max_num_sysex8_streams = 0; - }; +## Message Creation + +Stream Messages are represented by a + +```cpp +struct stream_message : universal_packet +{ + stream_message(status_t, packet_format = packet_format::complete); + + packet_format format(); + void set_format(packet_format); +}; +``` + +Instead of using `stream_message` constructors one can create messages using factory functions: + +### Endpoint Discovery + +```cpp +stream_message make_endpoint_discovery_message( + uint8_t filter, uint8_t ump_version_major = 1, uint8_t ump_version_minor = 1); + +stream_message make_endpoint_info_message( + uint8_t num_function_blocks, bool static_function_blocks, + uint8_t protocols, uint8_t extensions, + uint8_t ump_version_major = 1, uint8_t ump_version_minor = 1); + +stream_message make_device_identity_message(const device_identity&); + +stream_message make_endpoint_name_message(packet_format, const std::string_view&); + +stream_message make_product_instance_id_message(packet_format, const std::string_view&); +``` + +For the `filter` parameter of the _Endpoint Discovery_ message use any combination of + +```cpp +namespace discovery_filter { + constexpr uint8_t endpoint_info = 0b00001; + constexpr uint8_t device_identity = 0b00010; + constexpr uint8_t endpoint_name = 0b00100; + constexpr uint8_t product_instance_id = 0b01000; + constexpr uint8_t stream_configuration = 0b10000; +} +``` + +or `discovery_filter::endpoint_all (0b11111)` to request all available infomation. + +`protocols` can be any combination of `protocol::midi1` and `protocol::midi2`, extensions can be any combination of `jitter_reduction_transmit` and `jitter_reduction_receive` or `0`. + +_Endpoint Name_ and _Product Instance ID_ messages can hold up to 14 characters, if you want to report longer names +do send a sequence of packets using the `packet_format` mechanism (`start`, `cont`, `end`). +Alternatively use the _Multi Part Message helpers_ described below. + +### Function Block Discovery + +```cpp +stream_message make_function_block_discovery_message( + uint8_t function_block, uint8_t filter); + +stream_message make_function_block_info_message( + uint7_t function_block, uint2_t direction, + group_t first_group, uint4_t num_groups_spanned = 1); + +stream_message make_function_block_info_message( + uint7_t function_block, const function_block_options&, + group_t first_group, uint4_t num_groups_spanned = 1); + +stream_message make_function_block_name_message( + packet_format, uint7_t function_block, const std::string_view& n); +``` + +With the _Function Block Discovery_ message pass `0xFF` as `function_block` to request information for all function blocks +or specify a function block id instead. + +As `filter` you can use any combination of + +```cpp +namespace discovery_filter { + constexpr uint8_t function_block_info = 0b01; + constexpr uint8_t function_block_name = 0b10; +} +``` + +or `discovery_filter::function_block_all` to request all available information. + +Use the + +```cpp +struct function_block_options +{ + // active + bool active = true; + + // directions + static constexpr uint2_t direction_input = 0b01; // Function Block receives MIDI Messages only + static constexpr uint2_t direction_output = 0b10; // Function Block transmits MIDI Messages only + static constexpr uint2_t bidirectional = 0b11; // Bidirectional + + uint2_t direction = bidirectional; + + // MIDI 1 + static constexpr uint2_t not_midi1 = 0b00; // Not MIDI 1.0 aka MIDI 2 + static constexpr uint2_t midi1_unrestricted = 0b01; // MIDI 1.0 - don't restrict Bandwidth (USB, Network) + static constexpr uint2_t midi1_31250 = 0b10; // Restrict Bandwidth to 31.25Kbps (5 pin DIN) + + uint2_t midi1 = not_midi1; + + // ui hints + static constexpr uint2_t ui_hint_as_direction = 0b00; + static constexpr uint2_t ui_hint_receiver = 0b01; + static constexpr uint2_t ui_hint_sender = 0b10; + + uint2_t ui_hint = ui_hint_as_direction; + + uint8_t ci_message_version = 0x00; + uint8_t max_num_sysex8_streams = 0; +}; +``` + +helper struct to report the properties of a function block in a _Function Block Info_ message. + +_Endpoint Name_ and _Product Instance ID_ Messages can hold up to 14 characters, if you want to report longer strings +do send a sequence of packets using the `packet_format` mechanism (`start`, `cont`, `end`). +Alternatively use the _Multi Part Message helpers_ described below. + +### Stream Configuration + +Use a _Stream Configuration Request_ to request changes to the MIDI protocol or extensions to be used. + +```cpp +stream_message make_stream_configuration_request(protocol_t, extensions_t = 0); +```` + +Reply with a _Stream Configuration Notification_ to report your current protocol and extenensions used. + +```cpp +stream_message make_stream_configuration_notification(protocol_t, extensions_t = 0); +``` + +You can send a _Stream Configuration Notification_ message at any time to report configuration changes, +not only as a reply to a _Stream Configuration Request_. + +## Message Filtering + +Filtering of Stream Messages can be done checking `universal_packet::type()` against +`packet_type::stream` or use + +```cpp +bool is_stream_message(const universal_packet&); +``` ## Multi Part Message helpers + +The Multi Part Message template helpers allow to send strings of arbitrary length with a single function call: + +```cpp template void send_endpoint_name(std::string_view, Sender&&); @@ -89,157 +168,167 @@ Code examples can be found in [`stream_message.examples.cpp`](stream_message.exa template void send_function_block_name(uint7_t function_block, std::string_view, Sender&&); +``` -## Data Views +Provide a `Sender` function that can send a single packet to a receiver. -### Endpoint Discovery View +## Data Views - namespace discovery_filter { - constexpr uint8_t endpoint_info = 0b00001; - constexpr uint8_t device_identity = 0b00010; - constexpr uint8_t endpoint_name = 0b00100; - constexpr uint8_t product_instance_id = 0b01000; - constexpr uint8_t stream_configuration = 0b10000; - constexpr uint8_t endpoint_all = 0b11111; +Different to most other message types there is no generic `endpoint_discovery_view` available. - constexpr uint8_t function_block_info = 0b01; - constexpr uint8_t function_block_name = 0b10; - constexpr uint8_t function_block_all = 0b11; - } // namespace discovery_filter +Instead every _Stream Message_ has its individual message view. - struct endpoint_discovery_view - { - endpoint_discovery_view(const universal_packet&); +### Endpoint Discovery View - uint8_t ump_version_major() const; - uint8_t ump_version_minor() const; - uint16_t ump_version() const; +```cpp +struct endpoint_discovery_view +{ + endpoint_discovery_view(const universal_packet&); - uint8_t filter() const; + uint8_t ump_version_major() const; + uint8_t ump_version_minor() const; + uint16_t ump_version() const; - bool requests_info() const; - bool requests_device_identity() const; - bool requests_name() const; - bool requests_product_instance_id() const; - bool requests_stream_configuration() const; - }; + uint8_t filter() const; + bool requests_info() const; + bool requests_device_identity() const; + bool requests_name() const; + bool requests_product_instance_id() const; + bool requests_stream_configuration() const; +}; - std::optional as_endpoint_discovery_view(const universal_packet&); +std::optional as_endpoint_discovery_view(const universal_packet&); +``` ### Endpoint Info View - struct endpoint_info_view - { - endpoint_info_view(const universal_packet&); +```cpp +struct endpoint_info_view +{ + endpoint_info_view(const universal_packet&); - uint8_t ump_version_major() const; - uint8_t ump_version_minor() const; - uint16_t ump_version() const; + uint8_t ump_version_major() const; + uint8_t ump_version_minor() const; + uint16_t ump_version() const; - uint8_t num_function_blocks() const; - bool static_function_blocks() const; - uint8_t protocols() const; - uint8_t extensions() const; - }; + uint8_t num_function_blocks() const; + bool static_function_blocks() const; + uint8_t protocols() const; + uint8_t extensions() const; +}; - std::optional as_endpoint_info_view(const universal_packet&); +std::optional as_endpoint_info_view(const universal_packet&); +``` ### Device Identity View - struct device_identity_view - { - device_identity_view(const universal_packet&); +```cpp +struct device_identity_view +{ + device_identity_view(const universal_packet&); - device_identity identity() const; - }; + device_identity identity() const; +}; - std::optional as_device_identity_view(const universal_packet&); +std::optional as_device_identity_view(const universal_packet&); +``` ### Endpoint Name View - struct endpoint_name_view - { - endpoint_name_view(const universal_packet&); +```cpp +struct endpoint_name_view +{ + endpoint_name_view(const universal_packet&); - packet_format format() const; - std::string payload() const; - }; + packet_format format() const; + std::string payload() const; +}; - std::optional as_endpoint_name_view(const universal_packet&); +std::optional as_endpoint_name_view(const universal_packet&); +``` ### Product Instance ID View - struct product_instance_id_view - { - product_instance_id_view(const universal_packet&); +```cpp +struct product_instance_id_view +{ + product_instance_id_view(const universal_packet&); - packet_format format() const; - std::string payload() const; - }; + packet_format format() const; + std::string payload() const; +}; - std::optional as_product_instance_id_view(const universal_packet&); +std::optional as_product_instance_id_view(const universal_packet&); +``` ### Stream Configuration View - struct stream_configuration_view - { - stream_configuration_view(const universal_packet&); +```cpp +struct stream_configuration_view +{ + stream_configuration_view(const universal_packet&); - protocol_t protocol() const; - extensions_t extensions() const; - }; + protocol_t protocol() const; + extensions_t extensions() const; +}; - std::optional as_stream_configuration_view(const universal_packet&); +std::optional as_stream_configuration_view(const universal_packet&); +``` ### Function Block Discovery View - struct function_block_discovery_view - { - function_block_discovery_view(const universal_packet&); +```cpp +struct function_block_discovery_view +{ + function_block_discovery_view(const universal_packet&); - uint8_t function_block() const; - uint8_t filter() const; + uint8_t function_block() const; + uint8_t filter() const; - bool requests_function_block(uint8_t block) const; - bool requests_info() const; - bool requests_name() const; - }; + bool requests_function_block(uint8_t block) const; + bool requests_info() const; + bool requests_name() const; +}; - std::optional as_function_block_discovery_view(const universal_packet&); +std::optional as_function_block_discovery_view(const universal_packet&); +``` ### Function Block Info View - struct function_block_info_view - { - function_block_info_view(const universal_packet&); - - bool active() const; - uint8_t function_block() const; +```cpp +struct function_block_info_view +{ + function_block_info_view(const universal_packet&); - uint8_t direction() const; - uint8_t midi1() const; - uint8_t ui_hint() const; + bool active() const; + uint8_t function_block() const; - uint8_t first_group() const; - uint8_t num_groups_spanned() const; + uint8_t direction() const; + uint8_t midi1() const; + uint8_t ui_hint() const; - uint7_t ci_message_version() const; - uint8_t max_num_sysex8_streams() const; - }; + uint8_t first_group() const; + uint8_t num_groups_spanned() const; - std::optional as_function_block_info_view(const universal_packet&); + uint7_t ci_message_version() const; + uint8_t max_num_sysex8_streams() const; +}; +std::optional as_function_block_info_view(const universal_packet&); +``` ### Function Block Name View - struct function_block_name_view - { - function_block_name_view(const universal_packet&); +```cpp +struct function_block_name_view +{ + function_block_name_view(const universal_packet&); - packet_format format() const; - uint8_t function_block() const; - std::string payload() const; - }; + packet_format format() const; + uint8_t function_block() const; + std::string payload() const; +}; - std::optional as_function_block_name_view(const universal_packet&); +std::optional as_function_block_name_view(const universal_packet&); +``` diff --git a/docs/system_message.md b/docs/system_message.md index 4bb526f..ef4015b 100644 --- a/docs/system_message.md +++ b/docs/system_message.md @@ -2,43 +2,55 @@ Create system messages by using the - system_message make_system_message(group_t, status_t, uint7_t data1 = 0, uint7_t data2 = 0); - system_message make_song_position_message(group_t, uint14_t position); +```cpp +system_message make_system_message(group_t, status_t, uint7_t data1 = 0, uint7_t data2 = 0); +system_message make_song_position_message(group_t, uint14_t position); +``` factory functions. Use - bool is_system_message(const universal_packet&); +```cpp +bool is_system_message(const universal_packet&); +``` to filter for system messages, you may use the - system_message_view(const universal_packet& ump) +```cpp +system_message_view(const universal_packet& ump) +``` for further inspection of the message: - if (is_system_message(packet)) - { - auto m = system_message_view{ packet }; +```cpp +if (is_system_message(packet)) +{ + auto m = system_message_view{ packet }; - group_t g = m.group(); - status_t s = m.status(); - uint7_t d1 = m.data_byte_1(); - uint7_t d2 = m.data_byte_2(); - } + group_t g = m.group(); + status_t s = m.status(); + uint7_t d1 = m.data_byte_1(); + uint7_t d2 = m.data_byte_2(); +} +``` Alternatively use - std::optional as_system_message_view(const universal_packet&); +```cpp +std::optional as_system_message_view(const universal_packet&); +``` to filter and view creation is a single call: - auto c = make_song_position_message(0, 0x1234); - if (auto m = as_system_message_view(c)) - { - group_t g = m->group(); - status_t s = m->status(); - uint7_t d1 = m->data_byte_1(); - uint7_t d2 = m->data_byte_2(); - uint14_t pos = m->get_song_position(); - } +```cpp +auto c = make_song_position_message(0, 0x1234); +if (auto m = as_system_message_view(c)) +{ + group_t g = m->group(); + status_t s = m->status(); + uint7_t d1 = m->data_byte_1(); + uint7_t d2 = m->data_byte_2(); + uint14_t pos = m->get_song_position(); +} +``` More code examples can be found in [`system_message.examples.cpp`](system_message.examples.cpp). diff --git a/docs/types.md b/docs/types.md index e5f9409..5a4d2d6 100644 --- a/docs/types.md +++ b/docs/types.md @@ -6,68 +6,78 @@ Code examples can be found in [`types.examples.cpp`](types.examples.cpp). To improve readability, the library defines type aliases for several MIDI entities: - using uint2_t = std::uint8_t; - using uint4_t = std::uint8_t; - using uint7_t = std::uint8_t; - using uint8_t = std::uint8_t; - using uint14_t = std::uint16_t; - using uint16_t = std::uint16_t; - using uint28_t = std::uint32_t; - using uint32_t = std::uint32_t; - using int32_t = std::int32_t; - using size_t = std::size_t; - - using group_t = uint4_t; - using status_t = uint8_t; - using channel_t = uint4_t; - using note_nr_t = uint7_t; - using controller_t = uint7_t; - using program_t = uint7_t; - using muid_t = uint28_t; - using manufacturer_t = uint32_t; - using protocol_t = uint8_t; - using extensions_t = uint8_t; +```cpp +using uint2_t = std::uint8_t; +using uint4_t = std::uint8_t; +using uint7_t = std::uint8_t; +using uint8_t = std::uint8_t; +using uint14_t = std::uint16_t; +using uint16_t = std::uint16_t; +using uint28_t = std::uint32_t; +using uint32_t = std::uint32_t; +using int32_t = std::int32_t; +using size_t = std::size_t; + +using group_t = uint4_t; +using status_t = uint8_t; +using channel_t = uint4_t; +using note_nr_t = uint7_t; +using controller_t = uint7_t; +using program_t = uint7_t; +using muid_t = uint28_t; +using manufacturer_t = uint32_t; +using protocol_t = uint8_t; +using extensions_t = uint8_t; +``` Intentionally no strong types are used, this may change in a future version, maybe configurable by a `cmake` option. Additionally strong types are available for core MIDI concepts: - struct velocity; - struct pitch_bend; - struct pitch_increment; - struct pitch_7_9; - struct pitch_7_25; - struct pitch_bend_sensitivity; - struct controller_increment; - struct controller_value; - struct device_identity; +```cpp +struct velocity; +struct pitch_bend; +struct pitch_increment; +struct pitch_7_9; +struct pitch_7_25; +struct pitch_bend_sensitivity; +struct controller_increment; +struct controller_value; +struct device_identity; +``` Instances of these types can be initialized with native MIDI 1 bit depths (7 or 14) and with high resolution values (16 or 32 bits). - auto v = velocity{ uint7_t{ 44 } }; - auto pb1 = pitch_bend{ uint32_t { 0x62311258 } }; - auto pb2 = pitch_bend{ -0.128 }; - auto p1 = pitch_7_25{ 81.3f }; - auto p2 = pitch_7_25{ note_nr_t { 68 } }; - auto c1 = controller_value{ uint7_t{ 126 } }; - auto c2 = controller_value{ 0.93 }; +```cpp +auto v = velocity{ uint7_t{ 44 } }; +auto pb1 = pitch_bend{ uint32_t { 0x62311258 } }; +auto pb2 = pitch_bend{ -0.128 }; +auto p1 = pitch_7_25{ 81.3f }; +auto p2 = pitch_7_25{ note_nr_t { 68 } }; +auto c1 = controller_value{ uint7_t{ 126 } }; +auto c2 = controller_value{ 0.93 }; +``` One can query the underlying value for different bit depths / types: - auto pb1 = pitch_bend{ uint32_t { 0x62311258 } }.as_double(); // [-1.0 .. 1.0] - auto pb2 = pitch_bend{ -0.128 }.as_uint14(); // 14 bit MIDI 1 - auto p1 = pitch_7_25{ 81.3f }.as_float(); // [0.0 .. 128.0) - auto c1 = controller_value{ uint7_t{ 126 } }.as_double(); // [0.0 .. 1.0] +```cpp +auto pb1 = pitch_bend{ uint32_t { 0x62311258 } }.as_double(); // [-1.0 .. 1.0] +auto pb2 = pitch_bend{ -0.128 }.as_uint14(); // 14 bit MIDI 1 +auto p1 = pitch_7_25{ 81.3f }.as_float(); // [0.0 .. 128.0) +auto c1 = controller_value{ uint7_t{ 126 } }.as_double(); // [0.0 .. 1.0] +``` There are numerical operators to do math with related types: - pitch_7_25 p1 += pitch_increment{ -199 }; - pitch_increment pbi = pitch_bend{ -0.128 } * pitch_bend_sensitivity{ note_nr_t{ 2 } }; - pitch_7_25 p3 = p1 + pbi; - pitch_7_25 p4 = p3 + 0.011f; +```cpp +pitch_7_25 p1 += pitch_increment{ -199 }; +pitch_increment pbi = pitch_bend{ -0.128 } * pitch_bend_sensitivity{ note_nr_t{ 2 } }; +pitch_7_25 p3 = p1 + pbi; +pitch_7_25 p4 = p3 + 0.011f; - controller_value v1 = controller_value{ 0.44 } + controller_increment{ -1234 }; - v1 += controller_increment{ 0x4000 }; +controller_value v1 = controller_value{ 0.44 } + controller_increment{ -1234 }; +v1 += controller_increment{ 0x4000 }; +``` ## Scaling Helpers @@ -75,18 +85,24 @@ The MIDI 2 specifications stricly defines how values should be down- and upscale There are predefined helper functions for the typical use cases: - auto as7bit1 = downsample_16_to_7bit(0x1234); - auto as7bit2 = downsample_32_to_7bit(0xFEDCBA98); - auto as14bit = downsample_32_to_7bit(0xFEDCBA98); +```cpp +auto as7bit1 = downsample_16_to_7bit(0x1234); +auto as7bit2 = downsample_32_to_7bit(0xFEDCBA98); +auto as14bit = downsample_32_to_7bit(0xFEDCBA98); - auto as16bit = upsample_7_to_16bit(uint7_t{ 42 }); - auto as32bit1 = upsample_7_to_32bit(uint7_t{ 42 }); - auto as32bit2 = upsample_14_to_32bit(uint14_t{ 0x0567 }); +auto as16bit = upsample_7_to_16bit(uint7_t{ 42 }); +auto as32bit1 = upsample_7_to_32bit(uint7_t{ 42 }); +auto as32bit2 = upsample_14_to_32bit(uint14_t{ 0x0567 }); +``` Additionally there is a function that upsamples values between arbitrary bit depths: - auto as14bit = upsample_x_to_ybit(0x34, 6, 14); +```cpp +auto as14bit = upsample_x_to_ybit(0x34, 6, 14); +``` Different to upsampling downsampling can be implemented using standard bit shifting: - auto as14bit = 20bitValue >> 6; +```cpp +auto as14bit = 20bitValue >> 6; +```` diff --git a/docs/universal_packet.md b/docs/universal_packet.md index bbe0632..766a7c6 100644 --- a/docs/universal_packet.md +++ b/docs/universal_packet.md @@ -4,45 +4,47 @@ _Universal MIDI Packets_ (UMPs) are represented using the `universal_packet` type. Technically a UMP is a sequence of 1 to 4 32 bit words in native endianness represented by an array of up to four 32 bit unsigned values (`uint32_t`). One can easily convert UMPs from one representation to another by copying the up to four data words from one C++ data structure to another. - struct universal_packet - { - uint32_t data[4]{ 0u, 0u, 0u, 0u }; +```cpp +struct universal_packet +{ + uint32_t data[4]{ 0u, 0u, 0u, 0u }; - packet_type type() const; - size_t size() const; + packet_type type() const; + size_t size() const; - group_t group() const { return ((data[0] >> 24u) & 0x0F); } - status_t status() const { return byte2(); } + group_t group() const { return ((data[0] >> 24u) & 0x0F); } + status_t status() const { return byte2(); } - uint8_t byte2() const { return ((data[0] >> 16u) & 0xFF); } - uint8_t byte3() const { return ((data[0] >> 8u) & 0xFF); } - uint8_t byte4() const { return (data[0] & 0xFF); } + uint8_t byte2() const { return ((data[0] >> 16u) & 0xFF); } + uint8_t byte3() const { return ((data[0] >> 8u) & 0xFF); } + uint8_t byte4() const { return (data[0] & 0xFF); } - uint8_t get_byte(size_t b) const; - uint7_t get_byte_7bit(size_t b) const { return get_byte(b) & 0x7F; } + uint8_t get_byte(size_t b) const; + uint7_t get_byte_7bit(size_t b) const { return get_byte(b) & 0x7F; } - bool has_channel() const; - channel_t channel() const; + bool has_channel() const; + channel_t channel() const; - void set_type(packet_type); - void set_group(group_t); - void set_byte(size_t, uint8_t); - void set_byte_7bit(size_t b, uint8_t v) { set_byte(b, v & 0x7F); } + void set_type(packet_type); + void set_group(group_t); + void set_byte(size_t, uint8_t); + void set_byte_7bit(size_t b, uint8_t v) { set_byte(b, v & 0x7F); } - universal_packet(uint32_t w) { data[0] = w; } - universal_packet(uint32_t w1, uint32_t w2); - universal_packet(uint32_t w1, uint32_t w2, uint32_t w3); - universal_packet(uint32_t w1, uint32_t w2, uint32_t w3, uint32_t w4); + universal_packet(uint32_t w) { data[0] = w; } + universal_packet(uint32_t w1, uint32_t w2); + universal_packet(uint32_t w1, uint32_t w2, uint32_t w3); + universal_packet(uint32_t w1, uint32_t w2, uint32_t w3, uint32_t w4); - bool is_utility_message() const; - bool is_system_message() const; - bool is_channel_voice_message() const; - bool is_data_message() const; + bool is_utility_message() const; + bool is_system_message() const; + bool is_channel_voice_message() const; + bool is_data_message() const; - bool is_midi1_protocol_message() const; - }; + bool is_midi1_protocol_message() const; +}; +``` -The `universal_packet` type provides accessors for `group`, `type`, `status`, `channel` (if applicable), individual data bytes and words. Please be aware that the `status` accessor in the `universal_packet` always provides the full status byte content, not masking the `channel` portion in Channel Voice Messages. Also be aware that some messages (like _Flex Data_) have additional status information in the packet. +The `universal_packet` type provides accessors for `group`, `type`, `status`, `channel` (if applicable), individual data bytes and words. Please be aware that the `status` accessor in the `universal_packet` always provides the full status byte content, not masking the `channel` portion in Channel Voice Messages. Also be aware that some messages (like _Flex Data_) have additional status information in the packet. There are convienience helpers available to check the `universal_packet` against a specific type. @@ -50,51 +52,57 @@ There are convienience helpers available to check the `universal_packet` against UMP packet types are represented by the `enum class packet_type`: - enum class packet_type : uint4_t { - utility = 0x0, - system = 0x1, - midi1_channel_voice = 0x2, - data = 0x3, - midi2_channel_voice = 0x4, - extended_data = 0x5, - - // introduced in UMP 1.1 - flex_data = 0xD, - stream = 0xF, - }; +```cpp +enum class packet_type : uint4_t { + utility = 0x0, + system = 0x1, + midi1_channel_voice = 0x2, + data = 0x3, + midi2_channel_voice = 0x4, + extended_data = 0x5, + + // introduced in UMP 1.1 + flex_data = 0xD, + stream = 0xF, +}; +``` ## Status The `universal_packet.h` header contains definitions for all currently known status bytes of the several message types. For status bytes with a channel component in the lower nibble the constants assume channel 0. -It is recommended to use the `status` members of the respective message data views to for comparisons. When using `universal_packet::status()` be aware to eventuelly mask the channel portion before comparing. +It is recommended to use the `status` members of the respective message data views to for comparisons. When using `universal_packet::status()` be aware to eventuelly mask the channel portion before comparing. ### Utility Messages `namespace utility_status` contains status definitions for all currently defined Utility messages. - namespace utility_status { - constexpr status_t noop = 0x00; - constexpr status_t jr_clock = 0x10; - constexpr status_t jr_timestamp = 0x20; - } // namespace utility_status +```cpp +namespace utility_status { + constexpr status_t noop = 0x00; + constexpr status_t jr_clock = 0x10; + constexpr status_t jr_timestamp = 0x20; +} // namespace utility_status +``` ### System Messages `namespace system_status` contains status definitions for all currently defined System messages. - namespace system_status { - constexpr status_t mtc_quarter_frame = 0xF1; - constexpr status_t song_position = 0xF2; - constexpr status_t song_select = 0xF3; - constexpr status_t tune_request = 0xF6; - constexpr status_t clock = 0xF8; - constexpr status_t start = 0xFA; - constexpr status_t cont = 0xFB; - constexpr status_t stop = 0xFC; - constexpr status_t active_sense = 0xFE; - constexpr status_t reset = 0xFF; - } // namespace system_status +```cpp +namespace system_status { + constexpr status_t mtc_quarter_frame = 0xF1; + constexpr status_t song_position = 0xF2; + constexpr status_t song_select = 0xF3; + constexpr status_t tune_request = 0xF6; + constexpr status_t clock = 0xF8; + constexpr status_t start = 0xFA; + constexpr status_t cont = 0xFB; + constexpr status_t stop = 0xFC; + constexpr status_t active_sense = 0xFE; + constexpr status_t reset = 0xFF; +} // namespace system_status +``` Please refer to [system_message.md](system_message.md) for detailed information on these messages. @@ -102,15 +110,17 @@ Please refer to [system_message.md](system_message.md) for detailed information `namespace midi1_channel_voice_status` provides status definitions for the Channel Voice Messages already available in MIDI 1. - namespace midi1_channel_voice_status { - constexpr status_t note_off = 0x80; - constexpr status_t note_on = 0x90; - constexpr status_t poly_pressure = 0xA0; - constexpr status_t control_change = 0xB0; - constexpr status_t program_change = 0xC0; - constexpr status_t channel_pressure = 0xD0; - constexpr status_t pitch_bend = 0xE0; - } // namespace midi1_channel_voice_status +```cpp +namespace midi1_channel_voice_status { + constexpr status_t note_off = 0x80; + constexpr status_t note_on = 0x90; + constexpr status_t poly_pressure = 0xA0; + constexpr status_t control_change = 0xB0; + constexpr status_t program_change = 0xC0; + constexpr status_t channel_pressure = 0xD0; + constexpr status_t pitch_bend = 0xE0; +} // namespace midi1_channel_voice_status +``` Please refer to [midi1_channel_voice_message.md](midi1_channel_voice_message.md) for detailed information on these messages. @@ -121,12 +131,14 @@ You may also look into [channel_voice_message.md](channel_voice_message.md) for Data Messages cover traditional MIDI 1 7 bit System Exclusive. UMP packet status definitions for these can be found in `namespace data_status`. - namespace data_status { - constexpr status_t sysex7_complete = (status_t(packet_format::complete) << 4); - constexpr status_t sysex7_start = (status_t(packet_format::start) << 4); - constexpr status_t sysex7_continue = (status_t(packet_format::cont) << 4); - constexpr status_t sysex7_end = (status_t(packet_format::end) << 4); - } // namespace data_status +```cpp +namespace data_status { + constexpr status_t sysex7_complete = (status_t(packet_format::complete) << 4); + constexpr status_t sysex7_start = (status_t(packet_format::start) << 4); + constexpr status_t sysex7_continue = (status_t(packet_format::cont) << 4); + constexpr status_t sysex7_end = (status_t(packet_format::end) << 4); +} // namespace data_status +``` Please refer to [data_message.md](data_message.md) for detailed information on these messages. @@ -136,27 +148,29 @@ Typically you may not deal with these messages directly, refer to [sysex_collect `namespace channel_voice_status` provides status definitions for all (MIDI 2) Channel Voice Messages including the High Resolution versions of Channel Voice Messages. - namespace channel_voice_status { - constexpr status_t registered_per_note_controller = 0x00; - constexpr status_t assignable_per_note_controller = 0x10; +```cpp +namespace channel_voice_status { + constexpr status_t registered_per_note_controller = 0x00; + constexpr status_t assignable_per_note_controller = 0x10; - constexpr status_t registered_controller = 0x20; - constexpr status_t assignable_controller = 0x30; + constexpr status_t registered_controller = 0x20; + constexpr status_t assignable_controller = 0x30; - constexpr status_t relative_registered_controller = 0x40; - constexpr status_t relative_assignable_controller = 0x50; + constexpr status_t relative_registered_controller = 0x40; + constexpr status_t relative_assignable_controller = 0x50; - constexpr status_t per_note_pitch_bend = 0x60; + constexpr status_t per_note_pitch_bend = 0x60; - constexpr status_t note_off = 0x80; - constexpr status_t note_on = 0x90; - constexpr status_t poly_pressure = 0xA0; - constexpr status_t control_change = 0xB0; - constexpr status_t program_change = 0xC0; - constexpr status_t channel_pressure = 0xD0; - constexpr status_t pitch_bend = 0xE0; - constexpr status_t per_note_management = 0xF0; - } // namespace channel_voice_status + constexpr status_t note_off = 0x80; + constexpr status_t note_on = 0x90; + constexpr status_t poly_pressure = 0xA0; + constexpr status_t control_change = 0xB0; + constexpr status_t program_change = 0xC0; + constexpr status_t channel_pressure = 0xD0; + constexpr status_t pitch_bend = 0xE0; + constexpr status_t per_note_management = 0xF0; +} // namespace channel_voice_status +``` Please refer to [midi2_channel_voice_message.md](midi2_channel_voice_message.md) for detailed information on these messages. @@ -166,15 +180,17 @@ You may also look into [channel_voice_message.md](channel_voice_message.md) for Extended Data Messages include the 8 bit version of System Exclusive and newly introduced Mixed Data Set Messages. UMP packet status definitions for these can be found in `namespace extended_data_status`. - namespace extended_data_status { - constexpr status_t sysex8_complete = (status_t(packet_format::complete) << 4); - constexpr status_t sysex8_start = (status_t(packet_format::start) << 4); - constexpr status_t sysex8_continue = (status_t(packet_format::cont) << 4); - constexpr status_t sysex8_end = (status_t(packet_format::end) << 4); +```cpp +namespace extended_data_status { + constexpr status_t sysex8_complete = (status_t(packet_format::complete) << 4); + constexpr status_t sysex8_start = (status_t(packet_format::start) << 4); + constexpr status_t sysex8_continue = (status_t(packet_format::cont) << 4); + constexpr status_t sysex8_end = (status_t(packet_format::end) << 4); - constexpr status_t mixed_data_set_header = 0x80; - constexpr status_t mixed_data_set_payload = 0x90; - } // namespace extended_data_status + constexpr status_t mixed_data_set_header = 0x80; + constexpr status_t mixed_data_set_payload = 0x90; +} // namespace extended_data_status +``` Please refer to [extended_data_message.md](extended_data_message.md) for detailed information on these messages. @@ -184,16 +200,20 @@ Typically you may not deal with these messages directly, refer to [sysex_collect Flex Data Messages come in four different formats, as defined in `packet_format`. - enum class packet_format : uint2_t { - complete = 0b00, //!< complete message - start = 0b01, //!< start of message - cont = 0b10, //!< continued message - end = 0b11 //!< end of message - }; +```cpp +enum class packet_format : uint2_t { + complete = 0b00, //!< complete message + start = 0b01, //!< start of message + cont = 0b10, //!< continued message + end = 0b11 //!< end of message +}; +``` Also, Flex data messages have a _packet address_ field: - enum class packet_address : uint2_t { channel = 0b00, group = 0b01 }; +```cpp +enum class packet_address : uint2_t { channel = 0b00, group = 0b01 }; +``` Please refer to [flex_data_message.md](flex_data_message.md) for detailed information on these messages. @@ -201,30 +221,34 @@ Please refer to [flex_data_message.md](flex_data_message.md) for detailed inform `namespace stream_status` contains status definitions for all currently defined Stream messages. - namespace stream_status { - constexpr status_t endpoint_discovery = 0x00; - constexpr status_t endpoint_info = 0x01; - constexpr status_t device_identity = 0x02; - constexpr status_t endpoint_name = 0x03; - constexpr status_t product_instance_id = 0x04; - constexpr status_t stream_configuration_request = 0x05; - constexpr status_t stream_configuration_notify = 0x06; - - constexpr status_t function_block_discovery = 0x10; - constexpr status_t function_block_info = 0x11; - constexpr status_t function_block_name = 0x12; - } // namespace stream_status +```cpp +namespace stream_status { + constexpr status_t endpoint_discovery = 0x00; + constexpr status_t endpoint_info = 0x01; + constexpr status_t device_identity = 0x02; + constexpr status_t endpoint_name = 0x03; + constexpr status_t product_instance_id = 0x04; + constexpr status_t stream_configuration_request = 0x05; + constexpr status_t stream_configuration_notify = 0x06; + + constexpr status_t function_block_discovery = 0x10; + constexpr status_t function_block_info = 0x11; + constexpr status_t function_block_name = 0x12; +} // namespace stream_status +``` Some Stream messages have `protocol` and `extensions` fields: - namespace protocol { - constexpr protocol_t midi1 = 0x1; - constexpr protocol_t midi2 = 0x2; - } // namespace protocol - - namespace extensions { - constexpr protocol_t jitter_reduction_transmit = 0x1; - constexpr protocol_t jitter_reduction_receive = 0x2; - } // namespace extensions +```cpp +namespace protocol { + constexpr protocol_t midi1 = 0x1; + constexpr protocol_t midi2 = 0x2; +} // namespace protocol + +namespace extensions { + constexpr protocol_t jitter_reduction_transmit = 0x1; + constexpr protocol_t jitter_reduction_receive = 0x2; +} // namespace extensions +``` Please refer to [stream_message.md](stream_message.md) for detailed information on these messages. diff --git a/docs/utility_message.md b/docs/utility_message.md index efa7012..5ee9da3 100644 --- a/docs/utility_message.md +++ b/docs/utility_message.md @@ -4,25 +4,31 @@ Utility Messages are represented by a - struct utility_message : universal_packet - { - utility_message(status_t, uint16_t payload = 0u); +```cpp +struct utility_message : universal_packet +{ + utility_message(status_t, uint16_t payload = 0u); - // utility messages are groupless since UMP 1.1 - constexpr group_t group() const = delete; - constexpr void set_group(group_t) = delete; - }; + // utility messages are groupless since UMP 1.1 + constexpr group_t group() const = delete; + constexpr void set_group(group_t) = delete; +}; +``` As Utility messages are groupless the `group` accessors are deleted. Instead of using `utility_message` constructors one can create messages using factory functions: - constexpr utility_message make_utility_message(status_t, uint16_t data); +```cpp +constexpr utility_message make_utility_message(status_t, uint16_t data); +``` -Filtering of Utility Messages can be done checking `universal_packet::type()` against +Filtering of _Utility Messages_ can be done checking `universal_packet::type()` against `packet_type::utility` or use - bool universal_packet::is_utility_message(); +```cpp +bool universal_packet::is_utility_message(); +``` Currently the only use case for Utility messages are [Jitter Reduction Timestamps](../inc/midi/jitter_reduction_timestamps.h). @@ -31,10 +37,12 @@ Currently the only use case for Utility messages are [Jitter Reduction Timestamp Once validated against `packet_type::utility` one can create a `utility_message_view` for a `universal_packet`: - struct utility_message_view - { - utility_message_view(const universal_packet&); +```cpp +struct utility_message_view +{ + utility_message_view(const universal_packet&); - constexpr status_t status() const; - constexpr uint16_t payload() const; - }; + constexpr status_t status() const; + constexpr uint16_t payload() const; +}; +``` diff --git a/inc/midi/flex_data_message.h b/inc/midi/flex_data_message.h index 5c146be..1e037f2 100644 --- a/inc/midi/flex_data_message.h +++ b/inc/midi/flex_data_message.h @@ -79,6 +79,18 @@ struct flex_data_message_view constexpr uint32_t data1() const { return p.data[1]; } constexpr uint32_t data2() const { return p.data[2]; } constexpr uint32_t data3() const { return p.data[3]; } + constexpr uint8_t data_byte1() const { return p.get_byte(4); } + constexpr uint8_t data_byte2() const { return p.get_byte(5); } + constexpr uint8_t data_byte3() const { return p.get_byte(6); } + constexpr uint8_t data_byte4() const { return p.get_byte(7); } + constexpr uint8_t data_byte5() const { return p.get_byte(8); } + constexpr uint8_t data_byte6() const { return p.get_byte(9); } + constexpr uint8_t data_byte7() const { return p.get_byte(10); } + constexpr uint8_t data_byte8() const { return p.get_byte(11); } + constexpr uint8_t data_byte9() const { return p.get_byte(12); } + constexpr uint8_t data_byte10() const { return p.get_byte(13); } + constexpr uint8_t data_byte11() const { return p.get_byte(14); } + constexpr uint8_t data_byte12() const { return p.get_byte(15); } const std::string payload_as_string() const { return flex_data_message::payload_as_string(p); } diff --git a/tests/flex_data_message_tests.cpp b/tests/flex_data_message_tests.cpp index 95aac09..29fb389 100644 --- a/tests/flex_data_message_tests.cpp +++ b/tests/flex_data_message_tests.cpp @@ -139,6 +139,18 @@ TEST_F(flex_data_message, flex_data_message_view) EXPECT_EQ(0x12345678u, v.data1()); EXPECT_EQ(0x9ABCDEF0u, v.data2()); EXPECT_EQ(0xA9876543u, v.data3()); + EXPECT_EQ(0x12u, v.data_byte1()); + EXPECT_EQ(0x34u, v.data_byte2()); + EXPECT_EQ(0x56u, v.data_byte3()); + EXPECT_EQ(0x78u, v.data_byte4()); + EXPECT_EQ(0x9Au, v.data_byte5()); + EXPECT_EQ(0xBCu, v.data_byte6()); + EXPECT_EQ(0xDEu, v.data_byte7()); + EXPECT_EQ(0xF0u, v.data_byte8()); + EXPECT_EQ(0xA9u, v.data_byte9()); + EXPECT_EQ(0x87u, v.data_byte10()); + EXPECT_EQ(0x65u, v.data_byte11()); + EXPECT_EQ(0x43u, v.data_byte12()); } { @@ -157,6 +169,9 @@ TEST_F(flex_data_message, flex_data_message_view) EXPECT_EQ(0xAu, v.data1()); EXPECT_EQ(0xBu, v.data2()); EXPECT_EQ(0xCu, v.data3()); + EXPECT_EQ(0xAu, v.data_byte4()); + EXPECT_EQ(0xBu, v.data_byte8()); + EXPECT_EQ(0xCu, v.data_byte12()); } { @@ -175,6 +190,9 @@ TEST_F(flex_data_message, flex_data_message_view) EXPECT_EQ(7u, v.data1()); EXPECT_EQ(8u, v.data2()); EXPECT_EQ(9u, v.data3()); + EXPECT_EQ(7u, v.data_byte4()); + EXPECT_EQ(8u, v.data_byte8()); + EXPECT_EQ(9u, v.data_byte12()); } {