| 55 | }; |
| 56 | |
| 57 | int main() |
| 58 | { |
| 59 | #if defined(_WIN32) && __has_include(<winrt/base.h>) |
| 60 | // Necessary for using WinUWP and WinMIDI, must be done as early as possible in your main() |
| 61 | winrt::init_apartment(); |
| 62 | #endif |
| 63 | |
| 64 | using namespace std::literals; |
| 65 | using clk = std::chrono::steady_clock; |
| 66 | |
| 67 | const int num_threads = 20; |
| 68 | |
| 69 | // Open our midi output |
| 70 | libremidi::observer obs; |
| 71 | auto ports = obs.get_output_ports(); |
| 72 | if (ports.size() == 0) |
| 73 | { |
| 74 | std::cerr << "No MIDI outputs are available" << std::endl; |
| 75 | std::exit(1); |
| 76 | } |
| 77 | libremidi::midi_out output; |
| 78 | output.open_port(ports.front()); |
| 79 | |
| 80 | // Create a message queue for communication |
| 81 | threadsafe_queue<libremidi::message> queue; |
| 82 | auto midi_writer = [&](std::stop_token stoken) { |
| 83 | while (!stoken.stop_requested()) |
| 84 | { |
| 85 | queue.try_enqueue({176, 7, (unsigned char)(rand())}); |
| 86 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); |
| 87 | } |
| 88 | }; |
| 89 | |
| 90 | // Create producer threads |
| 91 | std::vector<std::jthread> output_threads; |
| 92 | for (int i = 0; i < 20; i++) |
| 93 | output_threads.emplace_back(midi_writer); |
| 94 | |
| 95 | // Consume the produced messages |
| 96 | auto t0 = clk::now(); |
| 97 | |
| 98 | int k = 0; |
| 99 | libremidi::message msg; |
| 100 | while ((clk::now() - t0) < 5s) |
| 101 | { |
| 102 | if (queue.try_dequeue(msg)) |
| 103 | { |
| 104 | output.send_message(msg); |
| 105 | k++; |
| 106 | } |
| 107 | } |
| 108 | output_threads.clear(); |
| 109 | |
| 110 | // Process remaining messages - most of the time is actually spent inside OS APIs for sending MIDI messages, |
| 111 | // and those aren't thread-safe so no way out of doing it that way. |
| 112 | while (queue.try_dequeue(msg)) |
| 113 | { |
| 114 | k++; |
nothing calls this directly
no test coverage detected