Module: MIDICommunicationsWindows::API Private
- Extended by:
- FFI::Library
- Defined in:
- lib/midi-communications-windows/api.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
Low-level FFI bindings to the Windows Multimedia (WinMM) MIDI API.
This module binds winmm.dll directly. Most users should use the
higher-level Input, Output and Device instead.
Why the bindings look the way they do
Pointer-sized types are never :ulong. Windows is LLP64: long stays
32 bits on 64-bit Windows while HANDLE, DWORD_PTR and UINT_PTR are
pointer-sized. Measured from inside FFI on Windows 11 x64:
FFI::Pointer.size is 8 and FFI.type_size(:ulong) is 4. Declaring a
handle as :ulong truncates it — silently, because the low half of a
handle often looks plausible. Every pointer-sized parameter here is
:uintptr_t.
The W variants, never the A ones. midiInGetDevCapsA returns the
port name in the machine's ANSI code page, which mangles any name that is
not ASCII. A user-created loopback called "Reloj Bitwig" is a realistic
port name, so the wide-character functions are the only correct choice.
dwCallback is :uintptr_t, not a callback type. WinMM overloads that
parameter by the flags in dwFlags: it holds a function pointer under
CALLBACK_FUNCTION, a thread id under CALLBACK_THREAD, a window handle
under CALLBACK_WINDOW, and nothing under CALLBACK_NULL. Typing it as a
callback would make the binding lie about three of those four.
These bindings were written from Microsoft's Win32 documentation. They are not derived from any existing Ruby binding.
Defined Under Namespace
Classes: MIDIHdr, MIDIInCaps, MIDIOutCaps, MSG
Constant Summary collapse
- MAXPNAMELEN =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Length of a port name in
MIDIINCAPSW/MIDIOUTCAPSW, in characters, including the terminating NUL. This is the origin of WinMM's 31-character limit on port names. 32- MMSYSERR_NOERROR =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
MMRESULTvalue meaning success. 0- MAX_ERROR_TEXT_LENGTH =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Characters reserved for an error description. WinMM truncates to fit.
256- CALLBACK_NULL =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
How WinMM should notify a client of input. Passed in
dwFlags; decides howdwCallbackis interpreted. 0x0000_0000- CALLBACK_WINDOW =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x0001_0000- CALLBACK_THREAD =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x0002_0000- CALLBACK_FUNCTION =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x0003_0000- MM_MIM_OPEN =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Notification messages delivered for an input device.
0x3C1- MM_MIM_CLOSE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C2- MM_MIM_DATA =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C3- MM_MIM_LONGDATA =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C4- MM_MIM_ERROR =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C5- MM_MIM_LONGERROR =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C6- MM_MOM_OPEN =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Notification messages delivered for an output device.
0x3C7- MM_MOM_CLOSE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C8- MM_MOM_DONE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x3C9- MHDR_DONE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
MIDIHDR#dwFlagsbits. 0x0000_0001- MHDR_PREPARED =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x0000_0002- MHDR_INQUEUE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x0000_0004- WM_QUIT =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Thread message queue.
0x0012- PM_NOREMOVE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
0x0000- WM_USER =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Windows posts messages of its own to the same queue WinMM uses. Measured: a
WM_USERwith both parameters zero follows every WinMM notification, including those after the port opens and closes. Nothing here emits them and nothing here knows what does, so the reader dispatches on the message id and ignores everything it did not ask for. 0x0400
Class Method Summary collapse
-
.check!(code, operation, direction) ⇒ void
private
Raises unless the call succeeded.
-
.error_text(code, direction) ⇒ String?
private
WinMM's own description of an error code.
-
.read_wide_string(pointer, max_characters) ⇒ String
private
Reads a NUL-terminated UTF-16LE string out of native memory.
Class Method Details
.check!(code, operation, direction) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Raises unless the call succeeded.
291 292 293 294 295 |
# File 'lib/midi-communications-windows/api.rb', line 291 def check!(code, operation, direction) return if code == MMSYSERR_NOERROR raise Error.new(operation, code, error_text(code, direction)) end |
.error_text(code, direction) ⇒ String?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
WinMM's own description of an error code.
The two error tables are not the same, which is why the direction has to be known: the same numeric code can mean different things for input and for output.
306 307 308 309 310 311 312 313 314 315 |
# File 'lib/midi-communications-windows/api.rb', line 306 def error_text(code, direction) buffer = FFI::MemoryPointer.new(:uint16, MAX_ERROR_TEXT_LENGTH) result = case direction when :input then midiInGetErrorTextW(code, buffer, MAX_ERROR_TEXT_LENGTH) when :output then midiOutGetErrorTextW(code, buffer, MAX_ERROR_TEXT_LENGTH) end read_wide_string(buffer, MAX_ERROR_TEXT_LENGTH) if result == MMSYSERR_NOERROR end |
.read_wide_string(pointer, max_characters) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Reads a NUL-terminated UTF-16LE string out of native memory.
Reading is done in code units rather than bytes on purpose: a byte-wise
search for a NUL pair would stop in the middle of a pair such as
'A' 'Ā' (41 00 00 01) and return a truncated name.
A name that does not decode is repaired rather than raised on: a port whose name arrives mangled is still a port the caller may want to open, and losing the whole enumeration over one bad character would be worse than showing a replacement character.
331 332 333 334 335 336 337 338 |
# File 'lib/midi-communications-windows/api.rb', line 331 def read_wide_string(pointer, max_characters) units = pointer.read_array_of_uint16(max_characters) units = units.take_while { |unit| !unit.zero? } units.pack('S<*') .force_encoding(Encoding::UTF_16LE) .encode(Encoding::UTF_8, invalid: :replace, undef: :replace) end |