diff options
Diffstat (limited to 'portmidi/pm_win')
| -rwxr-xr-x | portmidi/pm_win/README_WIN.txt | 174 | ||||
| -rwxr-xr-x | portmidi/pm_win/debugging_dlls.txt | 145 | ||||
| -rwxr-xr-x | portmidi/pm_win/pmwin.c | 98 | ||||
| -rwxr-xr-x | portmidi/pm_win/pmwinmm.c | 1196 | ||||
| -rwxr-xr-x | portmidi/pm_win/pmwinmm.h | 5 | ||||
| -rw-r--r-- | portmidi/pm_win/static.cmake | 24 |
6 files changed, 0 insertions, 1642 deletions
diff --git a/portmidi/pm_win/README_WIN.txt b/portmidi/pm_win/README_WIN.txt deleted file mode 100755 index 8bfb467..0000000 --- a/portmidi/pm_win/README_WIN.txt +++ /dev/null @@ -1,174 +0,0 @@ -File: PortMidi Win32 Readme
-Author: Belinda Thom, June 16 2002
-Revised by: Roger Dannenberg, June 2002, May 2004, June 2007,
- Umpei Kurokawa, June 2007
- Roger Dannenberg Sep 2009, May 2022
-
-Contents:
- Using Portmidi
- To Install Portmidi
- To Compile Portmidi
- About Cmake
- Using other versions of Visual C++
- To Create Your Own Portmidi Client Application
-
-
-
-=============================================================================
-USING PORTMIDI:
-=============================================================================
-
-I recommend building a static library and linking with your
-application. PortMidi is not large. See ../README.md for
-basic compiling instructions.
-
-The Windows version has a couple of extra switches: You can define
-DEBUG and MMDEBUG for a few extra messages (see the code).
-
-If PM_CHECK_ERRORS is defined, PortMidi reports and exits on any
-error. This requires terminal output to see, and aborts your
-application, so it's only intended for quick command line programs
-where you do not care to check return values and handle errors
-more robustly.
-
-PortMidi is designed to run without a console and should work perfectly
-well within a graphical user interface application.
-
-Read the portmidi.h file for PortMidi API details on using the PortMidi API.
-See <...>\pm_test\testio.c and other files in pm_test for usage examples.
-
-There are many other programs in pm_test, including a MIDI monitor.
-
-
-============================================================================
-DESIGN NOTES
-============================================================================
-
-Orderly cleanup after errors are encountered is based on a fixed order of
-steps and state changes to reflect each step. Here's the order:
-
-To open input:
- initialize return value to NULL
- - allocate the PmInternal strucure (representation of PortMidiStream)
- return value is (non-null) PmInternal structure
- - allocate midi buffer
- set buffer field of PmInternal structure
- - call system-dependent open code
- - allocate midiwinmm_type for winmm dependent data
- set descriptor field of PmInternal structure
- - open device
- set handle field of midiwinmm_type structure
- - allocate buffers
- - start device
- - return
- - return
-
-SYSEX HANDLING
-
-There are three cases: simple output, stream output, input
-Each must deal with:
- 1. Buffer Initialization (creating buffers)
- 2. Buffer Allocation (finding a free buffer)
- 3. Buffer Fill (putting bytes in the buffer)
- 4. Buffer Preparation (midiOutPrepare, etc.)
- 5. Buffer Send (to Midi device)
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- 8. Buffer Free (returning to the buffer pool)
- 9. Buffer Finalization (returning to heap)
-
-Here's how simple output handles sysex:
- 1. Buffer Initialization (creating buffers)
- allocated when code tries to write first byte to a buffer
- the test is "if (!m->sysex_buffers[0]) { ... }"
- this field is initialized to NULL when device is opened
- the size is SYSEX_BYTES_PER_BUFFER
- allocate_sysex_buffers() does the initialization
- note that the actual size of the allocation includes
- additional space for a MIDIEVENT (3 longs) which are
- not used in this case
- 2. Buffer Allocation (finding a free buffer)
- see get_free_sysex_buffer()
- cycle through m->sysex_buffers[] using m->next_sysex_buffer
- to determine where to look next
- if nothing is found, wait by blocking on m->sysex_buffer_signal
- this is signaled by the callback every time a message is
- received
- 3. Buffer Fill (putting bytes in the buffer)
- essentially a state machine approach
- hdr->dwBytesRecorded is a position in message pointed to by m->hdr
- keep appending bytes until dwBytesRecorded >= SYSEX_BYTES_PER_BUFFER
- then send the message, reseting the state to initial values
- 4. Buffer Preparation (midiOutPrepare, etc.)
- just before sending in winmm_end_sysex()
- 5. Buffer Send (to Midi device)
- message is padded with zero at end (since extra space was allocated
- this is ok) -- the zero works around a bug in (an old version of)
- MIDI YOKE drivers
- dwBufferLength gets dwBytesRecorded, and dwBytesRecorded gets 0
- uses midiOutLongMsg()
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- not applicable for output
- 8. Buffer Free (returning to the buffer pool)
- unprepare message to indicate that it is free
- SetEvent on m->buffer_signal in case client is waiting
- 9. Buffer Finalization (returning to heap)
- when device is closed, winmm_out_delete frees all sysex buffers
-
-Here's how stream output handles sysex:
- 1. Buffer Initialization (creating buffers)
- same code as simple output (see above)
- 2. Buffer Allocation (finding a free buffer)
- same code as simple output (see above)
- 3. Buffer Fill (putting bytes in the buffer)
- essentially a state machine approach
- m->dwBytesRecorded is a position in message
- keep appending bytes until buffer is full (one byte to spare)
- 4. Buffer Preparation (midiOutPrepare, etc.)
- done before sending message
- dwBytesRecorded and dwBufferLength are set in winmm_end_sysex
- 5. Buffer Send (to Midi device)
- uses midiStreamOutMsg()
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- not applicable for output
- 8. Buffer Free (returning to the buffer pool)
- unprepare message to indicate that it is free
- SetEvent on m->buffer_signal in case client is waiting
- 9. Buffer Finalization (returning to heap)
- when device is closed, winmm_out_delete frees all sysex buffers
-
-
-Here's how input handles sysex:
- 1. Buffer Initialization (creating buffers)
- two buffers are allocated in winmm_in_open
- 2. Buffer Allocation (finding a free buffer)
- same code as simple output (see above)
- 3. Buffer Fill (putting bytes in the buffer)
- not applicable for input
- 4. Buffer Preparation (midiOutPrepare, etc.)
- done before sending message -- in winmm_in_open and in callback
- 5. Buffer Send (to Midi device)
- uses midiInAddbuffer in allocate_sysex_input_buffer (called from
- winmm_in_open) and callback
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- done without pause in loop in callback
- 8. Buffer Free (returning to the buffer pool)
- done by midiInAddBuffer in callback, no pointer to buffers
- is retained except by device
- 9. Buffer Finalization (returning to heap)
- when device is closed, empty buffers are delivered to callback,
- which frees them
-
-IMPORTANT: In addition to the above, PortMidi now has
-"shortcuts" to optimize the transfer of sysex data. To enable
-the optimization for sysex output, the system-dependent code
-sets fields in the pmInternal structure: fill_base, fill_offset_ptr,
-and fill_length. When fill_base is non-null, the system-independent
-part of PortMidi is allowed to directly copy sysex bytes to
-"fill_base[*fill_offset_ptr++]" until *fill_offset_ptr reaches
-fill_length. See the code for details.
-
-
diff --git a/portmidi/pm_win/debugging_dlls.txt b/portmidi/pm_win/debugging_dlls.txt deleted file mode 100755 index 82b81a5..0000000 --- a/portmidi/pm_win/debugging_dlls.txt +++ /dev/null @@ -1,145 +0,0 @@ -========================================================================================================================
-Methods for Debugging DLLs
-========================================================================================================================
-If you have the source for both the DLL and the calling program, open the project for the calling executable file and
-debug the DLL from there. If you load a DLL dynamically, you must specify it in the Additional DLLs category of the
-Debug tab in the Project Settings dialog box.
-
-If you have the source for the DLL only, open the project that builds the DLL. Use the Debug tab in the Project
-Settings dialog box to specify the executable file that calls the DLL.
-
-You can also debug a DLL without a project. For example, maybe you just picked up a DLL and source code but you
-don’t have an associated project or workspace. You can use the Open command on the File menu to select the .DLL
-file you want to debug. The debug information should be in either the .DLL or the related .PDB file. After
-Visual C++ opens the file, on the Build menu click Start Debug and Go to begin debugging.
-
-To debug a DLL using the project for the executable file
-
-From the Project menu, click Settings.
-The Project Settings dialog box appears.
-
-Choose the Debug tab.
-
-
-In the Category drop-down list box, select General.
-
-
-In the Program Arguments text box, type any command-line arguments required by the executable file.
-
-
-In the Category drop-down list box, select Additional DLLs.
-
-
-In the Local Name column, type the names of DLLs to debug.
-If you are debugging remotely, the Remote Name column appears. In this column, type the complete path for the
-remote module to map to the local module name.
-
-In the Preload column, select the check box if you want to load the module before debugging begins.
-
-
-Click OK to store the information in your project.
-
-
-From the Build menu, click Start Debug and Go to start the debugger.
-You can set breakpoints in the DLL or the calling program. You can open a source file for the DLL and set breakpoints
-in that file, even though it is not a part of the executable file’s project.
-
-To debug a DLL using the project for the DLL
-
-From the Project menu, click Settings.
-The Project Settings dialog box appears.
-
-Choose the Debug tab.
-
-
-In the Category drop-down list box, select General.
-
-
-In the Executable For Debug Session text box, type the name of the executable file that calls the DLL.
-
-
-In the Category list box, select Additional DLLs.
-
-
-In the Local Module Name column, type the name of the DLLs you want to debug.
-
-
-Click OK to store the information in your project.
-
-
-Set breakpoints as required in your DLL source files or on function symbols in the DLL.
-
-
-From the Build menu, click Start Debug and Go to start the debugger.
-To debug a DLL created with an external project
-
-From the Project menu, click Settings.
-The Project Settings dialog box appears.
-
-Choose the Debug tab.
-
-
-In the Category drop-down list box, select General.
-
-
-In the Executable For Debug Session text box, type the name of the DLL that your external makefile builds.
-
-
-Click OK to store the information in your project.
-
-
-Build a debug version of the DLL with symbolic debugging information, if you don’t already have one.
-
-
-Follow one of the two procedures immediately preceding this one to debug the DLL.
-
-========================================================================================================================
-Why Don’t My DLL Breakpoints Work?
-========================================================================================================================
-Some reasons why your breakpoints don’t work as expected are listed here, along with solutions or work-arounds for each.
-If you follow the instructions in one topic and are still having breakpoint problems, look at some of the other topics.
-Often breakpoint problems result from a combination of conditions.
-
-You can't set a breakpoint in a source file when the corresponding symbolic information isn't loaded into memory by
-the debugger.
-You cannot set a breakpoint in any source file when the corresponding symbolic information will not be loaded into memory
-by the debugger.
-Symptoms include messages such as "the breakpoint cannot be set" or a simple, noninformational beep.
-
-When setting breakpoints before the code to be debugged has been started, the debugger uses a breakpoint list to keep
-track of how and where to set breakpoints. When you actually begin the debugging session, the debugger loads the symbolic
-information for all the code to be debugged and then walks through its breakpoint list, attempting to set the
-breakpoints.
-
-However, if one or more of the code modules have not been designated to the debugger, there will be no symbolic
-information for the debugger to use when walking through its breakpoint list. Situations where this is likely to
-occur include:
-
-Attempts to set breakpoints in a DLL before the call to LoadLibrary.
-
-Setting a breakpoint in an ActiveX server before the container has started the server.
-
-Other similar cases.
-
-To prevent this behavior in Visual C++, specify all additional DLLs and COM servers in the Additional DLLs field
-in the Debug/Options dialog box to notify the debugger that you want it to load symbolic debug information for
-additional .DLL files. When this has been done, breakpoints set in code that has not yet been loaded into memory
-will be "virtual" breakpoints. When the code is actually loaded into memory by the loader, these become physical
-breakpoints. Make sure that these additional debugging processes are not already running when you start your
-debugging session. The debugging process and these additional processes must be sychronized at the same beginning
-point to work correctly, hitting all breakpoints.
-
-Breakpoints are missed when more than one copy of a DLL is on your hard disk.
-Having more than one copy of a DLL on your hard drive, especially if it is in your Windows directory, can cause
-debugger confusion. The debugger will load the symbolic information for the DLL specified to it at run time (with the
-Additional DLLs field in the Debug/Options dialog box), while Windows has actually loaded a different copy of the
-DLL itself into memory. Because there is no way to force the debugger to load a specific DLL, it is a good idea to
-keep only one version of a DLL at a time in your path, current directory, and Windows directory.
-
-You can’t set "Break When Expression Has Changed" breakpoints on a variable local to a DLL.
-Setting a "Break When Expression Has Changed" breakpoint on a variable local to a DLL function before the call
-to LoadLibrary causes the breakpoint to be virtual (there are no physical addresses for the DLL in memory yet).
-Virtual breakpoints involving expressions pose a special problem. The DLL must be specified to the debugger at
-startup (causing its symbolic information to be loaded). In addition, the DLL's executable code must also be loaded
-into memory before this kind of breakpoint can be set. This means that the calling application's code must be
-executed to the point after its call to LoadLibrary before the debugger will allow this type of breakpoint to be set.
diff --git a/portmidi/pm_win/pmwin.c b/portmidi/pm_win/pmwin.c deleted file mode 100755 index 5cb73b0..0000000 --- a/portmidi/pm_win/pmwin.c +++ /dev/null @@ -1,98 +0,0 @@ -/* pmwin.c -- PortMidi os-dependent code */
-
-/* This file only needs to implement:
- pm_init(), which calls various routines to register the
- available midi devices,
- Pm_GetDefaultInputDeviceID(), and
- Pm_GetDefaultOutputDeviceID().
- This file must
- be separate from the main portmidi.c file because it is system
- dependent, and it is separate from, say, pmwinmm.c, because it
- might need to register devices for winmm, directx, and others.
-
- */
-
-#include "stdlib.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-#include "pmwinmm.h"
-#ifdef DEBUG
-#include "stdio.h"
-#endif
-#include <windows.h>
-
-/* pm_exit is called when the program exits.
- It calls pm_term to make sure PortMidi is properly closed.
- If DEBUG is on, we prompt for input to avoid losing error messages.
- */
-static void pm_exit(void) {
- pm_term();
-}
-
-
-static BOOL WINAPI ctrl_c_handler(DWORD fdwCtrlType)
-{
- exit(1); /* invokes pm_exit() */
- ExitProcess(1); /* probably never called */
- return TRUE;
-}
-
-/* pm_init is the windows-dependent initialization.*/
-void pm_init(void)
-{
- atexit(pm_exit);
- SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
-#ifdef DEBUG
- printf("registered pm_exit with atexit()\n");
-#endif
- pm_winmm_init();
- /* initialize other APIs (DirectX?) here */
-}
-
-
-void pm_term(void) {
- pm_winmm_term();
-}
-
-
-static PmDeviceID pm_get_default_device_id(int is_input, char *key) {
-#define PATTERN_MAX 256
- /* Find first input or device -- this is the default. */
- PmDeviceID id = pmNoDevice;
- int i;
- Pm_Initialize(); /* make sure descriptors exist! */
- for (i = 0; i < pm_descriptor_len; i++) {
- if (pm_descriptors[i].pub.input == is_input) {
- id = i;
- break;
- }
- }
- return id;
-}
-
-
-PmDeviceID Pm_GetDefaultInputDeviceID() {
- return pm_get_default_device_id(TRUE,
- "/P/M_/R/E/C/O/M/M/E/N/D/E/D_/I/N/P/U/T_/D/E/V/I/C/E");
-}
-
-
-PmDeviceID Pm_GetDefaultOutputDeviceID() {
- return pm_get_default_device_id(FALSE,
- "/P/M_/R/E/C/O/M/M/E/N/D/E/D_/O/U/T/P/U/T_/D/E/V/I/C/E");
-}
-
-
-#include "stdio.h"
-
-void *pm_alloc(size_t s) {
- return malloc(s);
-}
-
-
-void pm_free(void *ptr) {
- free(ptr);
-}
-
-
diff --git a/portmidi/pm_win/pmwinmm.c b/portmidi/pm_win/pmwinmm.c deleted file mode 100755 index 6f4b6f3..0000000 --- a/portmidi/pm_win/pmwinmm.c +++ /dev/null @@ -1,1196 +0,0 @@ -/* pmwinmm.c -- system specific definitions */
-
-#ifndef _WIN32_WINNT
- /* without this define, InitializeCriticalSectionAndSpinCount is
- * undefined. This version level means "Windows 2000 and higher"
- */
- #define _WIN32_WINNT 0x0500
-#endif
-
-#define UNICODE 1
-#include <wchar.h>
-#include "windows.h"
-#include "mmsystem.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-#include "pmwinmm.h"
-#include <string.h>
-#include "porttime.h"
-#ifndef UNICODE
-#error Expected UNICODE to be defined
-#endif
-
-
-/* asserts used to verify portMidi code logic is sound; later may want
- something more graceful */
-#include <assert.h>
-#ifdef MMDEBUG
-/* this printf stuff really important for debugging client app w/host errors.
- probably want to do something else besides read/write from/to console
- for portability, however */
-#define STRING_MAX 80
-#include "stdio.h"
-#endif
-
-#define streql(x, y) (strcmp(x, y) == 0)
-
-#define MIDI_SYSEX 0xf0
-#define MIDI_EOX 0xf7
-
-/* callback routines */
-static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn,
- UINT wMsg, DWORD_PTR dwInstance,
- DWORD_PTR dwParam1, DWORD_PTR dwParam2);
-static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD_PTR dwInstance,
- DWORD_PTR dwParam1,
- DWORD_PTR dwParam2);
-
-extern pm_fns_node pm_winmm_in_dictionary;
-extern pm_fns_node pm_winmm_out_dictionary;
-
-static void winmm_out_delete(PmInternal *midi); /* forward reference */
-
-/*
-A note about buffers: WinMM seems to hold onto buffers longer than
-one would expect, e.g. when I tried using 2 small buffers to send
-long sysex messages, at some point WinMM held both buffers. This problem
-was fixed by making buffers bigger. Therefore, it seems that there should
-be enough buffer space to hold a whole sysex message.
-
-The bufferSize passed into Pm_OpenInput (passed into here as buffer_len)
-will be used to estimate the largest sysex message (= buffer_len * 4 bytes).
-Call that the max_sysex_len = buffer_len * 4.
-
-For simple midi output (latency == 0), allocate 3 buffers, each with half
-the size of max_sysex_len, but each at least 256 bytes.
-
-For stream output, there will already be enough space in very short
-buffers, so use them, but make sure there are at least 16.
-
-For input, use many small buffers rather than 2 large ones so that when
-there are short sysex messages arriving frequently (as in control surfaces)
-there will be more free buffers to fill. Use max_sysex_len / 64 buffers,
-but at least 16, of size 64 bytes each.
-
-The following constants help to represent these design parameters:
-*/
-#define NUM_SIMPLE_SYSEX_BUFFERS 3
-#define MIN_SIMPLE_SYSEX_LEN 256
-
-#define MIN_STREAM_BUFFERS 16
-#define STREAM_BUFFER_LEN 24
-
-#define INPUT_SYSEX_LEN 64
-#define MIN_INPUT_BUFFERS 16
-
-/* if we run out of space for output (assume this is due to a sysex msg,
- expand by up to NUM_EXPANSION_BUFFERS in increments of EXPANSION_BUFFER_LEN
- */
-#define NUM_EXPANSION_BUFFERS 128
-#define EXPANSION_BUFFER_LEN 1024
-
-/* A sysex buffer has 3 DWORDS as a header plus the actual message size */
-#define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3)
-/* A MIDIHDR with a sysex message is the buffer length plus the header size */
-#define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
-
-/*
-==============================================================================
-win32 mmedia system specific structure passed to midi callbacks
-==============================================================================
-*/
-
-/* global winmm device info */
-MIDIINCAPS *midi_in_caps = NULL;
-MIDIINCAPS midi_in_mapper_caps;
-UINT midi_num_inputs = 0;
-MIDIOUTCAPS *midi_out_caps = NULL;
-MIDIOUTCAPS midi_out_mapper_caps;
-UINT midi_num_outputs = 0;
-
-/* per device info */
-typedef struct winmm_info_struct {
- union {
- HMIDISTRM stream; /* windows handle for stream */
- HMIDIOUT out; /* windows handle for out calls */
- HMIDIIN in; /* windows handle for in calls */
- } handle;
-
- /* midi output messages are sent in these buffers, which are allocated
- * in a round-robin fashion, using next_buffer as an index
- */
- LPMIDIHDR *buffers; /* pool of buffers for midi in or out data */
- int max_buffers; /* length of buffers array */
- int buffers_expanded; /* buffers array expanded for extra msgs? */
- int num_buffers; /* how many buffers allocated in buffers array */
- int next_buffer; /* index of next buffer to send */
- HANDLE buffer_signal; /* used to wait for buffer to become free */
- unsigned long last_time; /* last output time */
- int first_message; /* flag: treat first message differently */
- int sysex_mode; /* middle of sending sysex */
- unsigned long sysex_word; /* accumulate data when receiving sysex */
- unsigned int sysex_byte_count; /* count how many received */
- LPMIDIHDR hdr; /* the message accumulating sysex to send */
- unsigned long sync_time; /* when did we last determine delta? */
- long delta; /* difference between stream time and
- real time */
- CRITICAL_SECTION lock; /* prevents reentrant callbacks (input only) */
-} winmm_info_node, *winmm_info_type;
-
-
-/*
-=============================================================================
-general MIDI device queries
-=============================================================================
-*/
-
-/* add a device after converting device (product) name to UTF-8 */
-static void pm_add_device_w(char *api, WCHAR *device_name, int is_input,
- int is_virtual, void *descriptor, pm_fns_type dictionary)
-{
- char utf8name[4 * MAXPNAMELEN];
- WideCharToMultiByte(CP_UTF8, 0, device_name, -1,
- utf8name, 4 * MAXPNAMELEN - 1, NULL, NULL);
- /* ignore errors here -- if pm_descriptor_max is exceeded,
- some devices will not be accessible. */
- pm_add_device(api, utf8name, is_input, is_virtual, descriptor, dictionary);
-}
-
-
-static void pm_winmm_general_inputs()
-{
- UINT i;
- WORD wRtn;
- midi_num_inputs = midiInGetNumDevs();
- midi_in_caps = (MIDIINCAPS *) pm_alloc(sizeof(MIDIINCAPS) *
- midi_num_inputs);
- if (midi_in_caps == NULL) {
- /* if you can't open a particular system-level midi interface
- * (such as winmm), we just consider that system or API to be
- * unavailable and move on without reporting an error.
- */
- return;
- }
-
- for (i = 0; i < midi_num_inputs; i++) {
- wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) & midi_in_caps[i],
- sizeof(MIDIINCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_in_caps[i].szPname, TRUE, FALSE,
- (void *) (intptr_t) i, &pm_winmm_in_dictionary);
- }
- }
-}
-
-
-static void pm_winmm_mapper_input()
-{
- WORD wRtn;
- /* Note: if MIDIMAPPER opened as input (documentation implies you
- can, but current system fails to retrieve input mapper
- capabilities) then you still should retrieve some form of
- setup info. */
- wRtn = midiInGetDevCaps((UINT) MIDIMAPPER,
- (LPMIDIINCAPS) & midi_in_mapper_caps,
- sizeof(MIDIINCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_in_mapper_caps.szPname, TRUE, FALSE,
- (void *) (intptr_t) MIDIMAPPER,
- &pm_winmm_in_dictionary);
- }
-}
-
-
-static void pm_winmm_general_outputs()
-{
- UINT i;
- DWORD wRtn;
- midi_num_outputs = midiOutGetNumDevs();
- midi_out_caps = pm_alloc(sizeof(MIDIOUTCAPS) * midi_num_outputs);
-
- if (midi_out_caps == NULL) {
- /* no error is reported -- see pm_winmm_general_inputs */
- return ;
- }
-
- for (i = 0; i < midi_num_outputs; i++) {
- wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) & midi_out_caps[i],
- sizeof(MIDIOUTCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_out_caps[i].szPname, FALSE, FALSE,
- (void *) (intptr_t) i, &pm_winmm_out_dictionary);
- }
- }
-}
-
-
-static void pm_winmm_mapper_output()
-{
- WORD wRtn;
- /* Note: if MIDIMAPPER opened as output (pseudo MIDI device
- maps device independent messages into device dependant ones,
- via NT midimapper program) you still should get some setup info */
- wRtn = midiOutGetDevCaps((UINT) MIDIMAPPER, (LPMIDIOUTCAPS)
- & midi_out_mapper_caps, sizeof(MIDIOUTCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_out_mapper_caps.szPname, FALSE, FALSE,
- (void *) (intptr_t) MIDIMAPPER,
- &pm_winmm_out_dictionary);
- }
-}
-
-
-/*
-============================================================================
-host error handling
-============================================================================
-*/
-
-static unsigned int winmm_check_host_error(PmInternal *midi)
-{
- return FALSE;
-}
-
-
-/*
-=============================================================================
-buffer handling
-=============================================================================
-*/
-static MIDIHDR *allocate_buffer(long data_size)
-{
- LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
- MIDIEVENT *evt;
- if (!hdr) return NULL;
- evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
- hdr->lpData = (LPSTR) evt;
- hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size);
- hdr->dwBytesRecorded = 0;
- hdr->dwFlags = 0;
- hdr->dwUser = hdr->dwBufferLength;
- return hdr;
-}
-
-
-static PmError allocate_buffers(winmm_info_type info, long data_size,
- long count)
-{
- int i;
- /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
- info->num_buffers = 0; /* in case no memory can be allocated */
- info->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
- if (!info->buffers) return pmInsufficientMemory;
- info->max_buffers = count;
- for (i = 0; i < count; i++) {
- LPMIDIHDR hdr = allocate_buffer(data_size);
- if (!hdr) { /* free everything allocated so far and return */
- for (i = i - 1; i >= 0; i--) pm_free(info->buffers[i]);
- pm_free(info->buffers);
- info->max_buffers = 0;
- return pmInsufficientMemory;
- }
- info->buffers[i] = hdr; /* this may be NULL if allocation fails */
- }
- info->num_buffers = count;
- return pmNoError;
-}
-
-
-static LPMIDIHDR get_free_output_buffer(PmInternal *midi)
-{
- LPMIDIHDR r = NULL;
- winmm_info_type info = (winmm_info_type) midi->api_info;
- while (TRUE) {
- int i;
- for (i = 0; i < info->num_buffers; i++) {
- /* cycle through buffers, modulo info->num_buffers */
- info->next_buffer++;
- if (info->next_buffer >= info->num_buffers) info->next_buffer = 0;
- r = info->buffers[info->next_buffer];
- if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer;
- }
- /* after scanning every buffer and not finding anything, block */
- if (WaitForSingleObject(info->buffer_signal, 1000) == WAIT_TIMEOUT) {
-#ifdef MMDEBUG
- printf("PortMidi warning: get_free_output_buffer() "
- "wait timed out after 1000ms\n");
-#endif
- /* if we're trying to send a sysex message, maybe the
- * message is too big and we need more message buffers.
- * Expand the buffer pool by 128KB using 1024-byte buffers.
- */
- /* first, expand the buffers array if necessary */
- if (!info->buffers_expanded) {
- LPMIDIHDR *new_buffers = (LPMIDIHDR *) pm_alloc(
- (info->num_buffers + NUM_EXPANSION_BUFFERS) *
- sizeof(LPMIDIHDR));
- /* if no memory, we could return a no-memory error, but user
- * probably will be unprepared to deal with it. Maybe the
- * MIDI driver is temporarily hung so we should just wait.
- * I don't know the right answer, but waiting is easier.
- */
- if (!new_buffers) continue;
- /* copy buffers to new_buffers and replace buffers */
- memcpy(new_buffers, info->buffers,
- info->num_buffers * sizeof(LPMIDIHDR));
- pm_free(info->buffers);
- info->buffers = new_buffers;
- info->max_buffers = info->num_buffers + NUM_EXPANSION_BUFFERS;
- info->buffers_expanded = TRUE;
- }
- /* next, add one buffer and return it */
- if (info->num_buffers < info->max_buffers) {
- r = allocate_buffer(EXPANSION_BUFFER_LEN);
- /* again, if there's no memory, we may not really be
- * dead -- maybe the system is temporarily hung and
- * we can just wait longer for a message buffer */
- if (!r) continue;
- info->buffers[info->num_buffers++] = r;
- goto found_buffer; /* break out of 2 loops */
- }
- /* else, we've allocated all NUM_EXPANSION_BUFFERS buffers,
- * and we have no free buffers to send. We'll just keep
- * polling to see if any buffers show up.
- */
- }
- }
-found_buffer:
- r->dwBytesRecorded = 0;
- /* actual buffer length is saved in dwUser field */
- r->dwBufferLength = (DWORD) r->dwUser;
- return r;
-}
-
-/*
-============================================================================
-begin midi input implementation
-============================================================================
-*/
-
-
-static unsigned int allocate_input_buffer(HMIDIIN h, long buffer_len)
-{
- LPMIDIHDR hdr = allocate_buffer(buffer_len);
- if (!hdr) return pmInsufficientMemory;
- /* note: pm_hosterror is normally a boolean, but here, we store Win
- * error code. The caller must test the value for nonzero, set
- * pm_hosterror_text, and then set pm_hosterror to TRUE */
- pm_hosterror = midiInPrepareHeader(h, hdr, sizeof(MIDIHDR));
- if (pm_hosterror) {
- pm_free(hdr);
- return pm_hosterror;
- }
- pm_hosterror = midiInAddBuffer(h, hdr, sizeof(MIDIHDR));
- return pm_hosterror;
-}
-
-
-static winmm_info_type winmm_info_create()
-{
- winmm_info_type info = (winmm_info_type) pm_alloc(sizeof(winmm_info_node));
- info->handle.in = NULL;
- info->handle.out = NULL;
- info->buffers = NULL; /* not used for input */
- info->num_buffers = 0; /* not used for input */
- info->max_buffers = 0; /* not used for input */
- info->buffers_expanded = FALSE; /* not used for input */
- info->next_buffer = 0; /* not used for input */
- info->buffer_signal = 0; /* not used for input */
- info->last_time = 0;
- info->first_message = TRUE; /* not used for input */
- info->sysex_mode = FALSE;
- info->sysex_word = 0;
- info->sysex_byte_count = 0;
- info->hdr = NULL; /* not used for input */
- info->sync_time = 0;
- info->delta = 0;
- return info;
-}
-
-
-static void report_hosterror(LPWCH error_msg)
-{
- WideCharToMultiByte(CP_UTF8, 0, error_msg, -1, pm_hosterror_text,
- sizeof(pm_hosterror_text), NULL, NULL);
- if (pm_hosterror == MMSYSERR_NOMEM) {
- /* add explanation to Window's confusing error message */
- /* if there's room: */
- if (PM_HOST_ERROR_MSG_LEN - strlen(pm_hosterror_text) > 60) {
- strcat_s(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
- " Probably this MIDI device is open "
- "in another application.");
- }
- }
- pm_hosterror = TRUE;
-}
-
-
-static void report_hosterror_in()
-{
- WCHAR error_msg[PM_HOST_ERROR_MSG_LEN];
- int err = midiInGetErrorText(pm_hosterror, error_msg,
- PM_HOST_ERROR_MSG_LEN);
- assert(err == MMSYSERR_NOERROR);
- report_hosterror(error_msg);
-}
-
-
-static void report_hosterror_out()
-{
- WCHAR error_msg[PM_HOST_ERROR_MSG_LEN];
- int err = midiOutGetErrorText(pm_hosterror, error_msg,
- PM_HOST_ERROR_MSG_LEN);
- assert(err == MMSYSERR_NOERROR);
- report_hosterror(error_msg);
-}
-
-
-static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
-{
- DWORD dwDevice;
- int i = midi->device_id;
- int max_sysex_len = midi->buffer_len * 4;
- int num_input_buffers = max_sysex_len / INPUT_SYSEX_LEN;
- winmm_info_type info;
-
- dwDevice = (DWORD) (intptr_t) pm_descriptors[i].descriptor;
-
- /* create system dependent device data */
- info = winmm_info_create();
- midi->api_info = info;
- if (!info) goto no_memory;
- /* 4000 is based on Windows documentation -- that's the value used
- in the memory manager. It's small enough that it should not
- hurt performance even if it's not optimal.
- */
- InitializeCriticalSectionAndSpinCount(&info->lock, 4000);
- /* open device */
- pm_hosterror = midiInOpen(
- &(info->handle.in), /* input device handle */
- dwDevice, /* device ID */
- (DWORD_PTR) winmm_in_callback, /* callback address */
- (DWORD_PTR) midi, /* callback instance data */
- CALLBACK_FUNCTION); /* callback is a procedure */
- if (pm_hosterror) goto free_descriptor;
-
- if (num_input_buffers < MIN_INPUT_BUFFERS)
- num_input_buffers = MIN_INPUT_BUFFERS;
- for (i = 0; i < num_input_buffers; i++) {
- if (allocate_input_buffer(info->handle.in, INPUT_SYSEX_LEN)) {
- /* either pm_hosterror was set, or the proper return code
- is pmInsufficientMemory */
- goto close_device;
- }
- }
- /* start device */
- pm_hosterror = midiInStart(info->handle.in);
- if (!pm_hosterror) {
- return pmNoError;
- }
-
- /* undo steps leading up to the detected error */
-
- /* ignore return code (we already have an error to report) */
- midiInReset(info->handle.in);
-close_device:
- midiInClose(info->handle.in); /* ignore return code */
-free_descriptor:
- midi->api_info = NULL;
- pm_free(info);
-no_memory:
- if (pm_hosterror) {
- report_hosterror_in();
- return pmHostError;
- }
- /* if !pm_hosterror, then the error must be pmInsufficientMemory */
- return pmInsufficientMemory;
- /* note: if we return an error code, the device will be
- closed and memory will be freed. It's up to the caller
- to free the parameter midi */
-}
-
-
-/* winmm_in_close -- close an open midi input device */
-/*
- * assume midi is non-null (checked by caller)
- */
-static PmError winmm_in_close(PmInternal *midi)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (!info) return pmBadPtr;
- /* device to close */
- if ((pm_hosterror = midiInStop(info->handle.in))) {
- midiInReset(info->handle.in); /* try to reset and close port */
- midiInClose(info->handle.in);
- } else if ((pm_hosterror = midiInReset(info->handle.in))) {
- midiInClose(info->handle.in); /* best effort to close midi port */
- } else {
- pm_hosterror = midiInClose(info->handle.in);
- }
- midi->api_info = NULL;
- DeleteCriticalSection(&info->lock);
- pm_free(info); /* delete */
- if (pm_hosterror) {
- report_hosterror_in();
- return pmHostError;
- }
- return pmNoError;
-}
-
-
-/* Callback function executed via midiInput SW interrupt (via midiInOpen). */
-static void FAR PASCAL winmm_in_callback(
- HMIDIIN hMidiIn, /* midiInput device Handle */
- UINT wMsg, /* midi msg */
- DWORD_PTR dwInstance, /* application data */
- DWORD_PTR dwParam1, /* MIDI data */
- DWORD_PTR dwParam2) /* device timestamp (wrt most recent midiInStart) */
-{
- PmInternal *midi = (PmInternal *) dwInstance;
- winmm_info_type info = (winmm_info_type) midi->api_info;
-
- /* NOTE: we do not just EnterCriticalSection() here because an
- * MIM_CLOSE message arrives when the port is closed, but then
- * the info->lock has been destroyed.
- */
-
- switch (wMsg) {
- case MIM_DATA: {
- /* if this callback is reentered with data, we're in trouble.
- * It's hard to imagine that Microsoft would allow callbacks
- * to be reentrant -- isn't the model that this is like a
- * hardware interrupt? -- but I've seen reentrant behavior
- * using a debugger, so it happens.
- */
- EnterCriticalSection(&info->lock);
-
- /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
- message LOB;
- dwParam2 is time message received by input device driver, specified
- in [ms] from when midiInStart called.
- each message is expanded to include the status byte */
-
- if ((dwParam1 & 0x80) == 0) {
- /* not a status byte -- ignore it. This happened running the
- sysex.c test under Win2K with MidiMan USB 1x1 interface,
- but I can't reproduce it. -RBD
- */
- /* printf("non-status byte found\n"); */
- } else { /* data to process */
- PmEvent event;
- if (midi->time_proc)
- dwParam2 = (*midi->time_proc)(midi->time_info);
- event.timestamp = (PmTimestamp)dwParam2;
- event.message = (PmMessage)dwParam1;
- pm_read_short(midi, &event);
- }
- LeaveCriticalSection(&info->lock);
- break;
- }
- case MIM_LONGDATA: {
- MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1;
- unsigned char *data = (unsigned char *) lpMidiHdr->lpData;
- unsigned int processed = 0;
- int remaining = lpMidiHdr->dwBytesRecorded;
-
- EnterCriticalSection(&info->lock);
- /* printf("midi_in_callback -- lpMidiHdr %x, %d bytes, %2x...\n",
- lpMidiHdr, lpMidiHdr->dwBytesRecorded, *data); */
- if (midi->time_proc)
- dwParam2 = (*midi->time_proc)(midi->time_info);
- /* can there be more than one message in one buffer? */
- /* assume yes and iterate through them */
- pm_read_bytes(midi, data + processed, remaining, (PmTimestamp)dwParam2);
-
- /* when a device is closed, the pending MIM_LONGDATA buffers are
- returned to this callback with dwBytesRecorded == 0. In this
- case, we do not want to send them back to the interface (if
- we do, the interface will not close, and Windows OS may hang). */
- if (lpMidiHdr->dwBytesRecorded > 0) {
- MMRESULT rslt;
- lpMidiHdr->dwBytesRecorded = 0;
- lpMidiHdr->dwFlags = 0;
-
- /* note: no error checking -- can this actually fail? */
- rslt = midiInPrepareHeader(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
- assert(rslt == MMSYSERR_NOERROR);
- /* note: I don't think this can fail except possibly for
- * MMSYSERR_NOMEM, but the pain of reporting this
- * unlikely but probably catastrophic error does not seem
- * worth it.
- */
- rslt = midiInAddBuffer(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
- assert(rslt == MMSYSERR_NOERROR);
- LeaveCriticalSection(&info->lock);
- } else {
- midiInUnprepareHeader(hMidiIn,lpMidiHdr,sizeof(MIDIHDR));
- LeaveCriticalSection(&info->lock);
- pm_free(lpMidiHdr);
- }
- break;
- }
- case MIM_OPEN:
- break;
- case MIM_CLOSE:
- break;
- case MIM_ERROR:
- /* printf("MIM_ERROR\n"); */
- break;
- case MIM_LONGERROR:
- /* printf("MIM_LONGERROR\n"); */
- break;
- default:
- break;
- }
-}
-
-/*
-===========================================================================
-begin midi output implementation
-===========================================================================
-*/
-
-/* begin helper routines used by midiOutStream interface */
-
-/* add_to_buffer -- adds timestamped short msg to buffer, returns fullp */
-static int add_to_buffer(winmm_info_type m, LPMIDIHDR hdr,
- unsigned long delta, unsigned long msg)
-{
- unsigned long *ptr = (unsigned long *)
- (hdr->lpData + hdr->dwBytesRecorded);
- *ptr++ = delta; /* dwDeltaTime */
- *ptr++ = 0; /* dwStream */
- *ptr++ = msg; /* dwEvent */
- hdr->dwBytesRecorded += 3 * sizeof(long);
- /* if the addition of three more words (a message) would extend beyond
- the buffer length, then return TRUE (full)
- */
- return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength;
-}
-
-
-static PmTimestamp pm_time_get(winmm_info_type info)
-{
- MMTIME mmtime;
- MMRESULT wRtn;
- mmtime.wType = TIME_TICKS;
- mmtime.u.ticks = 0;
- wRtn = midiStreamPosition(info->handle.stream, &mmtime, sizeof(mmtime));
- assert(wRtn == MMSYSERR_NOERROR);
- return mmtime.u.ticks;
-}
-
-
-/* end helper routines used by midiOutStream interface */
-
-
-static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
-{
- DWORD dwDevice;
- int i = midi->device_id;
- winmm_info_type info;
- MIDIPROPTEMPO propdata;
- MIDIPROPTIMEDIV divdata;
- int max_sysex_len = midi->buffer_len * 4;
- int output_buffer_len;
- int num_buffers;
- dwDevice = (DWORD) (intptr_t) pm_descriptors[i].descriptor;
-
- /* create system dependent device data */
- info = winmm_info_create();
- midi->api_info = info;
- if (!info) goto no_memory;
- /* create a signal */
- info->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
- /* this should only fail when there are very serious problems */
- assert(info->buffer_signal);
- /* open device */
- if (midi->latency == 0) {
- /* use simple midi out calls */
- pm_hosterror = midiOutOpen(
- (LPHMIDIOUT) & info->handle.out, /* device Handle */
- dwDevice, /* device ID */
- /* note: same callback fn as for StreamOpen: */
- (DWORD_PTR) winmm_streamout_callback, /* callback fn */
- (DWORD_PTR) midi, /* callback instance data */
- CALLBACK_FUNCTION); /* callback type */
- } else {
- /* use stream-based midi output (schedulable in future) */
- pm_hosterror = midiStreamOpen(
- &info->handle.stream, /* device Handle */
- (LPUINT) & dwDevice, /* device ID pointer */
- 1, /* reserved, must be 1 */
- (DWORD_PTR) winmm_streamout_callback,
- (DWORD_PTR) midi, /* callback instance data */
- CALLBACK_FUNCTION);
- }
- if (pm_hosterror != MMSYSERR_NOERROR) {
- goto free_descriptor;
- }
-
- if (midi->latency == 0) {
- num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
- output_buffer_len = max_sysex_len / num_buffers;
- if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
- output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
- } else {
- num_buffers = max(midi->buffer_len, midi->latency / 2);
- if (num_buffers < MIN_STREAM_BUFFERS)
- num_buffers = MIN_STREAM_BUFFERS;
- output_buffer_len = STREAM_BUFFER_LEN;
-
- propdata.cbStruct = sizeof(MIDIPROPTEMPO);
- propdata.dwTempo = 480000; /* microseconds per quarter */
- pm_hosterror = midiStreamProperty(info->handle.stream,
- (LPBYTE) & propdata,
- MIDIPROP_SET | MIDIPROP_TEMPO);
- if (pm_hosterror) goto close_device;
-
- divdata.cbStruct = sizeof(MIDIPROPTEMPO);
- divdata.dwTimeDiv = 480; /* divisions per quarter */
- pm_hosterror = midiStreamProperty(info->handle.stream,
- (LPBYTE) & divdata,
- MIDIPROP_SET | MIDIPROP_TIMEDIV);
- if (pm_hosterror) goto close_device;
- }
- /* allocate buffers */
- if (allocate_buffers(info, output_buffer_len, num_buffers))
- goto free_buffers;
- /* start device */
- if (midi->latency != 0) {
- pm_hosterror = midiStreamRestart(info->handle.stream);
- if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
- }
- return pmNoError;
-
-free_buffers:
- /* buffers are freed below by winmm_out_delete */
-close_device:
- midiOutClose(info->handle.out);
-free_descriptor:
- midi->api_info = NULL;
- winmm_out_delete(midi); /* frees buffers and m */
-no_memory:
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- return pmInsufficientMemory;
-}
-
-
-/* winmm_out_delete -- carefully free data associated with midi */
-/**/
-static void winmm_out_delete(PmInternal *midi)
-{
- int i;
- /* delete system dependent device data */
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (info) {
- if (info->buffer_signal) {
- /* don't report errors -- better not to stop cleanup */
- CloseHandle(info->buffer_signal);
- }
- /* if using stream output, free buffers */
- for (i = 0; i < info->num_buffers; i++) {
- if (info->buffers[i]) pm_free(info->buffers[i]);
- }
- info->num_buffers = 0;
- pm_free(info->buffers);
- info->max_buffers = 0;
- }
- midi->api_info = NULL;
- pm_free(info); /* delete */
-}
-
-
-/* see comments for winmm_in_close */
-static PmError winmm_out_close(PmInternal *midi)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (info->handle.out) {
- /* device to close */
- if (midi->latency == 0) {
- pm_hosterror = midiOutClose(info->handle.out);
- } else {
- pm_hosterror = midiStreamClose(info->handle.stream);
- }
- /* regardless of outcome, free memory */
- winmm_out_delete(midi);
- }
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- return pmNoError;
-}
-
-
-static PmError winmm_out_abort(PmInternal *midi)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
-
- /* only stop output streams */
- if (midi->latency > 0) {
- pm_hosterror = midiStreamStop(info->handle.stream);
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- }
- return pmNoError;
-}
-
-
-static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- assert(info);
- if (info->hdr) {
- pm_hosterror = midiOutPrepareHeader(info->handle.out, info->hdr,
- sizeof(MIDIHDR));
- if (pm_hosterror) {
- /* do not send message */
- } else if (midi->latency == 0) {
- /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded
- * should be zero. This is set in get_free_sysex_buffer().
- * The msg length goes in dwBufferLength in spite of what
- * Microsoft documentation says (or doesn't say). */
- info->hdr->dwBufferLength = info->hdr->dwBytesRecorded;
- info->hdr->dwBytesRecorded = 0;
- pm_hosterror = midiOutLongMsg(info->handle.out, info->hdr,
- sizeof(MIDIHDR));
- } else {
- pm_hosterror = midiStreamOut(info->handle.stream, info->hdr,
- sizeof(MIDIHDR));
- }
- midi->fill_base = NULL;
- info->hdr = NULL;
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- }
- return pmNoError;
-}
-
-
-static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- PmError rslt = pmNoError;
- assert(info);
-
- if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
- pm_hosterror = midiOutShortMsg(info->handle.out, event->message);
- if (pm_hosterror) {
- if (info->hdr) { /* device disconnect may delete hdr */
- info->hdr->dwFlags = 0; /* release the buffer */
- }
- report_hosterror_out();
- return pmHostError;
- }
- } else { /* use midiStream interface -- pass data through buffers */
- unsigned long when = event->timestamp;
- unsigned long delta;
- int full;
- if (when == 0) when = midi->now;
- /* when is in real_time; translate to intended stream time */
- when = when + info->delta + midi->latency;
- /* make sure we don't go backward in time */
- if (when < info->last_time) when = info->last_time;
- delta = when - info->last_time;
- info->last_time = when;
- /* before we insert any data, we must have a buffer */
- if (info->hdr == NULL) {
- /* stream interface: buffers allocated when stream is opened */
- info->hdr = get_free_output_buffer(midi);
- }
- full = add_to_buffer(info, info->hdr, delta, event->message);
- /* note: winmm_write_flush sets pm_hosterror etc. on host error */
- if (full) rslt = winmm_write_flush(midi, when);
- }
- return rslt;
-}
-
-#define winmm_begin_sysex winmm_write_flush
-#ifndef winmm_begin_sysex
-static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp)
-{
- winmm_info_type m = (winmm_info_type) midi->api_info;
- PmError rslt = pmNoError;
-
- if (midi->latency == 0) {
- /* do nothing -- it's handled in winmm_write_byte */
- } else {
- /* sysex expects an empty sysex buffer, so send whatever is here */
- rslt = winmm_write_flush(midi);
- }
- return rslt;
-}
-#endif
-
-static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp)
-{
- /* could check for callback_error here, but I haven't checked
- * what happens if we exit early and don't finish the sysex msg
- * and clean up
- */
- winmm_info_type info = (winmm_info_type) midi->api_info;
- PmError rslt = pmNoError;
- LPMIDIHDR hdr = info->hdr;
- if (!hdr) return rslt; /* something bad happened earlier,
- do not report an error because it would have been
- reported (at least) once already */
- /* a(n old) version of MIDI YOKE requires a zero byte after
- * the sysex message, but do not increment dwBytesRecorded: */
- hdr->lpData[hdr->dwBytesRecorded] = 0;
- if (midi->latency == 0) {
-#ifdef DEBUG_PRINT_BEFORE_SENDING_SYSEX
- /* DEBUG CODE: */
- { int i; int len = info->hdr->dwBufferLength;
- printf("OutLongMsg %d ", len);
- for (i = 0; i < len; i++) {
- printf("%2x ", (unsigned char) (info->hdr->lpData[i]));
- }
- }
-#endif
- } else {
- /* Using stream interface. There are accumulated bytes in info->hdr
- to send using midiStreamOut
- */
- /* add bytes recorded to MIDIEVENT length, but don't
- count the MIDIEVENT data (3 longs) */
- MIDIEVENT *evt = (MIDIEVENT *) (hdr->lpData);
- evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
- /* round up BytesRecorded to multiple of 4 */
- hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
- }
- rslt = winmm_write_flush(midi, timestamp);
- return rslt;
-}
-
-
-static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
- PmTimestamp timestamp)
-{
- /* write a sysex byte */
- PmError rslt = pmNoError;
- winmm_info_type info = (winmm_info_type) midi->api_info;
- LPMIDIHDR hdr = info->hdr;
- unsigned char *msg_buffer;
- assert(info);
- if (!hdr) {
- info->hdr = hdr = get_free_output_buffer(midi);
- assert(hdr);
- midi->fill_base = (unsigned char *) info->hdr->lpData;
- midi->fill_offset_ptr = (uint32_t *) &(hdr->dwBytesRecorded);
- /* when buffer fills, Pm_WriteSysEx will revert to calling
- * pmwin_write_byte, which expect to have space, so leave
- * one byte free for pmwin_write_byte. Leave another byte
- * of space for zero after message to make early version of
- * MIDI YOKE driver happy -- therefore dwBufferLength - 2 */
- midi->fill_length = hdr->dwBufferLength - 2;
- if (midi->latency != 0) {
- unsigned long when = (unsigned long) timestamp;
- unsigned long delta;
- unsigned long *ptr;
- if (when == 0) when = midi->now;
- /* when is in real_time; translate to intended stream time */
- when = when + info->delta + midi->latency;
- /* make sure we don't go backward in time */
- if (when < info->last_time) when = info->last_time;
- delta = when - info->last_time;
- info->last_time = when;
-
- ptr = (unsigned long *) hdr->lpData;
- *ptr++ = delta;
- *ptr++ = 0;
- *ptr = MEVT_F_LONG;
- hdr->dwBytesRecorded = 3 * sizeof(long);
- /* data will be added at an offset of dwBytesRecorded ... */
- }
- }
- /* add the data byte */
- msg_buffer = (unsigned char *) (hdr->lpData);
- msg_buffer[hdr->dwBytesRecorded++] = byte;
-
- /* see if buffer is full, leave one byte extra for pad */
- if (hdr->dwBytesRecorded >= hdr->dwBufferLength - 1) {
- /* write what we've got and continue */
- rslt = winmm_end_sysex(midi, timestamp);
- }
- return rslt;
-}
-
-
-static PmTimestamp winmm_synchronize(PmInternal *midi)
-{
- winmm_info_type info;
- unsigned long pm_stream_time_2;
- unsigned long real_time;
- unsigned long pm_stream_time;
-
- /* only synchronize if we are using stream interface */
- if (midi->latency == 0) return 0;
-
- /* figure out the time */
- info = (winmm_info_type) midi->api_info;
- pm_stream_time_2 = pm_time_get(info);
-
- do {
- /* read real_time between two reads of stream time */
- pm_stream_time = pm_stream_time_2;
- real_time = (*midi->time_proc)(midi->time_info);
- pm_stream_time_2 = pm_time_get(info);
- /* repeat if more than 1ms elapsed */
- } while (pm_stream_time_2 > pm_stream_time + 1);
- info->delta = pm_stream_time - real_time;
- info->sync_time = real_time;
- return real_time;
-}
-
-
-/* winmm_streamout_callback -- unprepare (free) buffer header */
-static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
-{
- PmInternal *midi = (PmInternal *) dwInstance;
- winmm_info_type info = (winmm_info_type) midi->api_info;
- LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
- int err;
-
- /* Even if an error is pending, I think we should unprepare msgs and
- signal their arrival
- */
- /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
- hdr, wMsg, MOM_DONE); */
- if (wMsg == MOM_DONE) {
- MMRESULT ret = midiOutUnprepareHeader(info->handle.out, hdr,
- sizeof(MIDIHDR));
- assert(ret == MMSYSERR_NOERROR);
- } else if (wMsg == MOM_CLOSE) {
- /* The streaming API gets a callback when the device is closed.
- * The non-streaming API gets a callback when the device is
- * removed or closed. It is misleading to set is_removed when
- * the device is closed normally, but in that case, midi itself
- * will be freed immediately, so there should be no way to
- * observe is_removed == TRUE. On the other hand, if the device
- * is removed, setting is_removed will cause PortMidi to return
- * the pmDeviceRemoved error on attempts to output to the device.
- * In the case of normal closing, due to midiOutClose(),
- * the call below is reentrant (!), but for some reason this does
- * not cause an error or infinite recursion, so we are not taking
- * any precautions to flag midi as "in the process of closing."
- */
- midi->is_removed = TRUE;
- midiOutClose(info->handle.out);
- }
- /* signal client in case it is blocked waiting for buffer */
- err = SetEvent(info->buffer_signal);
- assert(err); /* false -> error */
-}
-
-
-/*
-===========================================================================
-begin exported functions
-===========================================================================
-*/
-
-#define winmm_in_abort pm_fail_fn
-pm_fns_node pm_winmm_in_dictionary = {
- none_write_short,
- none_sysex,
- none_sysex,
- none_write_byte,
- none_write_short,
- none_write_flush,
- winmm_synchronize,
- winmm_in_open,
- winmm_in_abort,
- winmm_in_close,
- success_poll,
- winmm_check_host_error
- };
-
-pm_fns_node pm_winmm_out_dictionary = {
- winmm_write_short,
- winmm_begin_sysex,
- winmm_end_sysex,
- winmm_write_byte,
- /* short realtime message: */ winmm_write_short,
- winmm_write_flush,
- winmm_synchronize,
- winmm_out_open,
- winmm_out_abort,
- winmm_out_close,
- none_poll,
- winmm_check_host_error
- };
-
-
-/* initialize winmm interface. Note that if there is something wrong
- with winmm (e.g. it is not supported or installed), it is not an
- error. We should simply return without having added any devices to
- the table. Hence, no error code is returned. Furthermore, this init
- code is called along with every other supported interface, so the
- user would have a very hard time figuring out what hardware and API
- generated the error. Finally, it would add complexity to pmwin.c to
- remember where the error code came from in order to convert to text.
- */
-void pm_winmm_init( void )
-{
- pm_winmm_mapper_input();
- pm_winmm_mapper_output();
- pm_winmm_general_inputs();
- pm_winmm_general_outputs();
-}
-
-
-/* no error codes are returned, even if errors are encountered, because
- there is probably nothing the user could do (e.g. it would be an error
- to retry.
- */
-void pm_winmm_term( void )
-{
- int i;
-#ifdef MMDEBUG
- int doneAny = 0;
- printf("pm_winmm_term called\n");
-#endif
- for (i = 0; i < pm_descriptor_len; i++) {
- PmInternal *midi = pm_descriptors[i].pm_internal;
- if (midi) {
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (info->handle.out) {
- /* close next open device*/
-#ifdef MMDEBUG
- if (doneAny == 0) {
- printf("begin closing open devices...\n");
- doneAny = 1;
- }
-#endif
- /* close all open ports */
- (*midi->dictionary->close)(midi);
- }
- }
- }
- if (midi_in_caps) {
- pm_free(midi_in_caps);
- midi_in_caps = NULL;
- }
- if (midi_out_caps) {
- pm_free(midi_out_caps);
- midi_out_caps = NULL;
- }
-#ifdef MMDEBUG
- if (doneAny) {
- printf("warning: devices were left open. They have been closed.\n");
- }
- printf("pm_winmm_term exiting\n");
-#endif
- pm_descriptor_len = 0;
-}
diff --git a/portmidi/pm_win/pmwinmm.h b/portmidi/pm_win/pmwinmm.h deleted file mode 100755 index 4a353e2..0000000 --- a/portmidi/pm_win/pmwinmm.h +++ /dev/null @@ -1,5 +0,0 @@ -/* pmwinmm.h -- system-specific definitions for windows multimedia API */
-
-void pm_winmm_init( void );
-void pm_winmm_term( void );
-
diff --git a/portmidi/pm_win/static.cmake b/portmidi/pm_win/static.cmake deleted file mode 100644 index 7fdad18..0000000 --- a/portmidi/pm_win/static.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# static.cmake -- change flags to link with static runtime libraries
-#
-# Even when BUILD_SHARED_LIBS is OFF, CMake specifies linking wtih
-# multithread DLL, so you give inconsistent linking instructions
-# resulting in warning messages from MS Visual Studio. If you want
-# a static binary, I've found this approach works to eliminate
-# warnings and make everything static:
-#
-# Changes /MD (multithread DLL) to /MT (multithread static)
-
-if(MSVC)
- foreach(flag_var
- CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
- CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO
- CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
- CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
- if(${flag_var} MATCHES "/MD")
- string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
- endif(${flag_var} MATCHES "/MD")
- endforeach(flag_var)
-
- message(STATUS
- "Note: overriding CMAKE_*_FLAGS_* to use Visual C static multithread library")
-endif(MSVC)
|
