Update of /cvsroot/audacity/lib-src/portmidi/pm_win
In directory 
23jxhf1.ch3.sourceforge.com:/tmp/cvs-serv11472/lib-src/portmidi/pm_win

Added Files:
        debugging_dlls.txt pmwin.c pmwinmm.c pmwinmm.h README_WIN.txt 
Log Message:
Added the 'portmidi' library (used for MIDI playback) to the repository.
Edited AudioIO.cpp to allow for MIDI playback (all changes are wrapped within 
EXPERIMENTAL_MIDI_OUT).
Edited MidiIOprefs.cpp to allow changing MIDI devices via the MIDI preferences 
panel.
Other various MIDI-related bug fixes in portmidi, portsmf, and libscorealign.

--- NEW FILE: debugging_dlls.txt ---
========================================================================================================================
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. 

--- NEW FILE: pmwin.c ---
/* 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();
#ifdef DEBUG
#define STRING_MAX 80
    {
        char line[STRING_MAX];
        printf("Type ENTER...\n");
        /* note, w/o this prompting, client console application can not see one
           of its errors before closing. */
        fgets(line, STRING_MAX, stdin);
    }
#endif
}


/* pm_init is the windows-dependent initialization.*/
void pm_init(void)
{
    atexit(pm_exit);
#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) {
    HKEY hkey;
#define PATTERN_MAX 256
    char pattern[PATTERN_MAX];
    long pattern_max = PATTERN_MAX;
    DWORD dwType;
    /* Find first input or device -- this is the default. */
    PmDeviceID id = pmNoDevice;
    int i, j;
    Pm_Initialize(); /* make sure descriptors exist! */
    for (i = 0; i < pm_descriptor_index; i++) {
        if (descriptors[i].pub.input == is_input) {
            id = i;
            break;
        }
    }
    /* Look in registry for a default device name pattern. */
    if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ, &hkey) != 
        ERROR_SUCCESS) {
        return id;
    }
    if (RegOpenKeyEx(hkey, "JavaSoft", 0, KEY_READ, &hkey) !=
        ERROR_SUCCESS) {
        return id;
    }
    if (RegOpenKeyEx(hkey, "Prefs", 0, KEY_READ, &hkey) !=
        ERROR_SUCCESS) {
        return id;
    }
    if (RegOpenKeyEx(hkey, "/Port/Midi", 0, KEY_READ, &hkey) !=
        ERROR_SUCCESS) {
        return id;
    }
    if (RegQueryValueEx(hkey, key, NULL, &dwType, pattern, &pattern_max) != 
        ERROR_SUCCESS) {
        return id;
    }

    /* decode pattern: upper case encoded with "/" prefix */
    i = j = 0;
    while (pattern[i]) {
        if (pattern[i] == '/' && pattern[i + 1]) {
            pattern[j++] = toupper(pattern[++i]);
        } else {
            pattern[j++] = tolower(pattern[i]);
        }
        i++;
    }
    pattern[j] = 0; /* end of string */

    /* now pattern is the string from the registry; search for match */
    i = pm_find_default_device(pattern, is_input);
    if (i != pmNoDevice) {
        id = i;
    }
    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); 
}



--- NEW FILE: pmwinmm.h ---
/* midiwin32.h -- system-specific definitions */

void pm_winmm_init( void );
void pm_winmm_term( void );


--- NEW FILE: README_WIN.txt ---
File: PortMidi Win32 Readme
Author: Belinda Thom, June 16 2002
Revised by: Roger Dannenberg, June 2002, May 2004, June 2007, 
            Umpei Kurokawa, June 2007

=============================================================================
USING PORTMIDI:
=============================================================================

Using Microsoft Visual C++ project files (provided with PortMidi), there
are two configurations of the PortMidi library. The Debug version is 
intended for debugging, especially in a console application. The Debug
version enables some extra error checking and outputs some text as well
as a prompt to type ENTER so that you don't lose any debugging text when
the program exits. You can turn off this extra debugging info by taking
out the compile-time definition for DEBUG. (But leave _DEBUG, which I
think is important for compiling in Debug mode.) This debugging version also
defines PM_CHECK_ERRORS, which forces a check for error return codes from
every call to PortMidi. You can disable this checking (especially if you
want to handle error codes in your own way) by removing PM_CHECK_ERRORS
from the predefined symbols list in the Settings dialog box.

PortMidi is designed to run without a console and should work perfectly 
well within a graphical user interface application. The Release version
is both optimized and lacking the debugging printout code of the Debug
version.

Read the portmidi.h file for PortMidi API details on using the PortMidi API.
See <...>\pm_dll_test\test.c or <...>\multithread\test.c for usage examples.

=============================================================================
TO INSTALL PORTMIDI:
=============================================================================
1)  get current source from the portmedia project at SourceForge.net

2)  copy source into directory: <...>\portmidi

=============================================================================
TO COMPILE PORTMIDI:
=============================================================================

3)  cd to or open the portmidi directory

4)  start or click on the portmidi.sln workspace (note, there is also
    portmidi-VC9.sln for Visual C++ version 9 users).
        
5)  the following projects exist within this workspace:
    - portmidi (the PortMidi library)
    - porttime (a small portable library implementing timer facilities)
    - test (simple midi I/O testing)
    - midithread (an example illustrating low-latency MIDI processing
        using a dedicated low-latency thread)
    - sysex (simple sysex message I/O testing)
    - latency (uses porttime to measure system latency)
    - midithru (an example illustrating software MIDI THRU)
    - qtest (a test of the new multicore-safe queue implementation)
    - mm  (allows monitoring of midi messages)
    - pmjni (a dll to provide an interface to PortMidi for Java)

6)  open the pmjni project properties
    - visit Configuration Properties, C/C++, General
    - find Additional Include Directories property and open the editor (...)
    - at the end of the list, you will find two paths beginning with E:\
    - these are absolute paths to the Java SDK; you'll need to install the
      Java SDK (from Sun) and update these directories in order to build
      this project.

6)  verify that all project settings are for Win32 Debug release:
    - type Alt-F7
    - highlight all three projects in left part of Project Settings window; 
    - "Settings For" should say "Win32 Debug"
        
    -In Visual C++ 2005 Express Edition, there is a drop down menu in 
     the top toolbar to select the Win32 and Debug option.

7)  use Build->Batch Build ... to build everything in the project
        
    -In Visual C++ 2005 Express Edition, use Build->Build Solution
        
8)  The settings for these projects were distributed in the zip file, so
    compile should just work.

9)  run test project; use the menu that shows up from the command prompt to
    test that portMidi works on your system. tests include: 
                - verify midi output works
                - verify midi input works

10) run other projects if you wish: sysex, latency, midithread, mm, 
    qtest, midithru

11) use pm_java/make.bat (run in a cmd window from pm_java) to compile
    the java code.

12) run pm_java/pmdefaults.bat (run in a cmd window from pm_java) to
    run the PmDefaults program. This lets you select the default input
    and output devices for PortMidi.

============================================================================
TO CREATE YOUR OWN PORTMIDI CLIENT APPLICATION:
============================================================================

NOTE: this section needs to be reviewed and tested. My suggestion would
be to copy the test project file (test.dsp) and modify it. -RBD

The easiest way is to start a new project w/in the portMidi workspace:

1) To open new project: 
        - File->New->Projects
        - Location: <...>\portmidi\<yourProjectName>
        - check Add to current workspace
        - select Win32 Console Application (recommended for now)
        - do *NOT* select the "make dependency" box (you will explicitly do this
      in the next step)
        - Click OK
        - Select "An Empty Project" and click Finish
        
        In Visual C++ 2005 Express Edition, 
        - File->New->Projects
        - Location: <...>\portmidi\<yourProjectName>
        - select Add to solution
        - select CLR Empty project in CLR
        - select Win32 Console Application in Win32
        - select Empty project in General
        
2) Now this project will be the active project. Make it explicitly depend
   on PortMidi dll:
        - Project->Dependencies
        - Click pm_dll

3) add whatever files you wish to add to your new project, using portMidi
   calls as desired (see USING PORTMIDI at top of this readme)

4) when you include portMidi files, do so like this:
        - #include "..\pm_common\portmidi.h"
        - etc.

5) build and run your project

============================================================================
DESIGN NOTES
============================================================================

PortMidi for Win32 exists as a simple static library,
with Win32-specific code in pmwin.c and MM-specific code in pmwinmm.c.

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 -- the most complex, least exercised, and therefore most
      buggy part of PortMidi (but maybe bugs are finally gone)

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.

-----------

Additional notes on using VS 2005 (maybe this is obsolete now?):

1) Make sure "Configuration: All Configurations" is selected in all of the 
following Properties modifications!

2) In my case the project defaulted to compiling all .c files with the C++ 
compiler, which was disastrous. I had to go to set Properties for each file, to 
wit: Expand Configuration Properties, Expand C/C++, Select Advanced, set the 
Compile As popup to Compile as C Code (/TC). (For better or worse, the project 
I inherited has a bunch of .c files that rely on C++ features, so I couldn't 
reliably set this the project properties level.)

3) While you're there, make sure that the C/C++ -> General -> "Compile with 
Common Language Runtime support" is set to "No Common Language Runtime support" 
(the C compiler *can't* support CLR, but VS won't do anything useful like 
automatically set the two options to match)-.

4) I never got VS precompiled header thing to work sensibly, so I took the path 
of least resistance and turned PCH's off for all my files. Properties -> 
Configuration Properties -> C/C++ -> Precompiled Headers -> Create/Use 
Precompiled Header popup set to "Not Using Precompiled Headers". The compiler 
is reasonably fast even if it has to parse all the header files, so unless 
someone wants to explain VS's PCHs to me, the hell with it, I say.


--- NEW FILE: pmwinmm.c ---
/* pmwinmm.c -- system specific definitions */

#ifdef _MSC_VER
 #pragma warning(disable: 4133) // stop warnings about implicit typecasts
#endif

#ifndef _WIN32_WINNT
    /* without this define, InitializeCriticalSectionAndSpinCount is 
     * undefined. This version level means "Windows 2000 and higher" 
     */
    #define _WIN32_WINNT 0x0500
#endif

#include "windows.h"
#include "mmsystem.h"
#include "portmidi.h"
#include "pmutil.h"
#include "pminternal.h"
#include "pmwinmm.h"
[...1420 lines suppressed...]
                (*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 DEBUG
    if (doneAny) {
        printf("warning: devices were left open. They have been closed.\n");
    }
    printf("pm_winmm_term exiting\n");
#endif
    pm_descriptor_index = 0;
}


------------------------------------------------------------------------------
_______________________________________________
Audacity-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/audacity-cvs

Reply via email to