From 923f039333120d52f18106d4535f5f9e217c3e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Wed, 11 Sep 2024 15:01:54 -0400 Subject: [PATCH] [docs] Doc & test improvements --- book/src/SUMMARY.md | 1 + book/src/backends.md | 8 ++-- book/src/compiling.md | 2 +- book/src/configuration.md | 33 ++++++++++++++ book/src/context-sharing.md | 2 + book/src/foreword.md | 1 + book/src/keyboard.md | 32 ++++++++++++++ book/src/queue.md | 4 ++ tests/unit/midi_in.cpp | 85 ++++++++++++++++++++++++++++++++++++- 9 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 book/src/keyboard.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 1df2508..ab96fa7 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -20,6 +20,7 @@ - [External polling](./polling.md) - [Timestamping](./timestamping.md) - [Queue vs callbacks](./queue.md) +- [Computer keyboard input](./keyboard.md) # Reference - [Backends](./backends.md) diff --git a/book/src/backends.md b/book/src/backends.md index 1e251bf..432a195 100644 --- a/book/src/backends.md +++ b/book/src/backends.md @@ -11,7 +11,7 @@ or because it has not been implemented yet. | | ALSA Raw | ALSA Seq | PipeWire | |---------------|----------|----------|----------| | MIDI 1 | Yes | Yes | Yes | -| MIDI 2 | Yes | Yes | No | +| MIDI 2 | Yes | Yes | N/A | | Virtual ports | N/A | Yes | Yes | | Observer | Yes | Yes | Yes | | Scheduling | No | No | No | @@ -32,9 +32,9 @@ without preventing application loading if the end user does not use it. | | WinMM | UWP | WinMIDI | |---------------|-------|-----|---------| | MIDI 1 | Yes | Yes | No | -| MIDI 2 | N/A | N/A | No | +| MIDI 2 | N/A | N/A | Yes | | Virtual ports | N/A | No | No | -| Observer | Yes | Yes | No | +| Observer | Yes | Yes | Yes | | Scheduling | No | No | No | ## Mac & iOS @@ -62,7 +62,7 @@ without preventing application loading if the end user does not use it. | | JACK | |---------------|------| | MIDI 1 | Yes | -| MIDI 2 | No | +| MIDI 2 | N/A | | Virtual ports | Yes | | Observer | Yes | | Scheduling | No | diff --git a/book/src/compiling.md b/book/src/compiling.md index 043243e..487fca4 100644 --- a/book/src/compiling.md +++ b/book/src/compiling.md @@ -53,4 +53,4 @@ e.g. `-DCMAKE_OSX_DEPLOYMENT_TARGET=11` or more recent needs to be passed to CMa ## Advanced features -- Ability to set a fixed message size for zero-allocation scenarios, with -DLIBREMIDI_SLIM_MESSAGE= (in CMake or directly to the compiler) +- For MIDI 1: ability to set a fixed message size upper-bound for zero-allocation scenarios, with `-DLIBREMIDI_SLIM_MESSAGE=` (in CMake or directly to the compiler) diff --git a/book/src/configuration.md b/book/src/configuration.md index 141c428..998d35f 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -2,8 +2,41 @@ The `midi_in`, `midi_out` and `midi_observer` objects are configured through a `input_configuration` (resp. `output_`, etc.) object passed in argument to the constructor. + +Example: + +```cpp +#include + +... + +libremidi::midi_in in{ + libremidi::input_configuration{ + .on_message = ... + , .ignore_sysex = false + , .ignore_sensing = true + } +}; +``` + ## Custom back-end configuration Additionnally, each back-end supports back-end specific configuration options, to enable users to tap into advanced features of a given API while retaining the general C++ abstraction. For instance, this enables to set output buffer sizes, chunking parameters, etc. for back-ends which support the feature. + + +```cpp +#include + +... + +libremidi::midi_in in{ + libremidi::input_configuration{ + .on_message = ... + }, + libremidi::pipewire_input_configuration{ + .client_name = "My app" + } +}; +``` diff --git a/book/src/context-sharing.md b/book/src/context-sharing.md index 0c8f682..b296311 100644 --- a/book/src/context-sharing.md +++ b/book/src/context-sharing.md @@ -39,6 +39,8 @@ libremidi::midi_out out{ }; ``` +In that case, note that the `obs` has ownership of for instance the JACK context object: it must outlive `in` and `out`. + The relevant examples are: - `coremidi_share.cpp` for a complete example for CoreMIDI. - `jack_share.cpp` for a complete example for JACK. diff --git a/book/src/foreword.md b/book/src/foreword.md index 6739eb2..fc3797b 100644 --- a/book/src/foreword.md +++ b/book/src/foreword.md @@ -27,3 +27,4 @@ It also features some new & improved backends: - Windows UWP. - WebMIDI in Emscripten. - JACK support on all platforms where it is available. +- Computer keyboard input. diff --git a/book/src/keyboard.md b/book/src/keyboard.md new file mode 100644 index 0000000..b97d663 --- /dev/null +++ b/book/src/keyboard.md @@ -0,0 +1,32 @@ +# Computer keyboard input + +This backend allwos to use the computer keys to play MIDI. +The backend does not directly read the key events as this would require making the library much more complex. +Instead, it provides a callback that you can plug into your favourite GUI toolkit to process scan codes. + +The mapping is customizable. By default: + +``` + ,---,---,---,---,---,---,---,---,---,---,---,---,---,-------, + | V0| V1| V2| V3| V4| V5| V6| V7| V8| V9|V10|V11|V12| <- | + |---'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-----| + | ->| | | C#| D#| | F#| G#| A#| | C#| D#| | F#| | + |-----',--',--',--',--',--',--',--',--',--',--',--',--'| | + | Caps | C | D | E | F | G | A | B | C | D | E | F | G | | + |----,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'-,-'---'----| + | -^ | | O-| O+| V-| V+| | | | | | | ----^ | + |----'-,-',--'--,'---'---'---'---'---'---'-,-'---',--,------| + | ctrl | | alt | |altgr | | ctrl | + '------' '-----'--------------------------'------' '------' +``` + +Where V0 to V12 set the velocity between 0 and 127 in steps of ~10, O- / O+ increase or decrease the octave and V- / V+ increase or decrease the velocity by 10. + + +Example: +``` +#include + +libremidi::kbd_input_configuration api_conf; +api_conf.set_input_scancode_callbacks( +[] ) diff --git a/book/src/queue.md b/book/src/queue.md index 2d95876..122a218 100644 --- a/book/src/queue.md +++ b/book/src/queue.md @@ -3,3 +3,7 @@ The old queued input mechanism present in RtMidi and previous versions of the library has been moved out of the code as it can be built entirely on the callback mechanism and integrated with the user application's event processing queue instead. A basic example is provided in `qmidiin.cpp`. + +We recommend if possible to instead use an asynchronous runtime in order to keep an imperative behaviour while benefiting from the non-blocking properties of async programming. + +An example based on C++20 coroutines with Boost.Cobalt is provided in `coroutines.cpp`. diff --git a/tests/unit/midi_in.cpp b/tests/unit/midi_in.cpp index f0b4de6..785eb61 100644 --- a/tests/unit/midi_in.cpp +++ b/tests/unit/midi_in.cpp @@ -1,15 +1,98 @@ #include "../include_catch.hpp" +#include #include #include #include #if __has_include() + #include + #include #endif -#include +TEST_CASE("creation", "[midi_in]") +{ + GIVEN("A default midi input") + { + libremidi::midi_in in(libremidi::input_configuration{.on_message = [](auto) {}}); + THEN("created with the default MIDI 1 api for the platform") + { + REQUIRE(in.get_current_api() == libremidi::midi1::default_api()); + } + } + + GIVEN("A default ump input") + { + libremidi::ump_input_configuration conf{.on_message = [](auto) {}}; + libremidi::midi_in in(conf); + THEN("created with the default MIDI 2 api for the platform") + { + REQUIRE(in.get_current_api() == libremidi::midi2::default_api()); + } + } + + GIVEN("A midi input with an explicitly unspecified API") + { + libremidi::midi_in in( + libremidi::input_configuration{.on_message = [](auto) {}}, libremidi::API::UNSPECIFIED); + THEN("created with the default api") + { + REQUIRE(in.get_current_api() == libremidi::midi1::default_api()); + } + } + + GIVEN("A midi input with an empty API") + { + libremidi::midi_in in(libremidi::input_configuration{.on_message = [](auto) {}}, std::any{}); + THEN("created with defaultapi") + { + REQUIRE(in.get_current_api() == libremidi::midi1::default_api()); + } + } + GIVEN("A midi input with an explicit API") + { + libremidi::midi_in in( + libremidi::input_configuration{.on_message = [](auto) {}}, libremidi::API::KEYBOARD); + THEN("created with that api") + { + REQUIRE(in.get_current_api() == libremidi::API::KEYBOARD); + } + } + + GIVEN("A midi input with a wrong API") + { + libremidi::midi_in in(libremidi::input_configuration{.on_message = [](auto) {}}, float(1.23f)); + THEN("created with dummy api") + { + REQUIRE(in.get_current_api() == libremidi::API::DUMMY); + } + } + + GIVEN("A midi input with a proper API") + { + libremidi::midi_in in( + libremidi::input_configuration{.on_message = [](auto) {}}, + libremidi::kbd_input_configuration{}); + THEN("created with the correct api") + { + REQUIRE(in.get_current_api() == libremidi::API::KEYBOARD); + } + } + + GIVEN("A midi 2 input with a proper midi1 API") + { + libremidi::midi_in in( + libremidi::ump_input_configuration{.on_message = [](auto) {}}, + libremidi::kbd_input_configuration{}); + THEN("created with the correct api") + { + REQUIRE(in.get_current_api() == libremidi::API::KEYBOARD); + } + } +} + TEST_CASE("poly aftertouch", "[midi_in]") { #if __has_include()