[PD-cvs] pd/portmidi/pm_test latency.c, NONE, 1.1 latency.dsp, NONE, 1.1 midithread.c, NONE, 1.1 midithread.dsp, NONE, 1.1 midithru.c, NONE, 1.1 midithru.dsp, NONE, 1.1 midithru.dsw, NONE, 1.1 sysex.c, NONE, 1.1 sysex.dsp, NONE, 1.1 test.c, NONE, 1.1 test.dsp, NONE, 1.1 txdata.syx, NONE, 1.1

Hans-Christoph Steiner eighthave at users.sourceforge.net
Thu Dec 15 01:57:02 CET 2005


Update of /cvsroot/pure-data/pd/portmidi/pm_test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7035/pm_test

Added Files:
	latency.c latency.dsp midithread.c midithread.dsp midithru.c 
	midithru.dsp midithru.dsw sysex.c sysex.dsp test.c test.dsp 
	txdata.syx 
Log Message:
checking in missing files on behalf of Miller (cleared it with him first).  The files are from portmidi17nov04.zip

--- NEW FILE: midithread.dsp ---
# Microsoft Developer Studio Project File - Name="midithread" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Console Application" 0x0103

CFG=midithread - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "midithread.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "midithread.mak" CFG="midithread - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "midithread - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "midithread - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "$(CFG)" == "midithread - Win32 Release"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "midithreadRelease"
# PROP Intermediate_Dir "midithreadRelease"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../pm_common" /I "../porttime" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 ..\Release\portmidi.lib ..\porttime\Release\porttime.lib winmm.lib /nologo /subsystem:console /machine:I386

!ELSEIF  "$(CFG)" == "midithread - Win32 Debug"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "midithreadDebug"
# PROP BASE Intermediate_Dir "midithreadDebug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "midithreadDebug"
# PROP Intermediate_Dir "midithreadDebug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../pm_common" /I "../porttime" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept

!ENDIF 

# Begin Target

# Name "midithread - Win32 Release"
# Name "midithread - Win32 Debug"
# Begin Group "Source Files"

# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File

SOURCE=.\midithread.c
# End Source File
# End Group
# Begin Group "Header Files"

# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"

# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

--- NEW FILE: midithread.c ---
/* midithread.c -- example program showing how to do midi processing 
                   in a preemptive thread

  Notes: if you handle midi I/O from your main program, there will be
  some delay before handling midi messages whenever the program is
  doing something like file I/O, graphical interface updates, etc.

  To handle midi with minimal delay, you should do all midi processing
  in a separate, high priority thread. A convenient way to get a high
  priority thread in windows is to use the timer callback provided by
  the PortTime library. That is what we show here.

  If the high priority thread writes to a file, prints to the console,
  or does just about anything other than midi processing, this may 
  create delays, so all this processing should be off-loaded to the
  "main" process or thread. Communication between threads can be tricky.
  If one thread is writing at the same time the other is reading, very
  tricky race conditions can arise, causing programs to behave
  incorrectly, but only under certain timing conditions -- a terrible
  thing to debug. Advanced programmers know this as a synchronization
  problem. See any operating systems textbook for the complete story.

  To avoid synchronization problems, a simple, reliable approach is
  to communicate via messages. PortMidi offers a message queue as a
  datatype, and operations to insert and remove messages. Use two 
  queues as follows: midi_to_main transfers messages from the midi
  thread to the main thread, and main_to_midi transfers messages from
  the main thread to the midi thread. Queues are safe for use between
  threads as long as ONE thread writes and ONE thread reads. You must 
  NEVER allow two threads to write to the same queue.

  This program transposes incoming midi data by an amount controlled
  by the main program. To change the transposition, type an integer
  followed by return. The main program sends this via a message queue
  to the midi thread. To quit, type 'q' followed by return.

  The midi thread can also send a pitch to the main program on request.
  Type 'm' followed by return to wait for the next midi message and
  print the pitch.

  This program illustrates:
    Midi processing in a high-priority thread.
    Communication with a main process via message queues.

 */

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "assert.h"
#include "portmidi.h"
#include "pmutil.h"
#include "porttime.h"

/* if INPUT_BUFFER_SIZE is 0, PortMidi uses a default value */
#define INPUT_BUFFER_SIZE 0

#define OUTPUT_BUFFER_SIZE 100
#define DRIVER_INFO NULL
#define TIME_PROC NULL
#define TIME_INFO NULL
/* use zero latency because we want output to be immediate */
#define LATENCY 0

#define STRING_MAX 80

/**********************************/
/* DATA USED ONLY BY process_midi */
/* (except during initialization) */
/**********************************/

int active = FALSE;
int monitor = FALSE;
int midi_thru = TRUE;

long transpose;
PmStream *midi_in;
PmStream *midi_out;

/****************************/
/* END OF process_midi DATA */
/****************************/

/* shared queues */
PmQueue *midi_to_main;
PmQueue *main_to_midi;

#define QUIT_MSG 1000
#define MONITOR_MSG 1001
#define THRU_MSG 1002

/* timer interrupt for processing midi data */
void process_midi(PtTimestamp timestamp, void *userData)
{
    PmError result;
    PmEvent buffer; /* just one message at a time */
    long msg;

    /* do nothing until initialization completes */
    if (!active) 
        return;

    /* check for messages */
    do { 
        result = Pm_Dequeue(main_to_midi, &msg); 
        if (result) {
            if (msg >= -127 && msg <= 127) 
                transpose = msg;
            else if (msg == QUIT_MSG) {
                /* acknowledge receipt of quit message */
                Pm_Enqueue(midi_to_main, &msg);
                active = FALSE;
                return;
            } else if (msg == MONITOR_MSG) {
                /* main has requested a pitch. monitor is a flag that
                 * records the request:
                 */
                monitor = TRUE;
            } else if (msg == THRU_MSG) {
                /* toggle Thru on or off */
                midi_thru = !midi_thru;
            }
        }
    } while (result);         
    
    /* see if there is any midi input to process */
    do {
		result = Pm_Poll(midi_in);
        if (result) {
            long status, data1, data2;
            if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow) 
                continue;
            if (midi_thru) 
                Pm_Write(midi_out, &buffer, 1);
            /* unless there was overflow, we should have a message now */
            status = Pm_MessageStatus(buffer.message);
            data1 = Pm_MessageData1(buffer.message);
            data2 = Pm_MessageData2(buffer.message);
            if ((status & 0xF0) == 0x90 ||
                (status & 0xF0) == 0x80) {
                
                /* this is a note-on or note-off, so transpose and send */
                data1 += transpose;
                
                /* keep within midi pitch range, keep proper pitch class */
                while (data1 > 127) 
                    data1 -= 12;
                while (data1 < 0) 
                    data1 += 12;
                
                /* send the message */
                buffer.message = Pm_Message(status, data1, data2);
                Pm_Write(midi_out, &buffer, 1);
                
                /* if monitor is set, send the pitch to the main thread */
                if (monitor) {
                    Pm_Enqueue(midi_to_main, &data1);
                    monitor = FALSE; /* only send one pitch per request */
                }
            }
        }
    } while (result);
}

void exit_with_message(char *msg)
{
    char line[STRING_MAX];
    printf("%s\n", msg);
    fgets(line, STRING_MAX, stdin);
    exit(1);
}

int main()
{
    int id;
    long n;
    const PmDeviceInfo *info;
    char line[STRING_MAX];
    int spin;
    int done = FALSE;

    /* determine what type of test to run */
    printf("begin PortMidi multithread test...\n");
	
    /* note that it is safe to call PortMidi from the main thread for
       initialization and opening devices. You should not make any
       calls to PortMidi from this thread once the midi thread begins.
       to make PortMidi calls.
     */

    /* make the message queues */
    /* messages can be of any size and any type, but all messages in
     * a given queue must have the same size. We'll just use long's
     * for our messages in this simple example
     */
    midi_to_main = Pm_QueueCreate(32, sizeof(long));
    assert(midi_to_main != NULL);
    main_to_midi = Pm_QueueCreate(32, sizeof(long));
    assert(main_to_midi != NULL);

    /* a little test of enqueue and dequeue operations. Ordinarily, 
     * you would call Pm_Enqueue from one thread and Pm_Dequeue from
     * the other. Since the midi thread is not running, this is safe.
     */
    n = 1234567890;
    Pm_Enqueue(midi_to_main, &n);
    n = 987654321;
    Pm_Enqueue(midi_to_main, &n);
	Pm_Dequeue(midi_to_main, &n);
	if (n != 1234567890) {
        exit_with_message("Pm_Dequeue produced unexpected result.");
    }
    Pm_Dequeue(midi_to_main, &n);
	if(n != 987654321) {
        exit_with_message("Pm_Dequeue produced unexpected result.");
    }

    /* always start the timer before you start midi */
    Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */
    /* the timer will call our function, process_midi() every millisecond */
    
	Pm_Initialize();

    id = Pm_GetDefaultOutputDeviceID();
    info = Pm_GetDeviceInfo(id);
    if (info == NULL) {
        printf("Could not open default output device (%d).", id);
        exit_with_message("");
    }
    printf("Opening output device %s %s\n", info->interf, info->name);

    /* use zero latency because we want output to be immediate */
    Pm_OpenOutput(&midi_out, 
                  id, 
                  DRIVER_INFO,
                  OUTPUT_BUFFER_SIZE,
                  TIME_PROC,
                  TIME_INFO,
                  LATENCY);

    id = Pm_GetDefaultInputDeviceID();
    info = Pm_GetDeviceInfo(id);
    if (info == NULL) {
        printf("Could not open default input device (%d).", id);
        exit_with_message("");
    }
    printf("Opening input device %s %s\n", info->interf, info->name);
    Pm_OpenInput(&midi_in, 
                 id, 
                 DRIVER_INFO,
                 INPUT_BUFFER_SIZE,
                 TIME_PROC,
                 TIME_INFO);

    active = TRUE; /* enable processing in the midi thread -- yes, this
                      is a shared variable without synchronization, but
                      this simple assignment is safe */

    printf("Enter midi input; it will be transformed as specified by...\n");
    printf("%s\n%s\n%s\n",
           "Type 'q' to quit, 'm' to monitor next pitch, t to toggle thru or",
           "type a number to specify transposition.",
		   "Must terminate with [ENTER]");

    while (!done) {
        long msg;
        int len;
        fgets(line, STRING_MAX, stdin);
        /* remove the newline: */
        len = strlen(line);
        if (len > 0) line[len - 1] = 0; /* overwrite the newline char */
        if (strcmp(line, "q") == 0) {
            msg = QUIT_MSG;
            Pm_Enqueue(main_to_midi, &msg);
            /* wait for acknowlegement */
            do {
                spin = Pm_Dequeue(midi_to_main, &msg);
            } while (spin == 0); /* spin */ ;
            done = TRUE; /* leave the command loop and wrap up */
        } else if (strcmp(line, "m") == 0) {
            msg = MONITOR_MSG;
            Pm_Enqueue(main_to_midi, &msg);
            printf("Waiting for note...\n");
            do {
                spin = Pm_Dequeue(midi_to_main, &msg);
            } while (spin == 0); /* spin */ ;
            printf("... pitch is %ld\n", msg);
        } else if (strcmp(line, "t") == 0) {
            /* reading midi_thru asynchronously could give incorrect results,
               e.g. if you type "t" twice before the midi thread responds to
               the first one, but we'll do it this way anyway. Perhaps a more
               correct way would be to wait for an acknowledgement message
               containing the new state. */
            printf("Setting THRU %s\n", (midi_thru ? "off" : "on"));
            msg = THRU_MSG;
            Pm_Enqueue(main_to_midi, &msg);
        } else if (sscanf(line, "%ld", &msg) == 1) {
            if (msg >= -127 && msg <= 127) {
                /* send transposition value */
                printf("Transposing by %ld\n", msg);
                Pm_Enqueue(main_to_midi, &msg);
            } else {
                printf("Transposition must be within -127...127\n");
            }
        } else {
            printf("%s\n%s\n%s\n",
                   "Type 'q' to quit, 'm' to monitor next pitch, or",
                   "type a number to specify transposition.",
				   "Must terminate with [ENTER]");
        }
    }

    /* at this point, midi thread is inactive and we need to shut down
     * the midi input and output
     */
    Pt_Stop(); /* stop the timer */
    Pm_QueueDestroy(midi_to_main);
    Pm_QueueDestroy(main_to_midi);

    /* Belinda! if close fails here, some memory is deleted, right??? */
    Pm_Close(midi_in);
    Pm_Close(midi_out);
    
    printf("finished portMidi multithread test...enter any character to quit [RETURN]...");
    fgets(line, STRING_MAX, stdin);
    return 0;
}

--- NEW FILE: midithru.dsw ---
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!

###############################################################################

Project: "midithread"=.\midithru.dsp - Package Owner=<4>

Package=<5>
{{{
}}}

Package=<4>
{{{
}}}

###############################################################################

Global:

Package=<5>
{{{
}}}

Package=<3>
{{{
}}}

###############################################################################


--- NEW FILE: test.c ---
#include "portmidi.h"
#include "porttime.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "assert.h"

#define INPUT_BUFFER_SIZE 100
#define OUTPUT_BUFFER_SIZE 0
#define DRIVER_INFO NULL
#define TIME_PROC ((long (*)(void *)) Pt_Time)
#define TIME_INFO NULL
#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */

#define STRING_MAX 80 /* used for console input */

long latency = 0;

/* crash the program to test whether midi ports are closed */
/**/
void doSomethingReallyStupid() {
	int * tmp = NULL;
	*tmp = 5;
}


/* exit the program without any explicit cleanup */
/**/
void doSomethingStupid() {
	assert(0);
}


/* read a number from console */
/**/
int get_number(char *prompt)
{
    char line[STRING_MAX];
    int n = 0, i;
    printf(prompt);
    while (n != 1) {
        n = scanf("%d", &i);
        fgets(line, STRING_MAX, stdin);

    }
    return i;
}


/*
 * the somethingStupid parameter can be set to simulate a program crash.
 * We want PortMidi to close Midi ports automatically in the event of a
 * crash because Windows does not (and this may cause an OS crash)
 */
void main_test_input(unsigned int somethingStupid) {
    PmStream * midi;
    PmError status, length;
    PmEvent buffer[1];
    int num = 10;
    int i = get_number("Type input number: ");
    /* It is recommended to start timer before Midi; otherwise, PortMidi may
       start the timer with its (default) parameters
     */
    TIME_START;

    /* open input device */
    Pm_OpenInput(&midi, 
                 i,
                 DRIVER_INFO, 
                 INPUT_BUFFER_SIZE, 
                 TIME_PROC, 
                 TIME_INFO);

    printf("Midi Input opened. Reading %d Midi messages...\n",num);
    Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK);
    /* empty the buffer after setting filter, just in case anything
       got through */
    while (Pm_Poll(midi)) {
        Pm_Read(midi, buffer, 1);
    }
    /* now start paying attention to messages */
    i = 0; /* count messages as they arrive */
    while (i < num) {
        status = Pm_Poll(midi);
        if (status == TRUE) {
            length = Pm_Read(midi,buffer, 1);
            if (length > 0) {
                printf("Got message %d: time %ld, %2lx %2lx %2lx\n",
                       i,
                       buffer[0].timestamp,
                       Pm_MessageStatus(buffer[0].message),
                       Pm_MessageData1(buffer[0].message),
                       Pm_MessageData2(buffer[0].message));
                i++;
            } else {
                assert(0);
            }
        }
        /* simulate crash if somethingStupid is 1 or 2 */
        if ((i > (num/2)) && (somethingStupid == 1)) {
            doSomethingStupid();
        } else if ((i > (num/2)) && (somethingStupid == 2)) {
            doSomethingReallyStupid();
        }
    }

    /* close device (this not explicitly needed in most implementations) */
    printf("ready to close...");

    Pm_Close(midi);
    printf("done closing...");
}



void main_test_output() {
    PmStream * midi;
	char line[80];
    long off_time;
    int chord[] = { 60, 67, 76, 83, 90 };
    #define chord_size 5 
    PmEvent buffer[chord_size];
    PmTimestamp timestamp;

    /* determine which output device to use */
    int i = get_number("Type output number: ");

    /* It is recommended to start timer before PortMidi */
    TIME_START;

    /* open output device -- since PortMidi avoids opening a timer
       when latency is zero, we will pass in a NULL timer pointer
       for that case. If PortMidi tries to access the time_proc,
       we will crash, so this test will tell us something. */
    Pm_OpenOutput(&midi, 
                  i, 
                  DRIVER_INFO,
                  OUTPUT_BUFFER_SIZE, 
                  (latency == 0 ? NULL : TIME_PROC),
                  (latency == 0 ? NULL : TIME_INFO), 
                  latency);
    printf("Midi Output opened with %ld ms latency.\n", latency);

    /* output note on/off w/latency offset; hold until user prompts */
    printf("ready to send program 1 change... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
    /* if we were writing midi for immediate output, we could always use
       timestamps of zero, but since we may be writing with latency, we
       will explicitly set the timestamp to "now" by getting the time.
       The source of timestamps should always correspond to the TIME_PROC
       and TIME_INFO parameters used in Pm_OpenOutput(). */
    buffer[0].timestamp = TIME_PROC(TIME_INFO);
    buffer[0].message = Pm_Message(0xC0, 0, 0);
    Pm_Write(midi, buffer, 1);

    printf("ready to note-on... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
    buffer[0].timestamp = TIME_PROC(TIME_INFO);
    buffer[0].message = Pm_Message(0x90, 60, 100);
    Pm_Write(midi, buffer, 1);
    printf("ready to note-off... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
    buffer[0].timestamp = TIME_PROC(TIME_INFO);
    buffer[0].message = Pm_Message(0x90, 60, 0);
    Pm_Write(midi, buffer, 1);

    /* output short note on/off w/latency offset; hold until user prompts */
    printf("ready to note-on (short form)... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
    Pm_WriteShort(midi, TIME_PROC(TIME_INFO),
                  Pm_Message(0x90, 60, 100));
    printf("ready to note-off (short form)... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
    Pm_WriteShort(midi, TIME_PROC(TIME_INFO),
                  Pm_Message(0x90, 60, 0));

    /* output several note on/offs to test timing. 
       Should be 1s between notes */
    printf("chord will arpeggiate if latency > 0\n");
    printf("ready to chord-on/chord-off... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
    timestamp = TIME_PROC(TIME_INFO);
    for (i = 0; i < chord_size; i++) {
        buffer[i].timestamp = timestamp + 1000 * i;
        buffer[i].message = Pm_Message(0x90, chord[i], 100);
    }
    Pm_Write(midi, buffer, chord_size);

    off_time = timestamp + 1000 + chord_size * 1000; 
    while (TIME_PROC(TIME_INFO) < off_time) 
		/* busy wait */;
    for (i = 0; i < chord_size; i++) {
        buffer[i].timestamp = timestamp + 1000 * i;
        buffer[i].message = Pm_Message(0x90, chord[i], 0);
    }
    Pm_Write(midi, buffer, chord_size);    

    /* close device (this not explicitly needed in most implementations) */
    printf("ready to close and terminate... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
	
    Pm_Close(midi);
    Pm_Terminate();
    printf("done closing and terminating...\n");
}


void main_test_both()
{
    int i = 0;
    int in, out;
    PmStream * midi, * midiOut;
    PmEvent buffer[1];
    PmError status, length;
    int num = 10;
    
    in = get_number("Type input number: ");
    out = get_number("Type output number: ");

    /* In is recommended to start timer before PortMidi */
    TIME_START;

    Pm_OpenOutput(&midiOut, 
                  out, 
                  DRIVER_INFO,
                  OUTPUT_BUFFER_SIZE, 
                  TIME_PROC,
                  TIME_INFO, 
                  latency);
    printf("Midi Output opened with %ld ms latency.\n", latency);
    /* open input device */
    Pm_OpenInput(&midi, 
                 in,
                 DRIVER_INFO, 
                 INPUT_BUFFER_SIZE, 
                 TIME_PROC, 
                 TIME_INFO);
    printf("Midi Input opened. Reading %d Midi messages...\n",num);
    Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK);
    /* empty the buffer after setting filter, just in case anything
       got through */
    while (Pm_Poll(midi)) {
        Pm_Read(midi, buffer, 1);
    }
    i = 0;
    while (i < num) {
        status = Pm_Poll(midi);
        if (status == TRUE) {
            length = Pm_Read(midi,buffer,1);
            if (length > 0) {
                Pm_Write(midiOut, buffer, 1);
                printf("Got message %d: time %ld, %2lx %2lx %2lx\n",
					   i,
                       buffer[0].timestamp,
                       Pm_MessageStatus(buffer[0].message),
                       Pm_MessageData1(buffer[0].message),
                       Pm_MessageData2(buffer[0].message));
                i++;
            } else {
                assert(0);
            }
        }
    }

    /* since close device should not needed, lets get
       rid of it just to make sure program exit closes MIDI devices */
	/* Pm_Close(midi);
	  Pm_Close(midiOut);
	  Pm_Terminate(); */
}


/* main_test_stream exercises windows winmm API's stream mode */
/*    The winmm stream mode is used for latency>0, and sends
   timestamped messages. The timestamps are relative (delta) 
   times, whereas PortMidi times are absolute. Since peculiar
   things happen when messages are not always sent in advance,
   this function allows us to exercise the system and test it.
 */
void main_test_stream() {
    PmStream * midi;
	char line[80];
    PmEvent buffer[16];

	/* determine which output device to use */
    int i = get_number("Type output number: ");

	latency = 500; /* ignore LATENCY for this test and
				      fix the latency at 500ms */

    /* It is recommended to start timer before PortMidi */
    TIME_START;

	/* open output device */
    Pm_OpenOutput(&midi, 
                  i, 
                  DRIVER_INFO,
                  OUTPUT_BUFFER_SIZE, 
                  TIME_PROC,
                  TIME_INFO, 
                  latency);
    printf("Midi Output opened with %ld ms latency.\n", latency);

    /* output note on/off w/latency offset; hold until user prompts */
    printf("ready to send output... (type RETURN):");
    fgets(line, STRING_MAX, stdin);

    /* if we were writing midi for immediate output, we could always use
       timestamps of zero, but since we may be writing with latency, we
       will explicitly set the timestamp to "now" by getting the time.
       The source of timestamps should always correspond to the TIME_PROC
       and TIME_INFO parameters used in Pm_OpenOutput(). */
    buffer[0].timestamp = TIME_PROC(TIME_INFO);
    buffer[0].message = Pm_Message(0xC0, 0, 0);
	buffer[1].timestamp = buffer[0].timestamp;
	buffer[1].message = Pm_Message(0x90, 60, 100);
	buffer[2].timestamp = buffer[0].timestamp + 1000;
	buffer[2].message = Pm_Message(0x90, 62, 100);
	buffer[3].timestamp = buffer[0].timestamp + 2000;
	buffer[3].message = Pm_Message(0x90, 64, 100);
	buffer[4].timestamp = buffer[0].timestamp + 3000;
	buffer[4].message = Pm_Message(0x90, 66, 100);
	buffer[5].timestamp = buffer[0].timestamp + 4000;
	buffer[5].message = Pm_Message(0x90, 60, 0);
	buffer[6].timestamp = buffer[0].timestamp + 4000;
	buffer[6].message = Pm_Message(0x90, 62, 0);
	buffer[7].timestamp = buffer[0].timestamp + 4000;
	buffer[7].message = Pm_Message(0x90, 64, 0);
	buffer[8].timestamp = buffer[0].timestamp + 4000;
	buffer[8].message = Pm_Message(0x90, 66, 0);

    Pm_Write(midi, buffer, 9);
#ifdef SEND8
	/* Now, we're ready for the real test.
	   Play 4 notes at now, now+500, now+1000, and now+1500
	   Then wait until now+2000.
	   Play 4 more notes as before.
	   We should hear 8 evenly spaced notes. */
	now = TIME_PROC(TIME_INFO);
	for (i = 0; i < 4; i++) {
		buffer[i * 2].timestamp = now + (i * 500);
		buffer[i * 2].message = Pm_Message(0x90, 60, 100);
		buffer[i * 2 + 1].timestamp = now + 250 + (i * 500);
		buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0);
	}
    Pm_Write(midi, buffer, 8);

    while (Pt_Time() < now + 2500) 
		/* busy wait */;
	/* now we are 500 ms behind schedule, but since the latency
	   is 500, the delay should not be audible */
	now += 2000;
	for (i = 0; i < 4; i++) {
		buffer[i * 2].timestamp = now + (i * 500);
		buffer[i * 2].message = Pm_Message(0x90, 60, 100);
		buffer[i * 2 + 1].timestamp = now + 250 + (i * 500);
		buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0);
	}
    Pm_Write(midi, buffer, 8);
#endif
    /* close device (this not explicitly needed in most implementations) */
    printf("ready to close and terminate... (type RETURN):");
    fgets(line, STRING_MAX, stdin);
	
    Pm_Close(midi);
    Pm_Terminate();
    printf("done closing and terminating...\n");
}


void show_usage()
{
    printf("Usage: test [-h] [-l latency-in-ms]\n");
    exit(0);
}

int main(int argc, char *argv[])
{
    int i = 0, n = 0;
    char line[STRING_MAX];
    int test_input = 0, test_output = 0, test_both = 0, somethingStupid = 0;
	int stream_test = 0;
	int latency_valid = FALSE;
    
    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0) {
            show_usage();
        } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) {
            i = i + 1;
            latency = atoi(argv[i]);
            printf("Latency will be %ld\n", latency);
			latency_valid = TRUE;
        } else {
            show_usage();
        }
    }

	while (!latency_valid) {
		printf("Latency in ms: ");
		if (scanf("%ld", &latency) == 1) {
			latency_valid = TRUE;
		}
	}

    /* determine what type of test to run */
    printf("begin portMidi test...\n");
    printf("%s%s%s%s%s",
           "enter your choice...\n    1: test input\n",
           "    2: test input (fail w/assert)\n",
           "    3: test input (fail w/NULL assign)\n",
           "    4: test output\n    5: test both\n",
	       "    6: stream test\n");
    while (n != 1) {
        n = scanf("%d", &i);
        fgets(line, STRING_MAX, stdin);
        switch(i) {
        case 1: 
            test_input = 1;
            break;
        case 2: 
            test_input = 1;
            somethingStupid = 1;
            break;
        case 3: 
            test_input = 1;
            somethingStupid = 2;
            break;
        case 4: 
            test_output = 1;
            break;
        case 5:
            test_both = 1;
            break;
		case 6:
			stream_test = 1;
			break;
        default:
            printf("got %d (invalid input)\n", n);
            break;
        }
    }
    
    /* list device information */
    for (i = 0; i < Pm_CountDevices(); i++) {
        const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
        if (((test_input  | test_both) & info->input) |
            ((test_output | test_both | stream_test) & info->output)) {
            printf("%d: %s, %s", i, info->interf, info->name);
            if (info->input) printf(" (input)");
            if (info->output) printf(" (output)");
            printf("\n");
        }
    }
    
    /* run test */
	if (stream_test) {
		main_test_stream();
	} else if (test_input) {
        main_test_input(somethingStupid);
    } else if (test_output) {
        main_test_output();
    } else if (test_both) {
        main_test_both();
    }
    
    printf("finished portMidi test...type ENTER to quit...");
    fgets(line, STRING_MAX, stdin);
    return 0;
}

--- NEW FILE: sysex.dsp ---
# Microsoft Developer Studio Project File - Name="sysex" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Console Application" 0x0103

CFG=sysex - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "sysex.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "sysex.mak" CFG="sysex - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "sysex - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "sysex - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "$(CFG)" == "sysex - Win32 Release"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "sysexRelease"
# PROP Intermediate_Dir "sysexRelease"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\pm_common" /I "..\porttime" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\Release\portmidi.lib ..\porttime\Release\porttime.lib ..\pm_win\Release\pm_dll.lib /nologo /subsystem:console /machine:I386

!ELSEIF  "$(CFG)" == "sysex - Win32 Debug"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "sysexDebug"
# PROP Intermediate_Dir "sysexDebug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\pm_common" /I "..\porttime" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept

!ENDIF 

# Begin Target

# Name "sysex - Win32 Release"
# Name "sysex - Win32 Debug"
# Begin Group "Source Files"

# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File

SOURCE=.\sysex.c
# End Source File
# End Group
# Begin Group "Header Files"

# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"

# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

--- NEW FILE: midithru.dsp ---
# Microsoft Developer Studio Project File - Name="midithru" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Console Application" 0x0103

CFG=midithru - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "midithru.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "midithru.mak" CFG="midithru - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "midithru - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "midithru - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "$(CFG)" == "midithru - Win32 Release"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "midithruRelease"
# PROP Intermediate_Dir "midithruRelease"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../pm_common" /I "../porttime" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 ..\Release\portmidi.lib ..\porttime\Release\porttime.lib winmm.lib /nologo /subsystem:console /machine:I386

!ELSEIF  "$(CFG)" == "midithru - Win32 Debug"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "midithruDebug"
# PROP BASE Intermediate_Dir "midithruDebug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "midithruDebug"
# PROP Intermediate_Dir "midithruDebug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../pm_common" /I "../porttime" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept

!ENDIF 

# Begin Target

# Name "midithru - Win32 Release"
# Name "midithru - Win32 Debug"
# Begin Group "Source Files"

# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File

SOURCE=.\midithru.c
# End Source File
# End Group
# Begin Group "Header Files"

# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"

# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

--- NEW FILE: latency.dsp ---
# Microsoft Developer Studio Project File - Name="latency" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Console Application" 0x0103

CFG=latency - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "latency.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "latency.mak" CFG="latency - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "latency - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "latency - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "$(CFG)" == "latency - Win32 Release"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "latency___Win32_Release"
# PROP BASE Intermediate_Dir "latency___Win32_Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "latencyRelease"
# PROP Intermediate_Dir "latencyRelease"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../porttime" /I "../pm_common" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 ..\Release\portmidi.lib ..\porttime\Release\porttime.lib winmm.lib /nologo /subsystem:console /machine:I386

!ELSEIF  "$(CFG)" == "latency - Win32 Debug"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "latency___Win32_Debug"
# PROP BASE Intermediate_Dir "latency___Win32_Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "latencyDebug"
# PROP Intermediate_Dir "latencyDebug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../porttime" /I "../pm_common" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib winmm.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept

!ENDIF 

# Begin Target

# Name "latency - Win32 Release"
# Name "latency - Win32 Debug"
# Begin Group "Source Files"

# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File

SOURCE=.\latency.c
# End Source File
# End Group
# Begin Group "Header Files"

# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"

# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

--- NEW FILE: txdata.syx ---
20  0 1d  4  c  6  0 34  1 4d  4  d 1f  7  3  6 
 c 5e  4 4d  d  b 18  5  3  6  0 3d  1 4a 16 18 
1f  8  3  6  d  0  1 63  4 13 3a 23  0  0  0  2 
 c  2  4  0 63 32  0  0  0 32  0 47 72 61 6e 64 
50 69 61 6e 6f 63 63 63 32 32 32  0  0  0  0  0 
10  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  9  9  f  c 27  2 35 37 10 1f  4  3  4 
 d 19  4 56  5 16 1f  f  8  d  c  0 43 60  4  e 
1f  c  3  7  e  0 43 63  5 10 3c 14  8  2 1b 56 
 5  2  4  0 63 32  0  0  0 32  0 4c 6f 54 69 6e 
65 38 31 5a 20 63 63 63 32 32 32  0 7f  0  1  0 
18  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  e  f  e  9  0  3 43 2d  e 1f  f  5  7 
 f 16 43 5a  0  0 1f 12  6  8  d  0  3 63  4  0 
1f 12  6  8  f  0  2 63  4  6 34 14  0  1  2 4e 
18  2  4  0 63 32  0 32  0 32  0 44 79 6e 6f 6d 
69 74 65 45 50 63 63 63 32 32 32  0 70  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  b  1  b  8 18 40 5f  a  e 1f 1f  0  a 
 f  0 40 5f  4  0 1f 1f  0  a  f  0 40 63  5  6 
1f 1f  0  a  f  0 40 5f  0  8 1f 20  0  3  0 5a 
18  4  4  0 63 32 32  0  0 32  0 50 65 72 63 4f 
72 67 61 6e 20 63 63 63 32 32 32  0  0  0  0  0 
 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  b  7  f  9  0  4 49 13 13 1f  8  7  5 
 e  0  2 58  0  c 1f  6  4  6  f 23  3 46 10  a 
1f  7  8  c  d  0  2 63  8  b  2 1c  0  0  0 52 
18  4  4  0 63 32  0 32  0 32  0 54 68 69 6e 20 
43 6c 61 76 20 63 63 63 32 32 32  0 70  0 20  0 
10  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  c  0  6  1  a  4 50 20  e 1f  c  0  6 
 1  a  4 50 1f  8 1f  b  9  5  e  0  2 63  5  e 
1f  b  9  5  e  0  3 63  4  8  4 1a  0  0  0 52 
1d  2  4  0 63 32  0 32  0 32  0 42 72 69 74 65 
43 65 6c 73 74 63 63 63 32 32 32  0 20  0 26  0 
 1  0  8  4  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  f 1f  4  8  f  0 3a 51  4  b  e 1f  0  8 
 f  0 22 4b  4  3  f 1a  b  8  d  0 3b 36  9  3 
12 1f  0  8  f  0 22 5d  4  b 3a 1e 19  5  0 52 
18  4  4  0 63 32  0  0  0 32  0 54 72 75 6d 70 
65 74 38 31 5a 63 63 63 32 32 32  0  0  0 50  0 
51  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  c  5  0  8  0  0  2 4a  4  b  f 1f  0  8 
 f  0  2 3f  4  3 1f  f  0  8  0 23  3 44  b  3 
10 1f  0  9  f  0  2 5e  4  c 3a 1f 19  7  0 52 
18  4  4  0 63 32  0  0  0 32  0 46 6c 75 67 65 
6c 68 6f 72 6e 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 10 1f  0  8  f  0 42 4a  0  3 11 1f  0  8 
 f  a 43 51  0  3 11  9  0  8  d  0 42 2b 16  6 
10 1f  0  9  f  0 42 63  4  b 3a 1e  9  9  0 5a 
24  4  4  0 63 32 31  0  0 32  0 52 61 73 70 41 
6c 74 6f 20 20 63 63 63 32 32 32  0 10  0 20  0 
54  0 20  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 10  9  2  6  d  0 41 3e  4 15  c  b  2  3 
 e  0 41 4f  4 12  c  e  2  8  d  0 42 4b  a 1c 
 d  b  1  9  e  0  3 63  a 14  0 23  f  2 1b 5e 
18  4  5  0 63 28 50 32  0 32  0 48 61 72 6d 6f 
6e 69 63 61 20 63 63 63 32 32 32  0 50 10 50  0 
50  0 10  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1c  2  0  4  e 63  0 4e  4  3  d  5  0  6 
 e 63  1 56  a  8 12  7  0  6  9 63  2 47 1b  e 
 a  a  0  5  f  0  1 63  4  b 32 1a  8  d  0 52 
 c  4  4  0 63 32  0  0  0 32  0 44 6f 75 62 6c 
65 42 61 73 73 63 63 63 32 32 32  0 10  0  0  0 
 3  0  0  5  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  b  4  0  4  f 14  2 49  9  6  a  7  0  4 
 f 14  2 51  a  0  8 1f  0  5  f  0  1 63  9  6 
 a 1f  0  5  f  0  1 63  a  0 3c 1f  6  9  0 52 
 5  4  4  0 63 32  0  0  0 32  0 48 69 53 74 72 
69 6e 67 20 31 63 63 63 32 32 32  0  2  0 30  0 
32  0 10  5  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 10 13  f  4  a  0  3 3b 14 14 1f  e  8  7 
 9  0  2 42  5  e 18 13  d  9  c  0  2 3c 13  8 
1f 11  7  4  f  0 42 63  4 10 3a 1b  0  0  0 52 
1d  4  4  0 63 32  0  0  0 32  0 48 61 72 70 20 
20 20 20 20 20 63 63 63 32 32 32  8  0  0 21  0 
 0  0  8  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  6  6  4  f  0 40 48  5  0  c  8  7  5 
 f  5  0 52  4  0  f  7  3  7  e  8  3 63  4  6 
 f  8  4  5  f  0  3 63  4  6 7c 1f  0  6  0 4a 
11  2  4  0 63 32  0  0  0 32  0 46 61 6e 66 61 
72 54 70 74 73 63 63 63 32 32 32  6  1  0 38  0 
 8  0 48  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  d  b  0  1  c  0  2 2c 3d  3  d  7  0  1 
 c  0  2 1f 3c  3  d 1f  0  5  f  0  2 63  5  6 
 d 1f  0  5  f  0  2 63  4  0 3c 63  0 2f  0 53 
11  4  4  0 63 32  0  0  0 32  0 42 72 65 61 74 
68 4f 72 67 6e 63 63 63 32 32 32  4 30  5 50  0 
11  0 18  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  9  0  6  0 27  2 51 19  b 1c  6  0  8 
 0 37  2 47  a  3 1f  a  0  9  0 3d  2 4d  a  e 
1f 12  8  8  f  0  3 61  4  b 28 1f  0  3  0 52 
 c  3  4  0 63 32  1 32  0 32  0 4e 79 6c 6f 6e 
47 75 69 74 20 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  e  e  f  f  0  3 48 2d  6 1f  f  4  f 
 f 25  3 5b  0  0 1f 12  6  c  e 1c  3 55  0 10 
1f 13  7  8  e  6  4 62  4  e 3b 14  0  0  0 42 
18  2  4  0 63 32  0 32  0 32  0 47 75 69 74 61 
72 20 23 31 20 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f 19  8  a  3  0  3 63 10 18 1f  c  5  b 
 5  0  3 52  0  b 1f 19  6  b  5  0  3 63  a 16 
1f  f 11  9  7  0  4 63  4  3 3a 14  0  0  0 42 
18  2  4  0 63 32  0 32  0 32  0 46 75 6e 6b 79 
20 50 69 63 6b 63 63 63 32 32 32  0 30  0  0  0 
 0  0  0  7  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  1  0  8  4  0  3 3d  a 1e 1f  1  0  8 
 0  0  0 43  0 10 1f  9  6  8  c 1b  7 46 1c 1e 
1f  9  0  9  9  0  1 63  4  3 3a 1c  0  0  0 52 
 c  4  5  0 63 4b  0  0  0 32  0 45 6c 65 63 42 
61 73 73 20 31 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  f  f  e  9  0  3 46 1d 16 1f  f  5  e 
 e  d  3 63  0  b 1f 13  6  5  d 1c  3 63  0  0 
1f 13  6  8  f  0  4 63  4  6 3b 1f  0  0  0 42 
 c  4  4  0 63 32  0 32  0 32  0 53 79 6e 46 75 
6e 6b 42 61 73 63 63 63 32 32 32  d 6c  0  0  0 
70  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f 10  7  8  3  0  3 4f  4  3 1f  9  0  8 
 0  0  1 4a  0  b 1f 11  0  8  0  0  1 47  4  8 
1f  9  0  8  0  0  0 63  0  b 39 19  0  7  0 52 
 c  2  4  0 63 32  0 32  0 32  0 4c 61 74 65 6c 
79 42 61 73 73 63 63 63 32 32 32  2  0  0  0  0 
40  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 13 12  0  9  d 22  0 51  0  b 1f 14  0  5 
 8 24 40 5c  0  3 1f 11  0  6  c 2c  0 53  9  0 
10 1f  0  b  f  0  0 5c  a  e 3a 22 11  e 1e 5e 
18  7  4  0 63 32  0 32  0 32  0 53 79 6e 63 20 
4c 65 61 64 20 63 63 63 32 32 32  0 70  0 40  0 
 2  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 13 1e  0  9  e  0  0 63 3f  b 1f 14  0  5 
 e 24  1 51  4  3 1f 14  0  f  1  0 41 4d  8  3 
 f 1f  0  b  f  0  2 63  4  b 3b 20 11 12 33 56 
18  4  4  0 63 37  e  0  0 32  0 4a 61 7a 7a 20 
46 6c 75 74 65 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 15 13  d  3  d 1e  2 50 18  e 15 14  9  4 
 c 1e  2 56 11  8 1b 1f  f  7  f  0  1 63  4  6 
1a 1f  e  6  f  0  2 63  4  0 7c  b  0  8  0 62 
18  4  4  0 63 32  0  0  0 32  0 4a 61 76 61 20 
4a 69 76 65 20 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  0  0  4  f  0 40 63 3c  0  b  8  7  7 
 f  5  0 63  4  6  f  5  3  7  f  8  0 3b  5  6 
 e  8  4  5  f  0  3 63  3  0 7e 1d  6  f  0 4a 
11  0  4  0 63 32  0  0  0 32  0 42 61 61 64 42 
72 65 61 74 68 63 63 63 32 32 32  6 30  0 38  0 
 1  0 46  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f  0  0  4  f  0 40 47 2f  0  e  8  7  7 
 f  5  0 4c  0  6 13 1c  d  c  6  8  0 63  5  6 
14 11  d  b  0  0  3 63  4  0 7a 10  0 51  0 68 
17  0  4  0 63 32  0  0  0 32  0 56 6f 63 61 6c 
4e 75 74 73 20 63 63 63 32 32 32  6 30  0 30  0 
 1  0 10  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f 1f  0  5  f  0  0 41 32  3 1f 14 10  5 
 5  1  2 63  7  3 1f  b 12  8  f  0  1 63  c  3 
1f 1f  f  8  f  0  1 63  4  3 39 23  0  0  0 62 
18  7  4  0 63 32  0  0  0 32  0 57 61 74 65 72 
47 6c 61 73 73 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  7  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 16  2  0  4  6  9  1 4f  8  0 19  e  1  4 
 0 20  1 43 19  0 1f 12 10  6  7  0  0 54 3d  3 
16  d  6  6  2 1e  3 61  8  e 3a 20  1 14  0 42 
 c  2  4  2 63 63 63  0  0 32  0 46 75 7a 7a 79 
20 4b 6f 74 6f 63 63 63 32 32 32  0  0  0  0  b 
50  0  0  5  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1c  8  0  3  e  0  1 55 12  3 1c  7  0  1 
 e 2e  1 58 27  b  e  4  0  2  a  0  2 63  4  a 
 d  9  0  2  c  1  2 63 10  b  4 54  0 47  0 53 
18  7  4  0 63 32  0  0  0 32  0 42 72 74 68 62 
65 6c 6c 73 20 63 63 63 32 32 32  0  4  0 40  0 
40  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1a  4  1  1  b 16  0 47  5  3 15  e  0  1 
 d  0  0 4c  5 16 1c  6  4  2  7  0  0 63  4 16 
18 18  3  1  e  0  0 5e  4 10 24  7  0  4  0 62 
24  4  4  0 63 32  0  0  0 32  0 54 75 62 65 20 
42 65 6c 6c 73 63 63 63 32 32 32  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f 1f 13  3  0  0  0 5f 3d  6 1f 12 13  2 
 0  0  1 52  5  2 1f 14 13  3  0  0  1 56 28  5 
1e  b 13  f  9  0  0 63  6  3 3b 63  0 63  0 73 
23  7  4  0 63 32  0  0  0 32  0 4e 6f 69 73 65 
20 53 68 6f 74 63 63 63 32 32 32  8  0  0  0  8 
 0  0  0  6  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 1f 16  0  3  7  0  1 50  0  3 1f 18  3  3 
 3 22  0 63  0 14 1d  7  6  3  6  0  1 3c  8  3 
1f  5  7  3  0  0  1 63  4 1b 39 23  0  8  0 42 
18  4  4  0 63 32  0  0  0 32  0 48 61 6e 64 20 
44 72 75 6d 20 63 63 63 32 32 32  0  1  0  3  0 
 1  0  1  3  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 
 0  0 7d f7 
--- NEW FILE: sysex.c ---
/* sysex.c -- example program showing how to send and receive sysex
    messages

   Messages are stored in a file using 2-digit hexadecimal numbers,
   one per byte, separated by blanks, with up to 32 numbers per line:
   F0 14 A7 4B ...

 */

#include "stdio.h"
#include "stdlib.h"
#include "assert.h"
#include "portmidi.h"
#include "porttime.h"
#include "string.h"

#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7

#define STRING_MAX 80

int latency = 0;

/* read a number from console */
/**/
int get_number(char *prompt)
{
    char line[STRING_MAX];
    int n = 0, i;
    printf(prompt);
    while (n != 1) {
        n = scanf("%d", &i);
        fgets(line, STRING_MAX, stdin);

    }
    return i;
}


/* loopback test -- send/rcv from 2 to 1000 bytes of random midi data */
/**/
void loopback_test()
{
    int outp;
    int inp;
    PmStream *midi_in;
    PmStream *midi_out;
    unsigned char msg[1024];
    char line[80];
    long len;
    int i;
    int data;
    PmEvent event;
    int shift;

    Pt_Start(1, 0, 0);
    
    printf("Connect a midi cable from an output port to an input port.\n");
    printf("This test will send random data via sysex message from output\n");
    printf("to input and check that the correct data was received.\n");
    outp = get_number("Type output device number: ");
    /* Open output with 1ms latency -- when latency is non-zero, the Win32
       implementation supports sending sysex messages incrementally in a 
       series of buffers. This is nicer than allocating a big buffer for the
       message, and it also seems to work better. Either way works.
     */
    Pm_OpenOutput(&midi_out, outp, NULL, 0, NULL, NULL, latency);
    inp = get_number("Type input device number: ");
    /* since we are going to send and then receive, make sure the input buffer
       is large enough for the entire message */
    Pm_OpenInput(&midi_in, inp, NULL, 512, NULL, NULL);

    srand((unsigned int) Pt_Time()); /* seed for random numbers */

    while (1) {
        PmError count;
        long start_time;
        long error_position;
        long expected = 0;
        long actual = 0;
        printf("Type return to send message, q to quit: ");
        fgets(line, STRING_MAX, stdin);
        if (line[0] == 'q') goto cleanup;

        /* compose the message */
        len = rand() % 998 + 2; /* len only counts data bytes */
        msg[0] = (char) MIDI_SYSEX; /* start of SYSEX message */
        /* data bytes go from 1 to len */
        for (i = 0; i < len; i++) {
            msg[i + 1] = rand() & 0x7f; /* MIDI data */
        }
        /* final EOX goes in len+1, total of len+2 bytes in msg */
        msg[len + 1] = (char) MIDI_EOX;

        /* sanity check: before we send, there should be no queued data */
        count = Pm_Read(midi_in, &event, 1);

        if (count != 0) {
			printf("Before sending anything, a MIDI message was found in\n");
			printf("the input buffer. Please try again.\n");
			break;
		}

        /* send the message */
        printf("Sending %ld byte sysex message.\n", len + 2);
        Pm_WriteSysEx(midi_out, 0, msg);

        /* receive the message and compare to msg[] */
        data = 0;
        shift = 0;
        i = 0;
        start_time = Pt_Time();
        error_position = -1;
        /* allow up to 2 seconds for transmission */
        while (data != MIDI_EOX && start_time + 2000 > Pt_Time()) {
            count = Pm_Read(midi_in, &event, 1);
            /* CAUTION: this causes busy waiting. It would be better to 
               be in a polling loop to avoid being compute bound. PortMidi
               does not support a blocking read since this is so seldom
               useful. There is no timeout, so if we don't receive a sysex
               message, or at least an EOX, the program will hang here.
             */
            if (count == 0) continue;
            
            /* printf("read %lx ", event.message);
               fflush(stdout); */
            
            /* compare 4 bytes of data until you reach an eox */
            for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) {
                data = (event.message >> shift) & 0xFF;
                if (data != msg[i] && error_position < 0) {
                    error_position = i;
                    expected = msg[i];
                    actual = data;
                }
                i++;
            }
        }
        if (error_position >= 0) {
            printf("Error at byte %ld: sent %lx recd %lx\n", error_position, expected, actual);
        } else if (i != len + 2) {
            printf("Error: byte %d not received\n", i);
        } else {
            printf("Correctly ");
        }
        printf("received %d byte sysex message.\n", i);
    }
cleanup:
    Pm_Close(midi_out);
    Pm_Close(midi_in);
    return;
}


#define is_real_time_msg(msg) ((0xF0 & Pm_MessageStatus(msg)) == 0xF8)


void receive_sysex()
{
    char line[80];
    FILE *f;
    PmStream *midi;
    int shift = 0;
    int data = 0;
    int bytes_on_line = 0;
    PmEvent msg;

    /* determine which output device to use */
    int i = get_number("Type input device number: ");

    /* open input device */
    Pm_OpenInput(&midi, i, NULL, 512, NULL, NULL);
    printf("Midi Input opened, type file for sysex data: ");

    /* open file */
    fgets(line, STRING_MAX, stdin);
    /* remove the newline character */
    if (strlen(line) > 0) line[strlen(line) - 1] = 0;
    f = fopen(line, "w");
    if (!f) {
        printf("Could not open %s\n", line);
        Pm_Close(midi);
        return;
    }

    printf("Ready to receive a sysex message\n");

    /* read data and write to file */
    while (data != MIDI_EOX) {
        PmError count;
        count = Pm_Read(midi, &msg, 1);
        /* CAUTION: this causes busy waiting. It would be better to 
           be in a polling loop to avoid being compute bound. PortMidi
           does not support a blocking read since this is so seldom
           useful.
         */
        if (count == 0) continue;
        /* ignore real-time messages */
        if (is_real_time_msg(Pm_MessageStatus(msg.message))) continue;

        /* write 4 bytes of data until you reach an eox */
        for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) {
            data = (msg.message >> shift) & 0xFF;
            /* if this is a status byte that's not MIDI_EOX, the sysex
               message is incomplete and there is no more sysex data */
            if (data & 0x80 && data != MIDI_EOX) break;
            fprintf(f, "%2x ", data);
            if (++bytes_on_line >= 16) {
                fprintf(f, "\n");
                bytes_on_line = 0;
            }
        }
    }
	fclose(f);
    Pm_Close(midi);
}


void send_sysex()
{
    char line[80];
    FILE *f;
    PmStream *midi;
    int data;
    int shift = 0;
    PmEvent msg;

	/* determine which output device to use */
    int i = get_number("Type output device number: ");

    msg.timestamp = 0; /* no need for timestamp */

	/* open output device */
    Pm_OpenOutput(&midi, i, NULL, 0, NULL, NULL, latency);
	printf("Midi Output opened, type file with sysex data: ");

    /* open file */
    fgets(line, STRING_MAX, stdin);
    /* remove the newline character */
    if (strlen(line) > 0) line[strlen(line) - 1] = 0;
    f = fopen(line, "r");
    if (!f) {
        printf("Could not open %s\n", line);
        Pm_Close(midi);
        return;
    }

    /* read file and send data */
    msg.message = 0;
    while (1) {
        /* get next byte from file */

        if (fscanf(f, "%x", &data) == 1) {
            /* printf("read %x, ", data); */
            /* OR byte into message at proper offset */
            msg.message |= (data << shift);
            shift += 8;
        }
        /* send the message if it's full (shift == 32) or if we are at end */
        if (shift == 32 || data == MIDI_EOX) {
            /* this will send sysex data 4 bytes at a time -- it would
               be much more efficient to send multiple PmEvents at once
               but this method is simpler. See Pm_WriteSysex for a more
               efficient code example.
             */
            Pm_Write(midi, &msg, 1);
            msg.message = 0;
            shift = 0;
        }
        if (data == MIDI_EOX) { /* end of message */
            fclose(f);
            Pm_Close(midi);
            return;
        }
    }
}


int main()
{
    int i;
    char line[80];
    
	/* list device information */
	for (i = 0; i < Pm_CountDevices(); i++) {
        const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
        printf("%d: %s, %s", i, info->interf, info->name);
        if (info->input) printf(" (input)");
        if (info->output) printf(" (output)");
        printf("\n");
    }
	latency = get_number("Latency in milliseconds (0 to send data immediatedly,\n"
		                 "  >0 to send timestamped messages): ");    
    while (1) {
        printf("Type r to receive sysex, s to send,"
               " l for loopback test, q to quit: ");
        fgets(line, STRING_MAX, stdin);
        switch (line[0]) {
          case 'r':
            receive_sysex();
            break;
          case 's':
            send_sysex();
            break;
          case 'l':
            loopback_test();
          case 'q':
            exit(0);
          default:
            break;
        }
    }
    return 0;
}


     

            

--- NEW FILE: midithru.c ---
/* midithru.c -- example program implementing background thru processing */

/* suppose you want low-latency midi-thru processing, but your application
   wants to take advantage of the input buffer and timestamped data so that
   it does not have to operate with very low latency.

   This program illustrates how to use a timer callback from PortTime to 
   implement a low-latency process that handles midi thru, including correctly
   merging midi data from the application with midi data from the input port.

   The main application, which runs in the main program thread, will use an
   interface similar to that of PortMidi, but since PortMidi does not allow
   concurrent threads to share access to a stream, the application will
   call private methods that transfer MIDI messages to and from the timer 
   thread. All PortMidi API calls are made from the timer thread.
 */

/* DESIGN

All setup will be done by the main thread. Then, all direct access to 
PortMidi will be handed off to the timer callback thread.

After this hand-off, the main thread will get/send messages via a queue.

The goal is to send incoming messages to the midi output while merging
any midi data generated by the application. Sysex is a problem here
because you cannot insert (merge) a midi message while a sysex is in
progress. There are at least three ways to implement midi thru with 
sysex messages:

1) Turn them off. If your application does not need them, turn them off
   with Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_SYSEX). You will
   not receive sysex (or active sensing messages), so you will not have 
   to handle them.

2) Make them atomic. As you receive sysex messages, copy the data into
   a (big) buffer. Ideally, expand the buffer as needed -- sysex messages
   do not have any maximum length. Even more ideally, use a list structure
   and real-time memory allocation to avoid latency in the timer thread.
   When a full sysex message is received, send it to the midi output all
   at once.

3) Process sysex incrementally. Send sysex data to midi output as it
   arrives. Block any non-real-time messages from the application until
   the sysex message completes. There is the risk that an incomplete
   sysex message will block messages forever, so implement a 5-second
   timeout: if no sysex data is seen for 5 seconds, release the block,
   possibly losing the rest of the sysex message. 

   Application messages must be processed similarly: once started, a
   sysex message will block MIDI THRU processing. We will assume that
   the application will not abort a sysex message, so timeouts are not
   necessary here.

This code implements (3).

Latency is also an issue. PortMidi requires timestamps to be in 
non-decreasing order. Since we'll be operating with a low-latency
timer thread, we can just set the latency to zero meaning timestamps
are ignored by PortMidi. This will allow thru to go through with
minimal latency. The application, however, needs to use timestamps
because we assume it is high latency (the whole purpose of this
example is to illustrate how to get low-latency thru with a high-latency
application.) So the callback thread will implement midi timing by
observing timestamps. The current timestamp will be available in the
global variable current_timestamp.

*/


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "assert.h"
#include "portmidi.h"
#include "pmutil.h"
#include "porttime.h"

#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7

/* active is set true when midi processing should start */
int active = FALSE;
/* process_midi_exit_flag is set when the timer thread shuts down */
int process_midi_exit_flag;

PmStream *midi_in;
PmStream *midi_out;

/* shared queues */
#define IN_QUEUE_SIZE 1024
#define OUT_QUEUE_SIZE 1024
PmQueue *in_queue;
PmQueue *out_queue;
PmTimestamp current_timestamp = 0;
int thru_sysex_in_progress = FALSE;
int app_sysex_in_progress = FALSE;
PmTimestamp last_timestamp = 0;


/* time proc parameter for Pm_MidiOpen */
long midithru_time_proc(void *info)
{
    return current_timestamp;
}


/* timer interrupt for processing midi data.
   Incoming data is delivered to main program via in_queue.
   Outgoing data from main program is delivered via out_queue.
   Incoming data from midi_in is copied with low latency to  midi_out.
   Sysex messages from either source block messages from the other.
 */
void process_midi(PtTimestamp timestamp, void *userData)
{
    PmError result;
    PmEvent buffer; /* just one message at a time */

    current_timestamp++; /* update every millisecond */
    /* if (current_timestamp % 1000 == 0) 
        printf("time %d\n", current_timestamp); */

    /* do nothing until initialization completes */
    if (!active) {
        /* this flag signals that no more midi processing will be done */
        process_midi_exit_flag = TRUE;
        return;
    }

    /* see if there is any midi input to process */
    if (!app_sysex_in_progress) {
        do {
	    result = Pm_Poll(midi_in);
            if (result) {
                long status;
                if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow) 
                    continue;
            
                /* record timestamp of most recent data */
                last_timestamp = current_timestamp;

                /* the data might be the end of a sysex message that
                   has timed out, in which case we must ignore it.
                   It's a continuation of a sysex message if status
                   is actually a data byte (high-order bit is zero). */
                status = Pm_MessageStatus(buffer.message);
                if (((status & 0x80) == 0) && !thru_sysex_in_progress) {
                    continue; /* ignore this data */
                }

                /* implement midi thru */
                /* note that you could output to multiple ports or do other
                   processing here if you wanted
                 */
                /* printf("thru: %x\n", buffer.message); */
                Pm_Write(midi_out, &buffer, 1);

                /* send the message to the application */
                /* you might want to filter clock or active sense messages here
                   to avoid sending a bunch of junk to the application even if
                   you want to send it to MIDI THRU
                 */
                Pm_Enqueue(in_queue, &buffer);

                /* sysex processing */
                if (status == MIDI_SYSEX) thru_sysex_in_progress = TRUE;
                else if ((status & 0xF8) != 0xF8) {
                    /* not MIDI_SYSEX and not real-time, so */
                    thru_sysex_in_progress = FALSE;
                }
                if (thru_sysex_in_progress && /* look for EOX */
                    (((buffer.message & 0xFF) == MIDI_EOX) ||
                     (((buffer.message >> 8) & 0xFF) == MIDI_EOX) ||
                     (((buffer.message >> 16) & 0xFF) == MIDI_EOX) ||
                     (((buffer.message >> 24) & 0xFF) == MIDI_EOX))) {
                    thru_sysex_in_progress = FALSE;
                }
            }
        } while (result);
    }


    /* see if there is application midi data to process */
    while (!Pm_QueueEmpty(out_queue)) {
        /* see if it is time to output the next message */
        PmEvent *next = (PmEvent *) Pm_QueuePeek(out_queue);
        assert(next); /* must be non-null because queue is not empty */
        if (next->timestamp <= current_timestamp) {
            /* time to send a message, first make sure it's not blocked */
            long status = Pm_MessageStatus(buffer.message);
            if ((status & 0xF8) == 0xF8) {
                ; /* real-time messages are not blocked */
            } else if (thru_sysex_in_progress) {
                /* maybe sysex has timed out (output becomes unblocked) */
                if (last_timestamp + 5000 < current_timestamp) {
                    thru_sysex_in_progress = FALSE;
                } else break; /* output is blocked, so exit loop */
            }
            Pm_Dequeue(out_queue, &buffer);
            Pm_Write(midi_out, &buffer, 1);

            /* inspect message to update app_sysex_in_progress */
            if (status == MIDI_SYSEX) app_sysex_in_progress = TRUE;
            else if ((status & 0xF8) != 0xF8) {
                /* not MIDI_SYSEX and not real-time, so */
                app_sysex_in_progress = FALSE;
            }
            if (app_sysex_in_progress && /* look for EOX */
                (((buffer.message & 0xFF) == MIDI_EOX) ||
                 (((buffer.message >> 8) & 0xFF) == MIDI_EOX) ||
                 (((buffer.message >> 16) & 0xFF) == MIDI_EOX) ||
                 (((buffer.message >> 24) & 0xFF) == MIDI_EOX))) {
                app_sysex_in_progress = FALSE;
            }
        } else break; /* wait until indicated timestamp */
    }
}


void exit_with_message(char *msg)
{
#define STRING_MAX 80
    char line[STRING_MAX];
    printf("%s\nType ENTER...", msg);
    fgets(line, STRING_MAX, stdin);
    exit(1);
}


void initialize()
/* set up midi processing thread and open midi streams */
{
    /* note that it is safe to call PortMidi from the main thread for
       initialization and opening devices. You should not make any
       calls to PortMidi from this thread once the midi thread begins.
       to make PortMidi calls.
     */

    /* note that this routine provides minimal error checking. If
       you use the PortMidi library compiled with PM_CHECK_ERRORS,
       then error messages will be printed and the program will exit
       if an error is encountered. Otherwise, you should add some
       error checking to this code.
     */

    const PmDeviceInfo *info;
    int id;

    /* make the message queues */
    in_queue = Pm_QueueCreate(IN_QUEUE_SIZE, sizeof(PmEvent));
    assert(in_queue != NULL);
    out_queue = Pm_QueueCreate(OUT_QUEUE_SIZE, sizeof(PmEvent));
    assert(out_queue != NULL);

    /* always start the timer before you start midi */
    Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */
    /* the timer will call our function, process_midi() every millisecond */
    
    Pm_Initialize();

    id = Pm_GetDefaultOutputDeviceID();
    info = Pm_GetDeviceInfo(id);
    if (info == NULL) {
        printf("Could not open default output device (%d).", id);
        exit_with_message("");
    }
    printf("Opening output device %s %s\n", info->interf, info->name);

    /* use zero latency because we want output to be immediate */
    Pm_OpenOutput(&midi_out, 
                  id, 
                  NULL /* driver info */,
                  OUT_QUEUE_SIZE,
                  &midithru_time_proc,
                  NULL /* time info */,
                  0 /* Latency */);

    id = Pm_GetDefaultInputDeviceID();
    info = Pm_GetDeviceInfo(id);
    if (info == NULL) {
        printf("Could not open default input device (%d).", id);
        exit_with_message("");
    }
    printf("Opening input device %s %s\n", info->interf, info->name);
    Pm_OpenInput(&midi_in, 
                 id, 
                 NULL /* driver info */,
                 0 /* use default input size */,
                 &midithru_time_proc,
                 NULL /* time info */);
    /* Note: if you set a filter here, then this will filter what goes
       to the MIDI THRU port. You may not want to do this.
     */
    Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_CLOCK);

    active = TRUE; /* enable processing in the midi thread -- yes, this
                      is a shared variable without synchronization, but
                      this simple assignment is safe */

}


void finalize()
{
    /* the timer thread could be in the middle of accessing PortMidi stuff */
    /* to detect that it is done, we first clear process_midi_exit_flag and
       then wait for the timer thread to set it
     */
    process_midi_exit_flag = FALSE;
    active = FALSE;
    /* busy wait for flag from timer thread that it is done */
    while (!process_midi_exit_flag) ;
    /* at this point, midi thread is inactive and we need to shut down
     * the midi input and output
     */
    Pt_Stop(); /* stop the timer */
    Pm_QueueDestroy(in_queue);
    Pm_QueueDestroy(out_queue);

    Pm_Close(midi_in);
    Pm_Close(midi_out);

    Pm_Terminate();    
}


int main(int argc, char *argv[])
{
    PmTimestamp last_time = 0;
    PmEvent buffer;

    /* determine what type of test to run */
    printf("begin PortMidi midithru program...\n");

    initialize(); /* set up and start midi processing */
	
    printf("%s\n%s\n",
           "This program will run for 60 seconds, or until you play middle C,",
           "echoing all input with a 2 second delay.");

    while (current_timestamp < 60000) {
        /* just to make the point that this is not a low-latency process,
           spin until half a second has elapsed */
        last_time = last_time + 500;
        while (last_time > current_timestamp) ;

        /* now read data and send it after changing timestamps */
        while (Pm_Dequeue(in_queue, &buffer) == 1) {
            /* printf("timestamp %d\n", buffer.timestamp); */
            /* printf("message %x\n", buffer.message); */
            buffer.timestamp = buffer.timestamp + 2000; /* delay */
            Pm_Enqueue(out_queue, &buffer);
            /* play middle C to break out of loop */
            if (Pm_MessageStatus(buffer.message) == 0x90 &&
                Pm_MessageData1(buffer.message) == 60) {
                goto quit_now;
            }
        }
    }
quit_now:
    finalize();
    exit_with_message("finished PortMidi midithru program.");
    return 0; /* never executed, but keeps the compiler happy */
}

--- NEW FILE: latency.c ---
/* latency.c -- measure latency of OS */

#include "porttime.h"
#include "portmidi.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "assert.h"

/* Latency is defined here to mean the time starting when a
   process becomes ready to run, and ending when the process
   actually runs. Latency is due to contention for the
   processor, usually due to other processes, OS activity
   including device drivers handling interrupts, and
   waiting for the scheduler to suspend the currently running
   process and activate the one that is waiting.

   Latency can affect PortMidi applications: if a process fails
   to wake up promptly, MIDI input may sit in the input buffer
   waiting to be handled, and MIDI output may not be generated
   with accurate timing. Using the latency parameter when 
   opening a MIDI output port allows the caller to defer timing
   to PortMidi, which in most implementations will pass the
   data on to the OS. By passing timestamps and data to the
   OS kernel, device driver, or even hardware, there are fewer
   sources of latency that can affect the ultimate timing of
   the data. On the other hand, the application must generate
   and deliver the data ahead of the timestamp. The amount by 
   which data is computed early must be at least as large as
   the worst-case latency to avoid timing problems.

   Latency is even more important in audio applications. If an
   application lets an audio output buffer underflow, an audible
   pop or click is produced. Audio input buffers can overflow,
   causing data to be lost. In general the audio buffers must
   be large enough to buffer the worst-case latency that the
   application will encounter.

   This program measures latency by recording the difference
   between the scheduled callback time and the current real time.
   We do not really know the scheduled callback time, so we will
   record the differences between the real time of each callback
   and the real time of the previous callback. Differences that
   are larger than the scheduled difference are recorded. Smaller
   differences indicate the system is recovering from an earlier
   latency, so these are ignored.
   Since printing by the callback process can cause all sorts of
   delays, this program records latency observations in a
   histogram. When the program is stopped, the histogram is
   printed to the console.

   Optionally the system can be tested under a load of MIDI input,
   MIDI output, or both.  If MIDI input is selected, the callback
   thread will read any waiting MIDI events each iteration.  You
   must generate events on this interface for the test to actually
   put any appreciable load on PortMidi.  If MIDI output is
   selected, alternating note on and note off events are sent each
   X iterations, where you specify X.  For example, with a timer
   callback period of 2ms and X=1, a MIDI event is sent every 2ms.


   INTERPRETING RESULTS: Time is quantized to 1ms, so there is
   some uncertainty due to rounding. A microsecond latency that
   spans the time when the clock is incremented will be reported
   as a latency of 1. On the other hand, a latency of almost
   1ms that falls between two clock ticks will be reported as 
   zero. In general, if the highest nonzero bin is numbered N,
   then the maximum latency is N+1.

CHANGE LOG

18-Jul-03 Mark Nelson -- Added code to generate MIDI or receive
            MIDI during test, and made period user-settable.
 */

#define HIST_LEN 21 /* how many 1ms bins in the histogram */

#define STRING_MAX 80 /* used for console input */

#define INPUT_BUFFER_SIZE 100
#define OUTPUT_BUFFER_SIZE 0

#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
#define min(a, b) ((a) <= (b) ? (a) : (b))
#endif

int get_number(char *prompt);

PtTimestamp previous_callback_time = 0;

int period;            /* milliseconds per callback */

long histogram[HIST_LEN];
long max_latency = 0;  /* worst latency observed */
long out_of_range = 0; /* how many points outside of HIST_LEN? */

int test_in, test_out; /* test MIDI in and/or out? */
int output_period;     /* output MIDI every __ iterations if test_out true */
int iteration = 0;
PmStream *in, *out;
int note_on = 0;       /* is the note currently on? */

/* callback function for PortTime -- computes histogram */
void pt_callback(PtTimestamp timestamp, void *userData)
{
    PtTimestamp difference = timestamp - previous_callback_time - period;
    previous_callback_time = timestamp;

    /* allow 5 seconds for the system to settle down */
    if (timestamp < 5000) return;

    iteration++;
    /* send a note on/off if user requested it */
    if (test_out && (iteration % output_period == 0)) {
        PmEvent buffer[1];
        buffer[0].timestamp = Pt_Time(NULL);
        if (note_on) {
            /* note off */
            buffer[0].message = Pm_Message(0x90, 60, 0);
            note_on = 0;
        } else {
            /* note on */
            buffer[0].message = Pm_Message(0x90, 60, 100);
            note_on = 1;
        }
        Pm_Write(out, buffer, 1);
        iteration = 0;
    }

    /* read all waiting events (if user requested) */
    if (test_in) {
       PmError status;
       PmEvent buffer[1];
       do {
          status = Pm_Poll(in);
          if (status == TRUE) {
              Pm_Read(in,buffer,1);
          }
       } while (status == TRUE);
    }

    if (difference < 0) return; /* ignore when system is "catching up" */

    /* update the histogram */
    if (difference < HIST_LEN) {
        histogram[difference]++;
    } else {
        out_of_range++;
    }

    if (max_latency < difference) max_latency = difference;
}


int main()
{
    char line[STRING_MAX];
    int i;
    int len;
    int choice;
    PtTimestamp stop;
    printf("Latency histogram.\n");
    period = get_number("Choose timer period (in ms): ");
    assert(period >= 1);
    printf("Benchmark with:\n\t%s\n\t%s\n\t%s\n\t%s\n",
           "1. No MIDI traffic",
           "2. MIDI input",
           "3. MIDI output",
           "4. MIDI input and output");
    choice = get_number("? ");
    switch (choice) {
      case 1: test_in = 0; test_out = 0; break;
      case 2: test_in = 1; test_out = 0; break;
      case 3: test_in = 0; test_out = 1; break;
      case 4: test_in = 1; test_out = 1; break;
      default: assert(0);
    }
    if (test_in || test_out) {
        /* list device information */
        for (i = 0; i < Pm_CountDevices(); i++) {
            const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
            if ((test_in && info->input) ||
                (test_out && info->output)) {
                printf("%d: %s, %s", i, info->interf, info->name);
                if (info->input) printf(" (input)");
                if (info->output) printf(" (output)");
                printf("\n");
            }
        }
        /* open stream(s) */
        if (test_in) {
            int i = get_number("MIDI input device number: ");
            Pm_OpenInput(&in, 
                  i,
                  NULL, 
                  INPUT_BUFFER_SIZE, 
                  (long (*)(void *)) Pt_Time, 
                  NULL);
            /* turn on filtering; otherwise, input might overflow in the 
               5-second period before timer callback starts reading midi */
            Pm_SetFilter(in, PM_FILT_ACTIVE | PM_FILT_CLOCK);
        }
        if (test_out) {
            int i = get_number("MIDI output device number: ");
            PmEvent buffer[1];
            Pm_OpenOutput(&out, 
                  i,
                  NULL,
                  OUTPUT_BUFFER_SIZE,
                  (long (*)(void *)) Pt_Time,
                  NULL, 
                  0); /* no latency scheduling */

            /* send a program change to force a status byte -- this fixes
               a problem with a buggy linux MidiSport driver, and shouldn't
               hurt anything else
             */
            buffer[0].timestamp = 0;
            buffer[0].message = Pm_Message(0xC0, 0, 0); /* program change */
            Pm_Write(out, buffer, 1);

            output_period = get_number(
                "MIDI out should be sent every __ callback iterations: ");

            assert(output_period >= 1);
        }
    }

    printf("%s%s", "Latency measurements will start in 5 seconds. ",
                   "Type return to stop: ");
    Pt_Start(period, &pt_callback, 0);
    fgets(line, STRING_MAX, stdin);
    stop = Pt_Time();
    Pt_Stop();

    /* courteously turn off the last note, if necessary */
    if (note_on) {
       PmEvent buffer[1];
       buffer[0].timestamp = Pt_Time(NULL);
       buffer[0].message = Pm_Message(0x90, 60, 0);
       Pm_Write(out, buffer, 1);
    }

    /* print the histogram */
    printf("Duration of test: %g seconds\n\n", max(0, stop - 5000) * 0.001);
    printf("Latency(ms)  Number of occurrences\n");
    /* avoid printing beyond last non-zero histogram entry */
    len = min(HIST_LEN, max_latency + 1);
    for (i = 0; i < len; i++) {
        printf("%2d      %10ld\n", i, histogram[i]);
    }
    printf("Number of points greater than %dms: %ld\n", 
           HIST_LEN - 1, out_of_range);
    printf("Maximum latency: %ld milliseconds\n", max_latency);
    printf("\nNote that due to rounding, actual latency can be 1ms higher\n");
    printf("than the numbers reported here.\n");
    printf("Type return to exit...");
    fgets(line, STRING_MAX, stdin);
    return 0;
}


/* read a number from console */
int get_number(char *prompt)
{
    char line[STRING_MAX];
    int n = 0, i;
    printf(prompt);
    while (n != 1) {
        n = scanf("%d", &i);
        fgets(line, STRING_MAX, stdin);

    }
    return i;
}

--- NEW FILE: test.dsp ---
# Microsoft Developer Studio Project File - Name="test" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Console Application" 0x0103

CFG=test - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "test.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "test.mak" CFG="test - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "test - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "test - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "$(CFG)" == "test - Win32 Release"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "testRelease"
# PROP Intermediate_Dir "testRelease"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /GX /O2 /I "../pm_common" /I "../porttime" /D "WIN32" /D "DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\Release\portmidi.lib ..\porttime\Release\porttime.lib ..\pm_win\Release\pm_dll.lib /nologo /subsystem:console /machine:I386

!ELSEIF  "$(CFG)" == "test - Win32 Debug"

# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "testDebug"
# PROP Intermediate_Dir "testDebug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../pm_common" /I "../porttime" /D "_CONSOLE" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ..\pm_win\Debug\portmidi.lib ..\porttime\Debug\porttime.lib ..\pm_win\Debug\pm_dll.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept

!ENDIF 

# Begin Target

# Name "test - Win32 Release"
# Name "test - Win32 Debug"
# Begin Group "Source Files"

# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File

SOURCE=.\test.c
# End Source File
# End Group
# Begin Group "Header Files"

# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"

# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project





More information about the Pd-cvs mailing list