Giter Site home page Giter Site logo

jcelerier / libremidi Goto Github PK

View Code? Open in Web Editor NEW
415.0 19.0 47.0 12.67 MB

A modern C++ MIDI 1 / MIDI 2 real-time & file I/O library. Supports Windows, macOS, Linux and WebMIDI.

License: Other

CMake 3.18% C++ 86.77% JavaScript 9.15% C 0.90%
midi uwp jackaudio alsa jack emscripten webmidi coremidi cpp20 midi-api midi2

libremidi's Introduction

libremidi

Build status

Packaging status

libremidi is a cross-platform C++20 library for real-time and MIDI file input and output.

This is a fork / rewrite based on two libraries:

Additionnally, for MIDI 2 parsing support we use cmidi2!

Read the documentation here.

Changelog

Since v5

libremidi finally supports MIDI 2 on all desktop platforms 🎉!

Since v4.5

  • Input logic refactored across all backends.
    • e.g. previously not backends had the same rules wrt timestamping, sysexes, etc. Now there is a single MIDI state machine which processes this.
  • PipeWire support.
  • Many bugfixes across the stack.

Since v4.4

  • iOS support restored (thanks @fwcd)
  • CI work: added Debian Bullseye, Bookworm, Trixie and make sure UMP code is being built.
  • Add compatibility with ni-midi2: the libremidi::ump type will convert automatically from / to midi::universal_packet and it is possible to send directly some ni-midi2 data types through libremidi::midi_out.
  • Added an example of very basic MIDI-CI interoperation with MIDI2.0Workbench: https://github.com/jcelerier/libremidi/blob/master/examples/midi2_interop.cpp
  • Observer: add a track_any flag to track MIDI ports that are not reported as being hardware or software.
  • UMP: allow send_ump to handle UMP streams, not only single UMP packets.

Since v4.3

  • Improvements to timing handling.
    • Added a Custom timestamping mechanism which allows the user to provide a custom callback to run timestamping as close as possible to the event's reception.
    • Added midi_in::absolute_timestamp() to get the origin timestamp for driver-provided ticks as accurately as possible.
      • e.g. in practice this is taking the time just near the ALSA queue creation or WinMM MIDI open.
    • Bugfixes in JACK
    • Many warning fixes - thanks @lilggamegenius for the extensive work!
    • Fix MIDI dump example - thanks @chdiesch!
    • Add SOVERSION to dynamic library

Since v4.2

  • More robust MIDI 2.0 support.
    • On macOS through CoreMIDI (input / output, requires macOS 11+).
    • On Linux through ALSA sequencer API (input / output, requires kernel 6.5+ and latest libasound).
    • The API can be used through MIDI 1 or MIDI 2 affordances, e. g. one can send UMP packets to a MIDI 1 API, they will get converted automatically.
    • More backends to come, in particular with the new Windows MIDI Services started here!
    • Sysex handling on MIDI 2.0 is the responsibility of the user of the library, which simplifies the design immensely and allows the library to be used in stricter real-time contexts (as an UMP message has a fixed size, unlike MIDI 1 sysex which required potentially unbounded dynamic allocations with the previous design).
    • See midi_echo.cpp for a complete example.
  • Linux: libasound and udev are now entirely loaded at run-time through dlopen. This is necessary for making apps that will run on older Linux versions which do not have the ALSA UMP APIs yet. Note that to make an app which supports MIDI 2 on recent Linuxes and still runs on older ones, you will need to use the latest ALSA library headers as part of your build on an older distribution, by building alsa-lib yourself (as the old distributions with an old glibc that you want to build against to make compatible software of course also ship an old libasound which won't have the UMP API...).

Since v4

  • Experimental MIDI 2.0 support.
  • A neat configuration system which enables to pass options to the underlying backends.
  • Possibility to share the contexts across inputs and outputs to avoid creating multiple clients in e.g. JACK.
  • Hotplug support for all the backends!
  • Reworked port opening API which now uses handles instead of port indices to increase robustness in the face of disconnection / reconnection of MIDI devices.
  • Integer timestamps everywhere, and in nanoseconds. Additionnally, it is now possible to choose different timestamping methods (e.g. relative, absolute monotonic clock...).
  • Experimental API to allow to poll manually in ALSA (Sequencer and Raw), in order to give more control to the user and enable processing events on any kind of Linux event-loop.
  • Increase the checks done by the MIDI parser.
  • Internally it's pretty much a complete rewrite. Standard threading primitives are now used, as well as modern Linux facilities for polling control (eventfd, timerfd). Most of the code has been refactored.
  • Ability to set a fixed message size for zero-allocation scenarios, with -DLIBREMIDI_SLIM_MESSAGE=<NBytes> (in CMake or directly to the compiler)

Since v3

  • Allow to pass span when available (C++20) or (uint8_t* bytes, std::size_t size) pairs whenever possible to reduce copying.

Since v1

  • The library can be used header-only, as explained in the docs
  • Callbacks are passed by std::function and generally simplified.
  • Ability to use boost::small_vector to pass midi bytes instead of std::vector to reduce allocations.
  • Less indirections, virtuals and memory allocations.
  • Simplify usage of some functions, use C++ return style everywhere.
  • Use of standard C++ snake_case.
  • Simplification of exceptions.
  • Passes clean through clang-tidy, clang analyzer, GCC -Wall -Wextra, ASAN, UBSAN etc etc.
  • Support chunking of output data (only supported on raw ALSA backend so far).

New & improved backends

  • JACK support on Windows.
  • JACK support through weakjack to allow runtime loading of JACK.
  • UWP MIDI support on Windows
  • Emscripten support to run on a web browser with WebMIDI.
  • Raw ALSA support in addition to the existing ALSA sequencer support.

Roadmap

  • Migrate to std::expected instead of exceptions for error handling.
  • Finish MIDI 2 implementations, provide helpers, etc.
  • More tests and compliance checks
  • Work even more towards this library being a zero-cost abstraction on top of native MIDI APIs
  • Rethink some design issues with the original RtMidi, for instance the way port numbers work is not reliable
  • Refactor duplicated code across backends

They use this library

libremidi's People

Contributors

bratner avatar cacodemon345 avatar chdiesch avatar cpyarger avatar curve avatar dimitre avatar fwcd avatar hyperlan-git avatar ivanhawkes avatar jcelerier avatar lilggamegenius avatar mccann avatar neilpang avatar stmpf avatar tmwsnrx avatar yurivict avatar yurkoy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libremidi's Issues

Windows: SysEx (at least MMC ones) are mostly being missed

Using https://www.midimonitor.com/ I've confirmed that the device is sending the MMC messages it claims to. However, in LibreMidi - those messages mostly aren't coming through.

Seems to be timing sensitive as if I fiddle some breakpoints in the process of messages being pumped into the queue they'll squeeze through here and there. Scratch that, I was just lightly bumping my pitch-wheel without noticing.

I'm getting all other messages I'm expecting. Keys and CC.

To reproduce: run qmidiin test and clack some controls that send MMC messages. Specific keyboard I'm using here is Nektar GX61 on USB Mode 3 (use MMC instead of Mode 0 which sends CCs).

How to open port?

Hi!
Im kinda writing driver between midi controller and synth.
So there is a need of opening specific devices to communicate. There is an open_port, right?
Seems it gets abstract meaningless index. Especially during plug outs.

I see changelog says now ports can be open with (device) handler, but didn't find any trace in examples.
Or may be I missed something.
Thanks

Midi Input Callback returning messages of zero bytes

Hi,

I've successfully ported over my program to this library (for which many thanks). I've successfully set the midi input callback function, and it was working as expected in a previous build. I've just updated the library and a bit of my code, and now the input is being called when a message is received (as expected) but the message is 0 bytes long. Here are some (hopefully relevant) bits of code:

This is in the constructor
try { if (midiOut != nullptr) { delete midiOut; } midiOut = new rtmidi::midi_out(); if (midiIn != nullptr) { delete midiIn; } midiIn = new rtmidi::midi_in(); } catch (std::exception e) { exit(EXIT_FAILURE); } }

This is in the initialisation of the port and the input callback

midiIn = new rtmidi::midi_in(); midiIn->set_callback([this] (const auto& message) { HandleMidiInput(message); });

and this is HandleMidiInput

if (midiIn != nullptr) { int nBytes; auto message = midiIn->get_message(); nBytes = message.size(); if (nBytes > 0)

and that's where it ends, as every message arrives as 0 bytes (verified by iterating through the message and trying to dump the bytes to the log). Any suggestions where I'm going wrong very gratefully received!

Thanks in advance

MIDI CI 2.0 support

MIDI 2 needs some level of JSON support.
What library do we want to use ? I'd propose RapidJSON as it is also header-only and allows reading and writing. simdjson is super efficient but only does parsing, and nlohmann has the better API, but does allocations left and right while rapidjson allows to configure allocatores so that we can have 100% alloc-free behaviour important for real-time systems.

Books / information resources needed

Hi, is there any source of information, like a book or some documentation to get me started on writing a simple program and from there, keep on going ? I have several MIDI protocol books and also know enough C++ to start, but I would like to see some implementation or some resources to ease the learning curve with this library. Thanks !

feature request: please consider supporting sysex_interval and sysex_bufsize

When sending large sysex messages (> 1MB, containing e.g. an OS update) to my hardware synth, I need the option to configure a maximum buffer size and minimum delay between sending successive buffers. Sending the complete message at once fails because of (presumably) hardware limitations in the synth.

Large sysex messages should be divided in chunks of size sysex_bufsize and these chunks should be sent one by one, with a sysex_interval sleep in between the sends.

So far the only tool on linux (that I'm aware of) that supports this is amidi from the alsa-utils, see https://git.alsa-project.org/?p=alsa-utils.git;a=blob;f=amidi/amidi.c;hb=HEAD

On windows the midi-ox tool supports configuring buffer size, and on MAC-OS sysex librarian also supports setting a buffer size.

Writing meta data?

Hello and thanks for your efforts here.

Which tools are the preferred GOTOs for testing this code?

The code below did appear to produce a broken file, where import into Tracktion/Cakewalk etc failed. Is there anything simple and open source that can be used as a baseline reference?

Any thoughts?

Environment: Windows 10 / VS 2022.

MTIA.

			// test out writer stuff.
			std::ofstream of("x.mid", std::ios::binary);
			if (of.good())
			{
				libremidi::message meta;
				libremidi::writer writer;
				// writer.add_track();

				// this block seems to create an unreadable file in any of the many tools tested?
				{
					// standard 4/4 timing
					meta = libremidi::meta_events::time_signature(4, 4);
					writer.add_event(0, libremidi::track_event{ 0, 0, meta });
					// 120 BPM as we know it Jim
					meta = libremidi::meta_events::tempo(50000); 
					writer.add_event(0, libremidi::track_event{ 0, 0, meta });
				}
				// play middle C loudly
				libremidi::message msg = libremidi::message::note_on(0,60,127);
				writer.add_event(0, libremidi::track_event{ 0, 0, msg });
				// off -why does it need velocity?
				msg = libremidi::message::note_off(0, 60, 0);
				writer.add_event(0, libremidi::track_event{ 500, 0, msg });

				// does it need an end of track message? 
                               // not according to the library code
				meta = libremidi::meta_events::end_of_track();
				writer.add_event(0, libremidi::track_event{ 0, 0, meta });

				// write the content to file.
				writer.write(of);
			}

Can't compile midiprobe.cpp on Visual Studio Code

Hi, I'm trying to compile the midiprobe.cpp however I'm getting the following error on my Visual Studio Code:

 *  Executing task: C/C++: MSVC Compiler - Version 19.29.30146 for x86 

Starting build...
cl.exe /Zi /std:c++17 /IC:\Users\rui\Documents\GitHub\libraries_implementations\projects\midi_libremidi\include /EHsc /nologo /FeC:\Users\rui\Documents\GitHub\libraries_implementations\projects\midi_libremidi\tests\midiprobe.exe C:\Users\rui\Documents\GitHub\libraries_implementations\projects\midi_libremidi\tests\midiprobe.cpp
midiprobe.cpp
midiprobe.obj : error LNK2019: unresolved external symbol "class std::vector<enum libremidi::API,class std::allocator<enum libremidi::API> > __cdecl libremidi::available_apis(void)" (?available_apis@libremidi@@YA?AV?$vector@W4API@libremidi@@V?$allocator@W4API@libremidi@@@std@@@std@@XZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: __thiscall libremidi::midi_in::midi_in(enum libremidi::API,class std::basic_string_view<char,struct std::char_traits<char> >,unsigned int)" (??0midi_in@libremidi@@QAE@W4API@1@V?$basic_string_view@DU?$char_traits@D@std@@@std@@I@Z) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: __thiscall libremidi::midi_in::~midi_in(void)" (??1midi_in@libremidi@@QAE@XZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: enum libremidi::API __thiscall libremidi::midi_in::get_current_api(void)const " (?get_current_api@midi_in@libremidi@@QBE?AW4API@2@XZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: unsigned int __thiscall libremidi::midi_in::get_port_count(void)" (?get_port_count@midi_in@libremidi@@QAEIXZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall libremidi::midi_in::get_port_name(unsigned int)" (?get_port_name@midi_in@libremidi@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@I@Z) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: __thiscall libremidi::midi_out::midi_out(enum libremidi::API,class std::basic_string_view<char,struct std::char_traits<char> >)" (??0midi_out@libremidi@@QAE@W4API@1@V?$basic_string_view@DU?$char_traits@D@std@@@std@@@Z) referenced in function "public: __thiscall libremidi::midi_out::midi_out(void)" (??0midi_out@libremidi@@QAE@XZ)
midiprobe.obj : error LNK2019: unresolved external symbol "public: __thiscall libremidi::midi_out::~midi_out(void)" (??1midi_out@libremidi@@QAE@XZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: enum libremidi::API __thiscall libremidi::midi_out::get_current_api(void)" (?get_current_api@midi_out@libremidi@@QAE?AW4API@2@XZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: unsigned int __thiscall libremidi::midi_out::get_port_count(void)" (?get_port_count@midi_out@libremidi@@QAEIXZ) referenced in function _main
midiprobe.obj : error LNK2019: unresolved external symbol "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall libremidi::midi_out::get_port_name(unsigned int)" (?get_port_name@midi_out@libremidi@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@I@Z) referenced in function _main
C:\Users\rui\Documents\GitHub\libraries_implementations\projects\midi_libremidi\tests\midiprobe.exe : fatal error LNK1120: 11 unresolved externals

Build finished with error(s).

 *  The terminal process terminated with exit code: -1. 
 *  Terminal will be reused by tasks, press any key to close it. 

As you can see, I'm getting a lot of these errors concerning error LNK2019! I wold like to use this library on my projects, but for some reason it never compiles...

Bug on MacOSX midiout, sendmessage (MidiOutCore::sendMessage: could not allocate packet list)

Inside coreaudio.hpp, in the send_message method:

void send_message(const unsigned char* message, size_t size) override

There is

Byte* buffer = (Byte*)alloca(nBytes + (sizeof(MIDIPacketList)));
ByteCount listSize = sizeof(buffer);   // <---------    size of the pointer

But Shouldn't it be:

Byte* buffer = (Byte*)alloca(nBytes + (sizeof(MIDIPacketList)));
ByteCount listSize = nBytes + (sizeof(MIDIPacketList));   // <--- size of the allocated bytes

Since from the apple docs:

MIDIPacket * MIDIPacketListAdd(MIDIPacketList *pktlist, ByteCount listSize, MIDIPacket *curPacket, MIDITimeStamp time, ByteCount nData, const Byte *data);

listSize:  The size, in bytes, of the packet list.

You were passing the size of the pointer, not the size of the allocated bytes

I was getting: MidiOutCore::sendMessage: could not allocate packet list when attempting to send_message a 2 byte ProgramChange.

So Basically I see that the MIDIPacketListAdd there is returning NULL, failing to create a packet, because it thinks there is not enough space (because the buffer pointer sizeof(buffer) is of course 8 bytes only, and we are setting MIDIPacketListAdd's listSize to 8 when we actually have tons of space allocated: nBytes + (sizeof(MIDIPacketList)) in fact).

MacOS Randomly crashing

I keep getting reports of crashes on MacOS, The crash reports state:

terminating with uncaught exception of type rtmidi::driver_error: MidiInCore::initialize: error creating OS-X MIDI client object (-50).

I have not yet been able to replicate it personally. Windows and Linux have been rock solid.

Hotplug support?

How complete is hotplug support?

You mention that callbacks for device connects/disconnects is still TODO. But does enumerating/reconnecting devices work while some devices are still in use?

Jack doesn't group virtual MIDI ports under same client

Hi, I'm trying to create a group of virtual MIDI ports using libremidi. When I pass an identical client name to each port on creation, Jack renames them since instead of using the existing client, it tries to create a new one and then renames it to avoid naming clashes. In the libremidi docs, it sounds like this should not be the expected behavior.

I'd guess the simplest solution would be to amend the midi_in_jack and midi_out_jack classes so that they keep a static list of the Jack clients already created and used (possibly as shared pointers?), and when the name of an existing client is passed, it uses that one instead of creating a new client.

I'm trying to work out the best way to do this, but it's a little tricky. I wanted to check in and see if there was already a solution in place or a plan to implement one. Thank you!

Add versioning?

Hi,

Thanks for making this library.
I'm using libremidi with Conan and, for now, I use a latest scope that fetches the master, but I have not found any way to settle on a version easily?

Do you plan to use tags/releases at some point?

Thanks

Thread-Safety

Is it safe to write data to a device from multiple threads at the same time?

Library doesn't install the header

All installed files are:

lib/cmake/libremidi/libremidi-exports-%%CMAKE_BUILD_TYPE%%.cmake
lib/cmake/libremidi/libremidi-exports.cmake
lib/liblibremidi.so

add libremidi to vcpkg?

vcpkg seems to be a good way for including C/C++ libraries in a cross-platform manner and have them downloadable and work well with github-ci and such. RtMidi and PortMidi are in vcpkg:

https://github.com/microsoft/vcpkg/tree/master/ports/rtmidi
https://github.com/microsoft/vcpkg/tree/master/ports/portmidi

I notice you already have support for conan according to #45, but I think it would be good to also add to vcpkg cause then more users could easily include if they are using vcpkg for library management.

Add a license file for the project as a whole

I'm working on adding libremidi port to vcpkg, and it requires a software license file. I see separate licenses that cover RTMidi and ModernMidi, but there doesn't appear to be one for libremidi itself.

In addition to being added to the current head of the repo, if possible a version of tag v3.0 with the license would also be needed, as that is the latest release and thus what would be installed by default in vcpkg.

copyleft?

Considering this is a fork/combination of two other libraries with lax permissive licenses, I am wondering if you have considered changing the license to a copyleft license. I'd encourage the Mozilla Public License 2.0 if you want this to be used in proprietary software, otherwise the GPL would be a good choice IMO.

Examples

Hi Jean, I am trying to swap from rtmidi in my application, to your RtMidi17 library, Would you happen to have any documentation on how to deal with your message callbacks? I am probably missing something obvious. I am trying to rebuild the function Handle input https://github.com/Alzy/obs-midi/blob/rtmidi17/midi-agent.cpp

v4 requirements gathering thread

Hello,
for anyone interested I'd like to pin this issue to discuss proposed API changes and reworks in a future version.

It'll be in this branch : https://github.com/jcelerier/libremidi/tree/v4

  • C++20 as minimum, unconditionally enable <span>, etc.
  • Refactor the library classes in individual files to make everything easier to edit
  • #73
    • ALSA -> not available yet on a released ALSA version it seems
    • CoreMIDI
    • Win32
  • Refactor the various individual queues, semaphores and threading objects used across the backends.
    • custom semaphore -> std::semaphore
    • pthread -> std::thread
  • Enable configuring a custom queue type through some config.hpp -> now unnecessary, queue API has been removed as it can be built more efficiently on top of libremidi::midi_in in user code.
  • Allow to construct specific MIDI APIs with a specific context object (for instance passing an existing jack_client_t to midi_in_jack, etc) to enable to integrate more easily against existing run-loops - would fix #62 and also the macOS MIDIClient issue which required a singleton in rtmidi
    • mainly this also imply that we need to move the ability to execute the run-loop methods outside of the library if necessary, e.g. to run inside the user's existing audio callback?
    • alsa raw
    • alsa seq
    • jack
    • coremidi
    • emscripten? -> it's global anyways
    • uwp? -> it's global anyways
  • Instead of passing various "varying" arguments & calling member functions before init, simply have a single "configuration" struct passed to the ctor: > done! see reworked examples in the branch
struct jack_in_conf {
  std::string_view client_name;
  void* context; // e.g. an existing jack or pipewire client
  int queue_size;
  int buffer_size;
};

struct configuration { 
  api_configuration api; // std::variant<jack_in_conf, alsa_in_conf, etc...>;

  chunking_parameters chunks;

  bool ignore_sysex; 
  bool ignore_time;
  bool ignore_sense;
};

libremidi::midi_in mid{ configuration {
  .api = jack_api_configuration { 
    .client_name = "foo" 
   }
 , .ignore_time = true 
}};
  • timestamps double -> int(64?) in libremidi::message #68 (done)
  • timestamps in nanoseconds everywhere
  • allow to choose whether we want timestamps and in which format (relative, or absolute with e.g. monotonic clock)
  • std::function_ref support if available?
  • finding a better way to name devices to prevent name conflicts. Ideally we would use something like libusb if available to get some proprely unique identifier (#57 #17)
  • pipewire backend - I have experience with the audio part as I developed a back-end for ossia here: https://github.com/ossia/libossia/blob/master/src/ossia/audio/pipewire_protocol.hpp
  • remove all the logging with std::cerr > put behind an ifdef and LIBREMIDI_DEBUG
  • allow users to plug-in their own custom logging back-end instead
  • backport the fixes that happened in RtMidi since then:
    git diff -w 6ad9c059ce895c44616bcfa8104ca94678bc9408 -- RtMidi.cpp RtMidi.h
  • passing timestamps: master...wip/midi_write_ts
  • observers: make sure that they all behave the same and stay coherent with what midi_in / midi_out says about the ports
  • document the semantic of the ins / outs for each backend
  • #74
    • alsa_raw
    • coremidi
  • #75
    • alsa_raw
    • alsa_seq
    • observers
  • refactor the observers and give them a context object too, to allow sharing the context with midi_in / midi_out
  • #76
  • thread-safety annotations with clang

Relevant:

  • what else?

Message Queue Limit Reached

Hi,

I need to fetch many MIDI messages at the same time.

I got the message: "midi_in: message queue limit reached!!"

How to bypass this ?

This is critical for my application: I'm building a MIDI controller and many midi messages are sent on the same time from the host to the controller to init the correct values.

Thanks,

Bruno

Faulty MIDI clock-in messages with ALSA support?

Hi @jcelerier,

thank you very much for this great library!

I'm heaving trouble getting correct clock in messages on ALSA using different platforms (RPi3B+, MBP), distributions (RPi OS, Ubuntu22.04) and MIDI devices.

When using your test cmidiin.cpp, I get the correct message content on Windows...

Byte 0 = 248, stamp = 0.063
Byte 0 = 248, stamp = 0.062
....

... but I get the following with ALSA support:

Byte 0 = 248, Byte 1 = 248, stamp = 0
Byte 0 = 248, Byte 1 = 248, stamp = 0
....

Instead of 24 pulses per quarter, only approx. 12 messages (with two bytes content) arrive.

Am I doing something wrong?

Thanks!

Allow to control threading & polling manually

library name

"RtMidi17" is a name that could become awkward in the future. What if the library ends up requiring C++20? Would you then rename it to RtMidi20? It may be a good idea to come up with an original name for this library.

Help with Xcode library linking

Hi,

Really sorry to ask for help here, but I'm bashing my head against a brick wall. I'm trying to move from Rtmidi to Rtmidi17, but I can't get Rtmidi17 to compile in Xcode as an external library using angled brackets. If I try replacing all the includes with double-quotes, the linker fails. Any help would be incredibly gratefully received.

Thanks

API Documentation

You look to have done great work commenting the code, and started on setting up Doxygen,

I was poking things this morning and was able to get exhale to work

this is what the result is

https://libremidi.readthedocs.io/en/latest/

would you like me to put in a pull request to add this to the repo?

midi out get_port_count returning zero devices on macos, Big sur

I've updated some code from RtMidi17 and tried to list midi ports on Apple M1.
Ports are not showing up. midiout.get_port_count() returns 0.
I've confirmed at least "IAC Driver Bus 1" is online, showing up on other software.
Not sure if I'm missing something here.

Cheers!

libremidi::midi_out midiout;

void listMidiPorts() {
	cout << "listMidiPorts" << endl;
	unsigned int nPorts = midiout.get_port_count();
    cout << "nPorts : " << nPorts << endl;
	if (nPorts == 0)
	{
		std::cout << "No output ports available!" << std::endl;
	}
	else {
		for (unsigned int i = 0; i < nPorts; i++) {
			string portName = midiout.get_port_name(i);
			cout << "  Output port #" << i << ": " << portName << endl;
		}
	}
}

get_message_type strange behaviour?

Hi,

I'm trying to use a switch on get_message_type() inside the midin_in callback but for some reason it's not handling every cases. Here is a small repro, this is not working:

const auto chan = mess.get_channel();
int32_t b1, b2, b3;
b1 = (int32_t) mess.bytes[1];
b2 = (int32_t) mess.bytes[2];
b3 = (int32_t) mess.bytes[3];

switch(mess.get_message_type()){
        case libremidi::message_type::POLY_PRESSURE:
                console->AddLog("# Channel (%u) | POLY_PRESSURE: %u - %u", chan, b1, b2);
            break;
}

This does work, but only when receiving on channel 1

const auto chan = mess.get_channel();
auto msg = (libremidi::message_type) mess.bytes[0];
int32_t b1, b2, b3;
b1 = (int32_t) mess.bytes[1];
b2 = (int32_t) mess.bytes[2];
b3 = (int32_t) mess.bytes[3];
switch (msg) {

        case libremidi::message_type::POLY_PRESSURE:
                console->AddLog("# Channel (%u) | POLY_PRESSURE: %u - %u", chan, b1, b2);
            break;

}

Writing multiple tracks to file is broken

Attempting to write multiple tracks to a single MIDI file results in broken files that get rejected by most software. The reason is that the writer states it uses N tracks but puts the messages of all those tracks into a single MTrk, effectively omitting the other N-1 MTrks entries.

Synchronize reader and writer classes to work together better

Reader and writer have slightly different internal serialization formats, and so MIDI cannot be transferred from one data structure to the other except one event at a time.

Because the formats are very similar, they could be correlated, so e.g. a writer could be constructed from the reader via a straightforward vector copy.

The I/O methods could be standardized as well--the reader currently parses a byte vector, while the writer writes an ostream.

(Windows MM) Observer and API enhancement suggestion

Hi,

I came across an issue with the observer_winmm. The port name the callbacks return are not identical with the port name returned by midi_in_winmm::get_port_name and midi_out_winmm::get_port_name. And as the index returned by those callbacks is always 0, this whole thing is, sorry to say, pretty useless.

Looking at the code, the reason is obvious, get_port_name adds a trailing integer to avoid port name clashing (which I believe is a good thing) but that is not implemented in the observer.

I had a look on how to fix that an in a "simple and clean way" and found out that there are quite some code redundancy between midi_in_winmm::get_port_name, midi_out_winmm::get_port_name and observer_winmm::get_port_list.

I believe there is an easy way to largely simplify/clean-up the whole thing but that would imply an API change. I briefly had a look at the other implementations (Alsa, CoreAudio,...) I believe that it would also benefit them and would even make it possible to implement the observer in a generic way for almost all implementations.

I suggest to make the following functions static:
midi_in_XXX::get_port_count
midi_in_XXX::get_port_name
midi_out_XXX::get_port_count
midi_out_XXX::get_port_name
Making them static would make the observer straight forward to implement and would remove code redundancy.
And, in the end, why do we need to instantiate a midi_in and a midi_out object to enumerate ports ?

Unfortunately as all class inherit from midi_api, that would mean having both a static and non-static version of these functions. If only C++ had virtual static functions 😄

Cheers

Renaud

alsa_raw compiler warning `-Wsubobject-linkage`

I'm getting the following warning when building on Linux with GNU GCC 13.2.1:

In file included from /home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw.hpp:4,
                 from /home/jan/Projects/libremidi/include/libremidi/backends.hpp:16,
                 from /home/jan/Projects/libremidi/include/libremidi/observer.cpp:5:
/home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw/observer.hpp:16:7: warning: ‘libremidi::alsa_raw::observer_impl’ has a field ‘libremidi::{anonymous}::alsa_raw_helpers::enumerator libremidi::alsa_raw::observer_impl::current_devices’ whose type uses the anonymous namespace [-Wsubobject-linkage]
   16 | class observer_impl : public observer_api
      |       ^~~~~~~~~~~~~
In file included from /home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw.hpp:4,
                 from /home/jan/Projects/libremidi/include/libremidi/backends.hpp:16,
                 from /home/jan/Projects/libremidi/include/libremidi/midi_out.cpp:5:
/home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw/observer.hpp:16:7: warning: ‘libremidi::alsa_raw::observer_impl’ has a field ‘libremidi::{anonymous}::alsa_raw_helpers::enumerator libremidi::alsa_raw::observer_impl::current_devices’ whose type uses the anonymous namespace [-Wsubobject-linkage]
   16 | class observer_impl : public observer_api
      |       ^~~~~~~~~~~~~
In file included from /home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw.hpp:4,
                 from /home/jan/Projects/libremidi/include/libremidi/backends.hpp:16,
                 from /home/jan/Projects/libremidi/include/libremidi/midi_in.cpp:5:
/home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw/observer.hpp:16:7: warning: ‘libremidi::alsa_raw::observer_impl’ has a field ‘libremidi::{anonymous}::alsa_raw_helpers::enumerator libremidi::alsa_raw::observer_impl::current_devices’ whose type uses the anonymous namespace [-Wsubobject-linkage]
   16 | class observer_impl : public observer_api
      |       ^~~~~~~~~~~~~
In file included from /home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw.hpp:4,
                 from /home/jan/Projects/libremidi/include/libremidi/backends.hpp:16,
                 from /home/jan/Projects/libremidi/include/libremidi/libremidi.cpp:5:
/home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw/observer.hpp:16:7: warning: ‘libremidi::alsa_raw::observer_impl’ has a field ‘libremidi::{anonymous}::alsa_raw_helpers::enumerator libremidi::alsa_raw::observer_impl::current_devices’ whose type uses the anonymous namespace [-Wsubobject-linkage]
   16 | class observer_impl : public observer_api
      |       ^~~~~~~~~~~~~
In file included from /home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw.hpp:4,
                 from /home/jan/Projects/libremidi/include/libremidi/backends.hpp:16,
                 from /home/jan/Projects/libremidi/include/libremidi/client.cpp:4:
/home/jan/Projects/libremidi/include/libremidi/backends/alsa_raw/observer.hpp:16:7: warning: ‘libremidi::alsa_raw::observer_impl’ has a field ‘libremidi::{anonymous}::alsa_raw_helpers::enumerator libremidi::alsa_raw::observer_impl::current_devices’ whose type uses the anonymous namespace [-Wsubobject-linkage]
   16 | class observer_impl : public observer_api
      |       ^~~~~~~~~~~~~

Integral timestamps/deltas

This looks like a nice revamp of RtMidi, thanks! One thing that bugs me as I maintain the Haskell bindings to RtMidi is that event times are represented as doubles. No other music protocol at that level uses floating point - not midi files, and not OSC! I think some of the backends even have integral timestamps. Are you open to changing this (or getting contributions along those lines)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.