Skip to content

Commit a3d0acf

Browse files
committed
improve documentation and add example code (part 1)
1 parent 05ce9b1 commit a3d0acf

33 files changed

+2726
-7
lines changed

CMakeLists.txt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ endif()
1212

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

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

128129
nimidi2_add_test(ni-midi2-tests GTEST)
129130
endif(NIMIDI2_TESTS)
131+
132+
if( NIMIDI2_EXAMPLES )
133+
set(ExampleSources
134+
docs/docs.cpp
135+
docs/channel_voice_message.examples.cpp
136+
docs/data_message.examples.cpp
137+
docs/extended_data_message.examples.cpp
138+
docs/flex_data_message.examples.cpp
139+
docs/midi1_byte_stream.examples.cpp
140+
docs/midi1_channel_voice_message.examples.cpp
141+
docs/midi2_channel_voice_message.examples.cpp
142+
docs/stream_message.examples.cpp
143+
docs/sysex_collector.examples.cpp
144+
docs/sysex.examples.cpp
145+
docs/system_message.examples.cpp
146+
docs/types.examples.cpp
147+
docs/universal_sysex.examples.cpp
148+
docs/utility_message.examples.cpp
149+
)
150+
151+
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${ExampleSources})
152+
153+
add_executable(ni-midi2-examples ${ExampleSources})
154+
target_link_libraries(ni-midi2-examples PRIVATE ni::midi2)
155+
endif(NIMIDI2_EXAMPLES)

README.md

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Mathematical operators allow to do integer / fixed point math on pitches and con
5555
auto from32bit = controller_value{ uint32_t{ 0x45883312 } };
5656
auto fromFloat = pitch_7_25 { 66.1f };
5757

58+
For more information see [Common Types and Scaling Helpers](docs/types.md).
59+
5860
Conrete instances of packets or messages are created using factory functions.
5961

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

78-
midi2_channel_voice_message make_midi2_note_on_message(group_t, status_t, note_nr_t, velocity);
79-
midi2_channel_voice_message make_midi2_note_on_message(group_t, status_t, note_nr_t, velocity, pitch_7_9);
80+
midi2_channel_voice_message make_midi2_note_on_message(group_t, channel_t, note_nr_t, velocity);
81+
midi2_channel_voice_message make_midi2_note_on_message(group_t, channel_t, note_nr_t, velocity, pitch_7_9);
8082
midi2_channel_voice_message make_registered_controller_message(
8183
group_t, channel_t, uint7_t bank, uint7_t index, controller_value);
8284
...
@@ -125,9 +127,9 @@ Alternatively one can use `std::optional` to check and cast in a single statemen
125127
if (auto m = as_system_message_view(packet))
126128
{
127129
// access message data
128-
if (m.status() == system_status::song_position)
130+
if (m->status() == system_status::song_position)
129131
{
130-
auto pos = m.get_song_position();
132+
auto pos = m->get_song_position();
131133
}
132134
}
133135

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

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

183+
For more information see [midi1_byte_stream.md](docs/midi1_byte_stream.md).
184+
181185
### Sysex collectors
182186

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

198202
sysex8_collector c {
199-
[](const sysex8 &s)
203+
[](const sysex8 &s, uint8_t stream_id)
200204
{
201205
// do something with message
202206
...
@@ -206,6 +210,7 @@ The `sysex7_collector` class allows to easily collect System Exclusive messages
206210
if (is_sysex8_packet(p))
207211
c.feed(p);
208212

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

210215
### Jitter Reduction Timestamps
211216

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

221226
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.
222227

228+
## Detail documentation and example files
229+
230+
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.
231+
232+
* Universal MIDI Packet [documentation](docs/universal_packet.md)
233+
* Common Types and Scaling Helpers [documentation](docs/types.md) and [examples](docs/types.examples.cpp)
234+
* Protocol Agnostic Channel Voice Message helper [documentation](docs/channel_voice_message.md) and [examples](docs/channel_voice_message.examples.cpp)
235+
* MIDI 1 Channel Voice Message [documentation](docs/midi1_channel_voice_message.md) and [examples](docs/midi1_channel_voice_message.examples.cpp)
236+
* MIDI 2 Channel Voice Message [documentation](docs/midi2_channel_voice_message.md) and [examples](docs/midi2_channel_voice_message.examples.cpp)
237+
* System Message [documentation](docs/system_message.md) and [examples](docs/system_message.examples.cpp)
238+
* Data Message [documentation](docs/data_message.md) and [examples](docs/data_message.examples.cpp)
239+
* Extended Data Message [documentation](docs/extended_data_message.md) and [examples](docs/extended_data_message.examples.cpp)
240+
* Flex Data Message [WIP documentation](docs/flex_data_message.md)
241+
* Stream Message [WIP documentation](docs/stream_message.md)
242+
* Utility Message [documentation](docs/utility_message.md)
243+
* MIDI 1 Byte Stream Helper [WIP documentation](docs/midi1_byte_stream.md) and [examples](docs/midi1_byte_stream.examples.cpp)
244+
245+
Until documentation is completed you may also find the [unit test code](tests) helpful.
246+
223247
## Building and Testing
224248

225249
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`.
@@ -229,6 +253,7 @@ There are some `cmake options` that allow customization of what is build and how
229253
option( NIMIDI2_TREAT_WARNINGS_AS_ERRORS "Treat compile warnings as errors" OFF )
230254
option( NIMIDI2_UNITY_BUILDS "Build ni-midi2 with unity builds" ON )
231255
option( NIMIDI2_TESTS "Build ni-midi2 Tests" ${IS_NIMIDI2} )
256+
option( NIMIDI2_EXAMPLES "Build ni-midi2 examples" ${IS_NIMIDI2} )
232257

233258
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.
234259

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#include <midi/channel_voice_message.h>
2+
3+
#include <cassert>
4+
5+
void note_on_examples()
6+
{
7+
using namespace midi;
8+
9+
const note_nr_t note_nr{ 99 };
10+
const velocity vel{ 0.9876 };
11+
const pitch_7_9 default_pitch{ note_nr };
12+
const pitch_7_9 custom_pitch{ 99.02 };
13+
14+
auto m1 = make_midi1_note_on_message(0, 0, note_nr, vel);
15+
auto m2 = make_midi2_note_on_message(0, 0, note_nr, vel);
16+
auto m3 = make_midi2_note_on_message(0, 0, note_nr, vel, custom_pitch);
17+
auto m4 = make_midi2_note_on_message(0, 0, note_nr, vel, uint8_t{ 42 }, uint16_t{ 1234 });
18+
19+
assert(is_note_on_message(m1));
20+
assert(is_note_on_message(m2));
21+
assert(is_note_on_message(m3));
22+
assert(is_note_on_message(m4));
23+
24+
assert(get_note_nr(m1) == note_nr);
25+
assert(get_note_nr(m2) == note_nr);
26+
assert(get_note_nr(m3) == note_nr);
27+
assert(get_note_nr(m4) == note_nr);
28+
assert(get_note_velocity(m1) == velocity{ vel.as_uint7() });
29+
assert(get_note_velocity(m2) == vel);
30+
assert(get_note_velocity(m3) == vel);
31+
assert(get_note_velocity(m4) == vel);
32+
assert(get_note_pitch(m1) == default_pitch);
33+
assert(get_note_pitch(m2) == default_pitch);
34+
assert(get_note_pitch(m3) == custom_pitch);
35+
assert(get_note_pitch(m4) == default_pitch);
36+
}
37+
38+
void note_off_examples()
39+
{
40+
using namespace midi;
41+
42+
const note_nr_t note_nr{ 71 };
43+
const velocity vel{ 0.1 };
44+
45+
auto m1 = make_midi1_note_off_message(0, 0, note_nr, vel);
46+
auto m2 = make_midi2_note_off_message(0, 0, note_nr, vel);
47+
auto m3 = make_midi1_note_on_message(0, 0, note_nr, velocity{ uint7_t{ 0 } });
48+
49+
assert(is_note_off_message(m1));
50+
assert(is_note_off_message(m2));
51+
assert(is_note_off_message(m3));
52+
assert(get_note_nr(m1) == note_nr);
53+
assert(get_note_nr(m2) == note_nr);
54+
assert(get_note_nr(m3) == note_nr);
55+
assert(get_note_velocity(m1) == velocity{ vel.as_uint7() });
56+
assert(get_note_velocity(m2) == vel);
57+
assert(get_note_velocity(m3) == velocity{ uint7_t{ 64 } });
58+
}
59+
60+
void poly_pressure_examples()
61+
{
62+
using namespace midi;
63+
64+
note_nr_t note_nr{ 34 };
65+
controller_value pressure{ 0.75 };
66+
67+
auto m1 = make_midi1_poly_pressure_message(0, 0, note_nr, pressure);
68+
auto m2 = make_midi2_poly_pressure_message(0, 0, note_nr, pressure);
69+
70+
assert(is_poly_pressure_message(m1));
71+
assert(is_poly_pressure_message(m2));
72+
73+
assert(get_note_nr(m1) == note_nr);
74+
assert(get_note_nr(m2) == note_nr);
75+
76+
assert(get_poly_pressure_value(m2) == pressure);
77+
assert(get_poly_pressure_value(m1) == controller_value{ pressure.as_uint7() });
78+
}
79+
80+
void control_change_examples()
81+
{
82+
using namespace midi;
83+
84+
controller_t ctrl{ control_change::brightness };
85+
controller_value val{ uint32_t{ 0x41221892 } };
86+
87+
auto m1 = make_midi1_control_change_message(0, 0, ctrl, val);
88+
auto m2 = make_midi2_control_change_message(0, 0, ctrl, val);
89+
90+
assert(is_control_change_message(m1));
91+
assert(is_control_change_message(m2));
92+
93+
assert(get_controller_nr(m1) == ctrl);
94+
assert(get_controller_nr(m2) == ctrl);
95+
96+
assert(get_controller_value(m2) == val);
97+
assert(get_controller_value(m1) == controller_value{ val.as_uint7() });
98+
}
99+
100+
void program_change_examples()
101+
{
102+
using namespace midi;
103+
104+
program_t program{ 7 };
105+
106+
// TODO: demonstrate MIDI2 bank
107+
auto m1 = make_midi1_program_change_message(0, 0, program);
108+
auto m2 = make_midi2_program_change_message(0, 0, program);
109+
110+
assert(is_program_change_message(m1));
111+
assert(is_program_change_message(m2));
112+
113+
assert(get_program_value(m1) == program);
114+
assert(get_program_value(m2) == program);
115+
}
116+
117+
void channel_pressure_examples()
118+
{
119+
using namespace midi;
120+
121+
controller_value pressure{ 0.75 };
122+
123+
auto m1 = make_midi1_channel_pressure_message(0, 0, pressure);
124+
auto m2 = make_midi2_channel_pressure_message(0, 0, pressure);
125+
126+
assert(is_channel_pressure_message(m1));
127+
assert(is_channel_pressure_message(m2));
128+
129+
assert(get_channel_pressure_value(m2) == pressure);
130+
assert(get_channel_pressure_value(m1) == controller_value{ pressure.as_uint7() });
131+
}
132+
133+
void channel_pitch_bend_examples()
134+
{
135+
using namespace midi;
136+
137+
pitch_bend pb{ -0.04 };
138+
139+
auto m1 = make_midi1_pitch_bend_message(0, 0, pb);
140+
auto m2 = make_midi2_pitch_bend_message(0, 0, pb);
141+
142+
assert(is_channel_pitch_bend_message(m1));
143+
assert(is_channel_pitch_bend_message(m2));
144+
145+
assert(get_channel_pitch_bend_value(m2) == pb);
146+
assert(get_channel_pitch_bend_value(m1) == pitch_bend{ pb.as_uint14() });
147+
}
148+
149+
void channel_voice_message_processor(const midi::universal_packet& p)
150+
{
151+
using namespace midi;
152+
153+
auto allocate_voice = [](note_nr_t, velocity, pitch_7_9) {};
154+
auto release_voice = [](note_nr_t, velocity) {};
155+
auto voice_pressure = [](note_nr_t, controller_value) {};
156+
auto voice_controller = [](note_nr_t, uint8_t, controller_value) {};
157+
auto voice_pitch_bend = [](note_nr_t, pitch_bend) {};
158+
auto channel_controller = [](controller_t, controller_value) {};
159+
auto channel_pitch_bend = [](pitch_bend) {};
160+
161+
if (is_note_on_message(p))
162+
{
163+
allocate_voice(get_note_nr(p), get_note_velocity(p), get_note_pitch(p));
164+
}
165+
else if (is_note_off_message(p))
166+
{
167+
release_voice(get_note_nr(p), get_note_velocity(p));
168+
}
169+
else if (is_poly_pressure_message(p))
170+
{
171+
voice_pressure(get_note_nr(p), get_poly_pressure_value(p));
172+
}
173+
else if (is_registered_per_note_controller_message(p))
174+
{
175+
voice_controller(get_note_nr(p), get_per_note_controller_index(p), get_controller_value(p));
176+
}
177+
else if (is_per_note_pitch_bend_message(p))
178+
{
179+
voice_pitch_bend(get_note_nr(p), get_per_note_pitch_bend_value(p));
180+
}
181+
else if (is_control_change_message(p))
182+
{
183+
channel_controller(get_controller_nr(p), get_controller_value(p));
184+
}
185+
else if (is_channel_pitch_bend_message(p))
186+
{
187+
channel_pitch_bend(get_channel_pitch_bend_value(p));
188+
}
189+
}
190+
191+
void run_channel_voice_message_examples()
192+
{
193+
note_on_examples();
194+
note_off_examples();
195+
poly_pressure_examples();
196+
control_change_examples();
197+
program_change_examples();
198+
channel_pressure_examples();
199+
channel_pitch_bend_examples();
200+
channel_voice_message_processor(midi::universal_packet{ 0x20901234 });
201+
}

0 commit comments

Comments
 (0)