portmidi/ 0000755 0000000 0000000 00000000000 11453601074 011404 5 ustar root root portmidi/pm_test/ 0000755 0000000 0000000 00000000000 11453601074 013057 5 ustar root root portmidi/pm_test/qtest.vcproj 0000755 0000000 0000000 00000011257 11274243554 015463 0 ustar root root
portmidi/pm_test/midithread.c 0000755 0000000 0000000 00000026734 11254141714 015353 0 ustar root root /* 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;
int 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 */
int32_t 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) {
int 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;
int32_t 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 int32_t's
* for our messages in this simple example
*/
midi_to_main = Pm_QueueCreate(32, sizeof(int32_t));
assert(midi_to_main != NULL);
main_to_midi = Pm_QueueCreate(32, sizeof(int32_t));
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) {
int32_t msg;
int input;
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 */ ;
// convert int32_t to long for safe printing
printf("... pitch is %ld\n", (long) 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, "%d", &input) == 1) {
if (input >= -127 && input <= 127) {
/* send transposition value, make sur */
printf("Transposing by %d\n", input);
msg = (int32_t) input;
Pm_Enqueue(main_to_midi, &msg);
} else {
printf("Transposition must be within -127...127\n");
}
} else {
printf("%s\n%s\n",
"Type 'q[ENTER]' to quit, 'm[ENTER]' to monitor next pitch, or",
"enter a number to specify transposition.");
}
}
/* 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;
}
portmidi/pm_test/midithru.vcproj 0000755 0000000 0000000 00000011323 11274243554 016142 0 ustar root root
portmidi/pm_test/mm.c 0000755 0000000 0000000 00000045653 11452617306 013660 0 ustar root root /* mm.c -- midi monitor */
/*****************************************************************************
* Change Log
* Date | Change
*-----------+-----------------------------------------------------------------
* 7-Apr-86 | Created changelog
* 31-Jan-90 | GWL : use new cmdline
* 5-Apr-91 | JDW : Further changes
* 16-Feb-92 | GWL : eliminate label mmexit:; add error recovery
* 18-May-92 | GWL : continuous clocks, etc.
* 17-Jan-94 | GWL : option to display notes
* 20-Nov-06 | RBD : port mm.c from CMU Midi Toolkit to PortMidi
* | mm.c -- revealing MIDI secrets for over 20 years!
*****************************************************************************/
#include "stdlib.h"
#include "ctype.h"
#include "string.h"
#include "stdio.h"
#include "porttime.h"
#include "portmidi.h"
#define STRING_MAX 80
#define MIDI_CODE_MASK 0xf0
#define MIDI_CHN_MASK 0x0f
/*#define MIDI_REALTIME 0xf8
#define MIDI_CHAN_MODE 0xfa */
#define MIDI_OFF_NOTE 0x80
#define MIDI_ON_NOTE 0x90
#define MIDI_POLY_TOUCH 0xa0
#define MIDI_CTRL 0xb0
#define MIDI_CH_PROGRAM 0xc0
#define MIDI_TOUCH 0xd0
#define MIDI_BEND 0xe0
#define MIDI_SYSEX 0xf0
#define MIDI_Q_FRAME 0xf1
#define MIDI_SONG_POINTER 0xf2
#define MIDI_SONG_SELECT 0xf3
#define MIDI_TUNE_REQ 0xf6
#define MIDI_EOX 0xf7
#define MIDI_TIME_CLOCK 0xf8
#define MIDI_START 0xfa
#define MIDI_CONTINUE 0xfb
#define MIDI_STOP 0xfc
#define MIDI_ACTIVE_SENSING 0xfe
#define MIDI_SYS_RESET 0xff
#define MIDI_ALL_SOUND_OFF 0x78
#define MIDI_RESET_CONTROLLERS 0x79
#define MIDI_LOCAL 0x7a
#define MIDI_ALL_OFF 0x7b
#define MIDI_OMNI_OFF 0x7c
#define MIDI_OMNI_ON 0x7d
#define MIDI_MONO_ON 0x7e
#define MIDI_POLY_ON 0x7f
#define private static
#ifndef false
#define false 0
#define true 1
#endif
typedef int boolean;
int debug = false; /* never set, but referenced by userio.c */
PmStream *midi_in; /* midi input */
boolean active = false; /* set when midi_in is ready for reading */
boolean in_sysex = false; /* we are reading a sysex message */
boolean inited = false; /* suppress printing during command line parsing */
boolean done = false; /* when true, exit */
boolean notes = true; /* show notes? */
boolean controls = true; /* show continuous controllers */
boolean bender = true; /* record pitch bend etc.? */
boolean excldata = true; /* record system exclusive data? */
boolean verbose = true; /* show text representation? */
boolean realdata = true; /* record real time messages? */
boolean clksencnt = true; /* clock and active sense count on */
boolean chmode = true; /* show channel mode messages */
boolean pgchanges = true; /* show program changes */
boolean flush = false; /* flush all pending MIDI data */
uint32_t filter = 0; /* remember state of midi filter */
uint32_t clockcount = 0; /* count of clocks */
uint32_t actsensecount = 0; /* cout of active sensing bytes */
uint32_t notescount = 0; /* #notes since last request */
uint32_t notestotal = 0; /* total #notes */
char val_format[] = " Val %d\n";
/*****************************************************************************
* Imported variables
*****************************************************************************/
extern int abort_flag;
/*****************************************************************************
* Routines local to this module
*****************************************************************************/
private void mmexit(int code);
private void output(PmMessage data);
private int put_pitch(int p);
private void showhelp();
private void showbytes(PmMessage data, int len, boolean newline);
private void showstatus(boolean flag);
private void doascii(char c);
private int get_number(char *prompt);
/* 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;
}
void receive_poll(PtTimestamp timestamp, void *userData)
{
PmEvent event;
int count;
if (!active) return;
while ((count = Pm_Read(midi_in, &event, 1))) {
if (count == 1) output(event.message);
else printf(Pm_GetErrorText(count));
}
}
/****************************************************************************
* main
* Effect: prompts for parameters, starts monitor
****************************************************************************/
int main(int argc, char **argv)
{
char *argument;
int inp;
PmError err;
int i;
if (argc > 1) { /* first arg can change defaults */
argument = argv[1];
while (*argument) doascii(*argument++);
}
showhelp();
/* use porttime callback to empty midi queue and print */
Pt_Start(1, receive_poll, 0);
/* list device information */
printf("MIDI input devices:\n");
for (i = 0; i < Pm_CountDevices(); i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
if (info->input) printf("%d: %s, %s\n", i, info->interf, info->name);
}
inp = get_number("Type input device number: ");
err = Pm_OpenInput(&midi_in, inp, NULL, 512, NULL, NULL);
if (err) {
printf(Pm_GetErrorText(err));
Pt_Stop();
mmexit(1);
}
Pm_SetFilter(midi_in, filter);
inited = true; /* now can document changes, set filter */
printf("Midi Monitor ready.\n");
active = true;
while (!done) {
char s[100];
if (fgets(s, 100, stdin)) {
doascii(s[0]);
}
}
active = false;
Pm_Close(midi_in);
Pt_Stop();
Pm_Terminate();
mmexit(0);
return 0; // make the compiler happy be returning a value
}
/****************************************************************************
* doascii
* Inputs:
* char c: input character
* Effect: interpret to revise flags
****************************************************************************/
private void doascii(char c)
{
if (isupper(c)) c = tolower(c);
if (c == 'q') done = true;
else if (c == 'b') {
bender = !bender;
filter ^= PM_FILT_PITCHBEND;
if (inited)
printf("Pitch Bend, etc. %s\n", (bender ? "ON" : "OFF"));
} else if (c == 'c') {
controls = !controls;
filter ^= PM_FILT_CONTROL;
if (inited)
printf("Control Change %s\n", (controls ? "ON" : "OFF"));
} else if (c == 'h') {
pgchanges = !pgchanges;
filter ^= PM_FILT_PROGRAM;
if (inited)
printf("Program Changes %s\n", (pgchanges ? "ON" : "OFF"));
} else if (c == 'n') {
notes = !notes;
filter ^= PM_FILT_NOTE;
if (inited)
printf("Notes %s\n", (notes ? "ON" : "OFF"));
} else if (c == 'x') {
excldata = !excldata;
filter ^= PM_FILT_SYSEX;
if (inited)
printf("System Exclusive data %s\n", (excldata ? "ON" : "OFF"));
} else if (c == 'r') {
realdata = !realdata;
filter ^= (PM_FILT_PLAY | PM_FILT_RESET | PM_FILT_TICK | PM_FILT_UNDEFINED);
if (inited)
printf("Real Time messages %s\n", (realdata ? "ON" : "OFF"));
} else if (c == 'k') {
clksencnt = !clksencnt;
filter ^= PM_FILT_CLOCK;
if (inited)
printf("Clock and Active Sense Counting %s\n", (clksencnt ? "ON" : "OFF"));
if (!clksencnt) clockcount = actsensecount = 0;
} else if (c == 's') {
if (clksencnt) {
if (inited)
printf("Clock Count %ld\nActive Sense Count %ld\n",
(long) clockcount, (long) actsensecount);
} else if (inited) {
printf("Clock Counting not on\n");
}
} else if (c == 't') {
notestotal+=notescount;
if (inited)
printf("This Note Count %ld\nTotal Note Count %ld\n",
(long) notescount, (long) notestotal);
notescount=0;
} else if (c == 'v') {
verbose = !verbose;
if (inited)
printf("Verbose %s\n", (verbose ? "ON" : "OFF"));
} else if (c == 'm') {
chmode = !chmode;
if (inited)
printf("Channel Mode Messages %s", (chmode ? "ON" : "OFF"));
} else {
if (inited) {
if (c == ' ') {
PmEvent event;
while (Pm_Read(midi_in, &event, 1)) ; /* flush midi input */
printf("...FLUSHED MIDI INPUT\n\n");
} else showhelp();
}
}
if (inited) Pm_SetFilter(midi_in, filter);
}
private void mmexit(int code)
{
/* if this is not being run from a console, maybe we should wait for
* the user to read error messages before exiting
*/
exit(code);
}
/****************************************************************************
* output
* Inputs:
* data: midi message buffer holding one command or 4 bytes of sysex msg
* Effect: format and print midi data
****************************************************************************/
char vel_format[] = " Vel %d\n";
private void output(PmMessage data)
{
int command; /* the current command */
int chan; /* the midi channel of the current event */
int len; /* used to get constant field width */
/* printf("output data %8x; ", data); */
command = Pm_MessageStatus(data) & MIDI_CODE_MASK;
chan = Pm_MessageStatus(data) & MIDI_CHN_MASK;
if (in_sysex || Pm_MessageStatus(data) == MIDI_SYSEX) {
#define sysex_max 16
int i;
PmMessage data_copy = data;
in_sysex = true;
/* look for MIDI_EOX in first 3 bytes
* if realtime messages are embedded in sysex message, they will
* be printed as if they are part of the sysex message
*/
for (i = 0; (i < 4) && ((data_copy & 0xFF) != MIDI_EOX); i++)
data_copy >>= 8;
if (i < 4) {
in_sysex = false;
i++; /* include the EOX byte in output */
}
showbytes(data, i, verbose);
if (verbose) printf("System Exclusive\n");
} else if (command == MIDI_ON_NOTE && Pm_MessageData2(data) != 0) {
notescount++;
if (notes) {
showbytes(data, 3, verbose);
if (verbose) {
printf("NoteOn Chan %2d Key %3d ", chan, Pm_MessageData1(data));
len = put_pitch(Pm_MessageData1(data));
printf(vel_format + len, Pm_MessageData2(data));
}
}
} else if ((command == MIDI_ON_NOTE /* && Pm_MessageData2(data) == 0 */ ||
command == MIDI_OFF_NOTE) && notes) {
showbytes(data, 3, verbose);
if (verbose) {
printf("NoteOff Chan %2d Key %3d ", chan, Pm_MessageData1(data));
len = put_pitch(Pm_MessageData1(data));
printf(vel_format + len, Pm_MessageData2(data));
}
} else if (command == MIDI_CH_PROGRAM && pgchanges) {
showbytes(data, 2, verbose);
if (verbose) {
printf(" ProgChg Chan %2d Prog %2d\n", chan, Pm_MessageData1(data) + 1);
}
} else if (command == MIDI_CTRL) {
/* controls 121 (MIDI_RESET_CONTROLLER) to 127 are channel
* mode messages. */
if (Pm_MessageData1(data) < MIDI_ALL_SOUND_OFF) {
showbytes(data, 3, verbose);
if (verbose) {
printf("CtrlChg Chan %2d Ctrl %2d Val %2d\n",
chan, Pm_MessageData1(data), Pm_MessageData2(data));
}
} else /* channel mode */ if (chmode) {
showbytes(data, 3, verbose);
if (verbose) {
switch (Pm_MessageData1(data)) {
case MIDI_ALL_SOUND_OFF:
printf("All Sound Off, Chan %2d\n", chan);
break;
case MIDI_RESET_CONTROLLERS:
printf("Reset All Controllers, Chan %2d\n", chan);
break;
case MIDI_LOCAL:
printf("LocCtrl Chan %2d %s\n",
chan, Pm_MessageData2(data) ? "On" : "Off");
break;
case MIDI_ALL_OFF:
printf("All Off Chan %2d\n", chan);
break;
case MIDI_OMNI_OFF:
printf("OmniOff Chan %2d\n", chan);
break;
case MIDI_OMNI_ON:
printf("Omni On Chan %2d\n", chan);
break;
case MIDI_MONO_ON:
printf("Mono On Chan %2d\n", chan);
if (Pm_MessageData2(data))
printf(" to %d received channels\n", Pm_MessageData2(data));
else
printf(" to all received channels\n");
break;
case MIDI_POLY_ON:
printf("Poly On Chan %2d\n", chan);
break;
}
}
}
} else if (command == MIDI_POLY_TOUCH && bender) {
showbytes(data, 3, verbose);
if (verbose) {
printf("P.Touch Chan %2d Key %2d ", chan, Pm_MessageData1(data));
len = put_pitch(Pm_MessageData1(data));
printf(val_format + len, Pm_MessageData2(data));
}
} else if (command == MIDI_TOUCH && bender) {
showbytes(data, 2, verbose);
if (verbose) {
printf(" A.Touch Chan %2d Val %2d\n", chan, Pm_MessageData1(data));
}
} else if (command == MIDI_BEND && bender) {
showbytes(data, 3, verbose);
if (verbose) {
printf("P.Bend Chan %2d Val %2d\n", chan,
(Pm_MessageData1(data) + (Pm_MessageData2(data)<<7)));
}
} else if (Pm_MessageStatus(data) == MIDI_SONG_POINTER) {
showbytes(data, 3, verbose);
if (verbose) {
printf(" Song Position %d\n",
(Pm_MessageData1(data) + (Pm_MessageData2(data)<<7)));
}
} else if (Pm_MessageStatus(data) == MIDI_SONG_SELECT) {
showbytes(data, 2, verbose);
if (verbose) {
printf(" Song Select %d\n", Pm_MessageData1(data));
}
} else if (Pm_MessageStatus(data) == MIDI_TUNE_REQ) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" Tune Request\n");
}
} else if (Pm_MessageStatus(data) == MIDI_Q_FRAME && realdata) {
showbytes(data, 2, verbose);
if (verbose) {
printf(" Time Code Quarter Frame Type %d Values %d\n",
(Pm_MessageData1(data) & 0x70) >> 4, Pm_MessageData1(data) & 0xf);
}
} else if (Pm_MessageStatus(data) == MIDI_START && realdata) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" Start\n");
}
} else if (Pm_MessageStatus(data) == MIDI_CONTINUE && realdata) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" Continue\n");
}
} else if (Pm_MessageStatus(data) == MIDI_STOP && realdata) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" Stop\n");
}
} else if (Pm_MessageStatus(data) == MIDI_SYS_RESET && realdata) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" System Reset\n");
}
} else if (Pm_MessageStatus(data) == MIDI_TIME_CLOCK) {
if (clksencnt) clockcount++;
else if (realdata) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" Clock\n");
}
}
} else if (Pm_MessageStatus(data) == MIDI_ACTIVE_SENSING) {
if (clksencnt) actsensecount++;
else if (realdata) {
showbytes(data, 1, verbose);
if (verbose) {
printf(" Active Sensing\n");
}
}
} else showbytes(data, 3, verbose);
fflush(stdout);
}
/****************************************************************************
* put_pitch
* Inputs:
* int p: pitch number
* Effect: write out the pitch name for a given number
****************************************************************************/
private int put_pitch(int p)
{
char result[8];
static char *ptos[] = {
"c", "cs", "d", "ef", "e", "f", "fs", "g",
"gs", "a", "bf", "b" };
/* note octave correction below */
sprintf(result, "%s%d", ptos[p % 12], (p / 12) - 1);
printf(result);
return strlen(result);
}
/****************************************************************************
* showbytes
* Effect: print hex data, precede with newline if asked
****************************************************************************/
char nib_to_hex[] = "0123456789ABCDEF";
private void showbytes(PmMessage data, int len, boolean newline)
{
int count = 0;
int i;
/* if (newline) {
putchar('\n');
count++;
} */
for (i = 0; i < len; i++) {
putchar(nib_to_hex[(data >> 4) & 0xF]);
putchar(nib_to_hex[data & 0xF]);
count += 2;
if (count > 72) {
putchar('.');
putchar('.');
putchar('.');
break;
}
data >>= 8;
}
putchar(' ');
}
/****************************************************************************
* showhelp
* Effect: print help text
****************************************************************************/
private void showhelp()
{
printf("\n");
printf(" Item Reported Range Item Reported Range\n");
printf(" ------------- ----- ------------- -----\n");
printf(" Channels 1 - 16 Programs 1 - 128\n");
printf(" Controllers 0 - 127 After Touch 0 - 127\n");
printf(" Loudness 0 - 127 Pitch Bend 0 - 16383, center = 8192\n");
printf(" Pitches 0 - 127, 60 = c4 = middle C\n");
printf(" \n");
printf("n toggles notes");
showstatus(notes);
printf("t displays noteon count since last t\n");
printf("b toggles pitch bend, aftertouch");
showstatus(bender);
printf("c toggles continuous control");
showstatus(controls);
printf("h toggles program changes");
showstatus(pgchanges);
printf("x toggles system exclusive");
showstatus(excldata);
printf("k toggles clock and sense counting only");
showstatus(clksencnt);
printf("r toggles other real time messages & SMPTE");
showstatus(realdata);
printf("s displays clock and sense count since last k\n");
printf("m toggles channel mode messages");
showstatus(chmode);
printf("v toggles verbose text");
showstatus(verbose);
printf("q quits\n");
printf("\n");
}
/****************************************************************************
* showstatus
* Effect: print status of flag
****************************************************************************/
private void showstatus(boolean flag)
{
printf(", now %s\n", flag ? "ON" : "OFF" );
}
portmidi/pm_test/sysex.vcproj 0000755 0000000 0000000 00000011257 11274243554 015476 0 ustar root root
portmidi/pm_test/qtest.c 0000644 0000000 0000000 00000011160 11301353626 014361 0 ustar root root #include "portmidi.h"
#include "pmutil.h"
#include "stdlib.h"
#include "stdio.h"
/* make_msg -- make a psuedo-random message of length n whose content
* is purely a function of i
*/
void make_msg(long msg[], int n, int i)
{
int j;
for (j = 0; j < n; j++) {
msg[j] = i % (j + 5);
}
}
/* print_msg -- print the content of msg of length n to stdout */
/**/
void print_msg(long msg[], int n)
{
int i;
for (i = 0; i < n; i++) {
printf(" %li", msg[i]);
}
}
/* cmp_msg -- compare two messages of length n */
/**/
int cmp_msg(long msg[], long msg2[], int n, int i)
{
int j;
for (j = 0; j < n; j++) {
if (msg[j] != msg2[j]) {
printf("Received message %d doesn't match sent message\n", i);
printf("in: "); print_msg(msg, n); printf("\n");
printf("out:"); print_msg(msg2, n); printf("\n");
return FALSE;
}
}
return TRUE;
}
int main()
{
int msg_len;
for (msg_len = 4; msg_len < 100; msg_len += 5) {
PmQueue *queue = Pm_QueueCreate(100, msg_len * sizeof(long));
int i;
long msg[100];
long msg2[100];
printf("msg_len = %d\n", msg_len);
if (!queue) {
printf("Could not allocate queue\n");
return 1;
}
/* insert/remove 1000 messages */
printf("test 1\n");
for (i = 0; i < 1357; i++) {
make_msg(msg, msg_len, i);
if (Pm_Enqueue(queue, msg)) {
printf("Pm_Enqueue error\n");
return 1;
}
if (Pm_Dequeue(queue, msg2) != 1) {
printf("Pm_Dequeue error\n");
return 1;
}
if (!cmp_msg(msg, msg2, msg_len, i)) {
return 1;
}
}
/* make full */
printf("test 2\n");
for (i = 0; i < 100; i++) {
make_msg(msg, msg_len, i);
if (Pm_Enqueue(queue, msg)) {
printf("Pm_Enqueue error\n");
return 1;
}
}
/* alternately remove and insert */
for (i = 100; i < 1234; i++) {
make_msg(msg, msg_len, i - 100); /* what we expect */
if (Pm_Dequeue(queue, msg2) != 1) {
printf("Pm_Dequeue error\n");
return 1;
}
if (!cmp_msg(msg, msg2, msg_len, i)) {
return 1;
}
make_msg(msg, msg_len, i);
if (Pm_Enqueue(queue, msg)) {
printf("Pm_Enqueue error\n");
return 1;
}
}
/* remove all */
while (!Pm_QueueEmpty(queue)) {
make_msg(msg, msg_len, i - 100); /* what we expect */
if (Pm_Dequeue(queue, msg2) != 1) {
printf("Pm_Dequeue error\n");
return 1;
}
if (!cmp_msg(msg, msg2, msg_len, i)) {
return 1;
}
i++;
}
if (i != 1334) {
printf("Message count error\n");
return 1;
}
/* now test overflow */
printf("test 3\n");
for (i = 0; i < 110; i++) {
make_msg(msg, msg_len, i);
if (Pm_Enqueue(queue, msg) == pmBufferOverflow) {
break; /* this is supposed to execute after 100 messages */
}
}
for (i = 0; i < 100; i++) {
make_msg(msg, msg_len, i);
if (Pm_Dequeue(queue, msg2) != 1) {
printf("Pm_Dequeue error\n");
return 1;
}
if (!cmp_msg(msg, msg2, msg_len, i)) {
return 1;
}
}
/* we should detect overflow after removing 100 messages */
if (Pm_Dequeue(queue, msg2) != pmBufferOverflow) {
printf("Pm_Dequeue overflow expected\n");
return 1;
}
/* after overflow is detected (and cleared), sender can
* send again
*/
/* see if function is restored, also test peek */
printf("test 4\n");
for (i = 0; i < 1357; i++) {
long *peek;
make_msg(msg, msg_len, i);
if (Pm_Enqueue(queue, msg)) {
printf("Pm_Enqueue error\n");
return 1;
}
peek = (long *) Pm_QueuePeek(queue);
if (!cmp_msg(msg, peek, msg_len, i)) {
return 1;
}
if (Pm_Dequeue(queue, msg2) != 1) {
printf("Pm_Dequeue error\n");
return 1;
}
if (!cmp_msg(msg, msg2, msg_len, i)) {
return 1;
}
}
Pm_QueueDestroy(queue);
}
return 0;
}
portmidi/pm_test/sysex.c 0000755 0000000 0000000 00000037177 11445664134 014427 0 ustar root root /* 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"
#ifdef WIN32
// need to get declaration for Sleep()
#include "windows.h"
#else
#include
#define Sleep(n) usleep(n * 1000)
#endif
#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7
#define STRING_MAX 80
#ifndef true
#define true 1
#define false 0
#endif
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];
int32_t len;
int i;
int data;
PmEvent event;
int shift;
long total_bytes = 0;
int32_t begin_time;
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.
*/
while ((latency = get_number(
"Latency in milliseconds (0 to send data immediatedly,\n"
" >0 to send timestamped messages): ")) < 0);
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 */
begin_time = Pt_Time();
while (1) {
PmError count;
int32_t start_time;
int error_position = -1; /* 0; -1; -1 for continuous */
int expected = 0;
int actual = 0;
/* this modification will run until an error is detected */
/* set error_position above to 0 for interactive, -1 for */
/* continuous */
if (error_position >= 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++) {
/* pick whether data is sequential or random... (docs say random) */
#define DATA_EXPR (i+1)
// #define DATA_EXPR rand()
msg[i + 1] = DATA_EXPR & 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 %d 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);
if (count == 0) {
Sleep(1); /* be nice: give some CPU time to the system */
continue; /* continue polling for input */
}
/* 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 %d: sent %x recd %x.\n", error_position,
expected, actual);
break;
} else if (i != len + 2) {
printf("Error: byte %d not received.\n", i);
break;
} else {
int seconds = (Pt_Time() - begin_time) / 1000;
if (seconds == 0) seconds = 1;
printf("Correctly received %d byte sysex message.\n", i);
total_bytes += i;
printf("Cummulative bytes/sec: %d\n", total_bytes / seconds);
}
}
cleanup:
Pm_Close(midi_out);
Pm_Close(midi_in);
return;
}
/* send_multiple test -- send many sysex messages */
/**/
void send_multiple_test()
{
int outp;
int length;
int num_msgs;
PmStream *midi_out;
unsigned char msg[1024];
int i;
PtTimestamp start_time;
PtTimestamp stop_time;
Pt_Start(1, 0, 0);
printf("This is for performance testing. You should be sending to this\n");
printf("program running the receive multiple test. Do NOT send to\n");
printf("a synthesizer or you risk reprogramming it\n");
outp = get_number("Type output device number: ");
while ((latency = get_number(
"Latency in milliseconds (0 to send data immediatedly,\n"
" >0 to send timestamped messages): ")) < 0);
Pm_OpenOutput(&midi_out, outp, NULL, 0, NULL, NULL, latency);
while ((length = get_number("Message length (7 - 1024): ")) < 7 ||
length > 1024) ;
while ((num_msgs = get_number("Number of messages: ")) < 1);
/* latency, length, and num_msgs should now all be valid */
/* compose the message except for sequence number in first 5 bytes */
msg[0] = (char) MIDI_SYSEX;
for (i = 6; i < length - 1; i++) {
msg[i] = i % 128; /* this is just filler */
}
msg[length - 1] = (char) MIDI_EOX;
start_time = Pt_Time();
/* send the messages */
for (i = num_msgs; i > 0; i--) {
/* insert sequence number into first 5 data bytes */
/* sequence counts down to zero */
int j;
int count = i;
/* 7 bits of message count i goes into each data byte */
for (j = 1; j <= 5; j++) {
msg[j] = count & 127;
count >>= 7;
}
/* send the message */
Pm_WriteSysEx(midi_out, 0, msg);
}
stop_time = Pt_Time();
Pm_Close(midi_out);
return;
}
#define MAX_MSG_LEN 1024
static unsigned char receive_msg[MAX_MSG_LEN];
static int receive_msg_index;
static int receive_msg_length;
static int receive_msg_count;
static int receive_msg_error;
static int receive_msg_messages;
static PmStream *receive_msg_midi_in;
static int receive_poll_running;
/* receive_poll -- callback function to check for midi input */
/**/
void receive_poll(PtTimestamp timestamp, void *userData)
{
PmError count;
PmEvent event;
int shift;
int data = 0;
int i;
if (!receive_poll_running) return; /* wait until midi device is opened */
shift = 0;
while (data != MIDI_EOX) {
count = Pm_Read(receive_msg_midi_in, &event, 1);
if (count == 0) return;
/* compare 4 bytes of data until you reach an eox */
for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) {
receive_msg[receive_msg_index++] = data =
(event.message >> shift) & 0xFF;
if (receive_msg_index >= MAX_MSG_LEN) {
printf("error: incoming sysex too long\n");
goto error;
}
}
}
/* check the message */
if (receive_msg_length == 0) {
receive_msg_length = receive_msg_index;
}
if (receive_msg_length != receive_msg_index) {
printf("error: incoming sysex wrong length\n");
goto error;
}
if (receive_msg[0] != MIDI_SYSEX) {
printf("error: incoming sysex missing status byte\n");
goto error;
}
/* get and check the count */
count = 0;
for (i = 0; i < 5; i++) {
count += receive_msg[i + 1] << (7 * i);
}
if (receive_msg_count == -1) {
receive_msg_count = count;
receive_msg_messages = count;
}
if (receive_msg_count != count) {
printf("error: incoming sysex has wrong count\n");
goto error;
}
for (i = 6; i < receive_msg_index - 1; i++) {
if (receive_msg[i] != i % 128) {
printf("error: incoming sysex has bad data\n");
goto error;
}
}
if (receive_msg[receive_msg_length - 1] != MIDI_EOX) goto error;
receive_msg_index = 0; /* get ready for next message */
receive_msg_count--;
return;
error:
receive_msg_error = 1;
return;
}
/* receive_multiple_test -- send/rcv from 2 to 1000 bytes of random midi data */
/**/
void receive_multiple_test()
{
PmError err;
int inp;
printf("This test expects to receive data sent by the send_multiple test\n");
printf("The test will check that correct data is received.\n");
/* Important: start PortTime first -- if it is not started first, it will
be started by PortMidi, and then our attempt to open again will fail */
receive_poll_running = false;
if ((err = Pt_Start(1, receive_poll, 0))) {
printf("PortTime error code: %d\n", err);
goto cleanup;
}
inp = get_number("Type input device number: ");
Pm_OpenInput(&receive_msg_midi_in, inp, NULL, 512, NULL, NULL);
receive_msg_index = 0;
receive_msg_length = 0;
receive_msg_count = -1;
receive_msg_error = 0;
receive_poll_running = true;
while ((!receive_msg_error) && (receive_msg_count != 0)) {
#ifdef WIN32
Sleep(1000);
#else
sleep(1); /* block and wait */
#endif
}
if (receive_msg_error) {
printf("Receive_multiple test encountered an error\n");
} else {
printf("Receive_multiple test successfully received %d sysex messages\n",
receive_msg_messages);
}
cleanup:
receive_poll_running = false;
Pm_Close(receive_msg_midi_in);
Pt_Stop();
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: ");
while ((latency = get_number(
"Latency in milliseconds (0 to send data immediatedly,\n"
" >0 to send timestamped messages): ")) < 0);
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");
}
while (1) {
printf("Type r to receive sysex, s to send,"
" l for loopback test, m to send multiple,"
" n to receive multiple, 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();
break;
case 'm':
send_multiple_test();
break;
case 'n':
receive_multiple_test();
break;
case 'q':
exit(0);
default:
break;
}
}
return 0;
}
portmidi/pm_test/mm.vcproj 0000755 0000000 0000000 00000011213 11274243554 014724 0 ustar root root
portmidi/pm_test/test.c 0000755 0000000 0000000 00000036614 11254141714 014216 0 ustar root root #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 ((int32_t (*)(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 */
int32_t 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 | PM_FILT_SYSEX);
/* 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,
(long) buffer[0].timestamp,
(long) Pm_MessageStatus(buffer[0].message),
(long) Pm_MessageData1(buffer[0].message),
(long) 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];
int32_t 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", (long) 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);
/* Send a program change to increase the chances we will hear notes */
/* Program 0 is usually a piano, but you can change it here: */
#define PROGRAM 0
buffer[0].message = Pm_Message(0xC0, PROGRAM, 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", (long) 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,
(long) buffer[0].timestamp,
(long) Pm_MessageStatus(buffer[0].message),
(long) Pm_MessageData1(buffer[0].message),
(long) Pm_MessageData2(buffer[0].message));
i++;
} else {
assert(0);
}
}
}
/* close 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", (long) 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 default_in;
int default_out;
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;
if (sizeof(void *) == 8)
printf("Apparently this is a 64-bit machine.\n");
else if (sizeof(void *) == 4)
printf ("Apparently this is a 32-bit machine.\n");
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", (long) latency);
latency_valid = TRUE;
} else {
show_usage();
}
}
while (!latency_valid) {
int lat; // declared int to match "%d"
printf("Latency in ms: ");
if (scanf("%d", &lat) == 1) {
latency = (int32_t) lat; // coerce from "%d" to known size
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 */
default_in = Pm_GetDefaultInputDeviceID();
default_out = Pm_GetDefaultOutputDeviceID();
for (i = 0; i < Pm_CountDevices(); i++) {
char *deflt;
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) {
deflt = (i == default_in ? "default " : "");
printf(" (%sinput)", deflt);
}
if (info->output) {
deflt = (i == default_out ? "default " : "");
printf(" (%soutput)", deflt);
}
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;
}
portmidi/pm_test/midithread.vcproj 0000755 0000000 0000000 00000011353 11274243554 016432 0 ustar root root
portmidi/pm_test/test.vcproj 0000755 0000000 0000000 00000011243 11274243554 015275 0 ustar root root
portmidi/pm_test/midiclock.vcproj 0000644 0000000 0000000 00000011337 11274243554 016255 0 ustar root root
portmidi/pm_test/txdata.syx 0000755 0000000 0000000 00000030414 10463413276 015123 0 ustar root root 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 portmidi/pm_test/latency.c 0000755 0000000 0000000 00000023264 11254141714 014673 0 ustar root root /* 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 */
int histogram[HIST_LEN];
int max_latency = 0; /* worst latency observed */
int 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 = 0;
while (period < 1) {
period = get_number("Choose timer period (in ms, >= 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,
(PmTimestamp (*)(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,
(PmTimestamp (*)(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 %10d\n", i, histogram[i]);
}
printf("Number of points greater than %dms: %d\n",
HIST_LEN - 1, out_of_range);
printf("Maximum latency: %d 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);
if(choice == 2)
Pm_Close(in);
else if(choice == 3)
Pm_Close(out);
else if(choice == 4)
{
Pm_Close(in);
Pm_Close(out);
}
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;
}
portmidi/pm_test/midiclock.c 0000644 0000000 0000000 00000021324 11262124064 015160 0 ustar root root /* miditime.c -- a test program that sends midi clock and MTC */
#include "portmidi.h"
#include "porttime.h"
#include
#include
#include
#include
#include
#ifndef false
#define false 0
#define true 1
#endif
#define private static
typedef int boolean;
#define MIDI_TIME_CLOCK 0xf8
#define MIDI_START 0xfa
#define MIDI_CONTINUE 0xfb
#define MIDI_STOP 0xfc
#define MIDI_Q_FRAME 0xf1
#define OUTPUT_BUFFER_SIZE 0
#define DRIVER_INFO NULL
#define TIME_PROC ((int32_t (*)(void *)) Pt_Time)
#define TIME_INFO NULL
#define LATENCY 0
#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */
#define STRING_MAX 80 /* used for console input */
/* to determine ms per clock:
* time per beat in seconds = 60 / tempo
* multiply by 1000 to get time per beat in ms: 60000 / tempo
* divide by 24 CLOCKs per beat: (60000/24) / tempo
* simplify: 2500 / tempo
*/
#define TEMPO_TO_CLOCK 2500.0
boolean done = false;
PmStream *midi;
/* shared flags to control callback output generation: */
boolean clock_running = false;
boolean send_start_stop = false;
boolean time_code_running = false;
boolean active = false; /* tells callback to do its thing */
float tempo = 60.0F;
/* protocol for handing off portmidi to callback thread:
main owns portmidi
main sets active = true: ownership transfers to callback
main sets active = false: main requests ownership
callback sees active == false, yields ownership back to main
main waits 2ms to make sure callback has a chance to yield
(stop making PortMidi calls), then assumes it can close
PortMidi
*/
/* timer_poll -- the timer callback function */
/*
* All MIDI sends take place here
*/
void timer_poll(PtTimestamp timestamp, void *userData)
{
static int callback_owns_portmidi = false;
static PmTimestamp clock_start_time = 0;
static double next_clock_time = 0;
/* SMPTE time */
static int frames = 0;
static int seconds = 0;
static int minutes = 0;
static int hours = 0;
static int mtc_count = 0; /* where are we in quarter frame sequence? */
static int smpte_start_time = 0;
static double next_smpte_time = 0;
#define QUARTER_FRAME_PERIOD (1.0 / 120.0) /* 30fps, 1/4 frame */
if (callback_owns_portmidi && !active) {
/* main is requesting (by setting active to false) that we shut down */
callback_owns_portmidi = false;
return;
}
if (!active) return; /* main still getting ready or it's closing down */
callback_owns_portmidi = true; /* main is ready, we have portmidi */
if (send_start_stop) {
if (clock_running) {
Pm_WriteShort(midi, 0, MIDI_STOP);
} else {
Pm_WriteShort(midi, 0, MIDI_START);
clock_start_time = timestamp;
next_clock_time = TEMPO_TO_CLOCK / tempo;
}
clock_running = !clock_running;
send_start_stop = false; /* until main sets it again */
/* note that there's a slight race condition here: main could
set send_start_stop asynchronously, but we assume user is
typing slower than the clock rate */
}
if (clock_running) {
if ((timestamp - clock_start_time) > next_clock_time) {
Pm_WriteShort(midi, 0, MIDI_TIME_CLOCK);
next_clock_time += TEMPO_TO_CLOCK / tempo;
}
}
if (time_code_running) {
int data = 0; // initialization avoids compiler warning
if ((timestamp - smpte_start_time) < next_smpte_time)
return;
switch (mtc_count) {
case 0: /* frames low nibble */
data = frames;
break;
case 1: /* frames high nibble */
data = frames >> 4;
break;
case 2: /* frames seconds low nibble */
data = seconds;
break;
case 3: /* frames seconds high nibble */
data = seconds >> 4;
break;
case 4: /* frames minutes low nibble */
data = minutes;
break;
case 5: /* frames minutes high nibble */
data = minutes >> 4;
break;
case 6: /* hours low nibble */
data = hours;
break;
case 7: /* hours high nibble */
data = hours >> 4;
break;
}
data &= 0xF; /* take only 4 bits */
Pm_WriteShort(midi, 0,
Pm_Message(MIDI_Q_FRAME, (mtc_count << 4) + data, 0));
mtc_count = (mtc_count + 1) & 7; /* wrap around */
if (mtc_count == 0) { /* update time by two frames */
frames += 2;
if (frames >= 30) {
frames = 0;
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
/* just let hours wrap if it gets that far */
}
}
}
}
next_smpte_time += QUARTER_FRAME_PERIOD;
} else { /* time_code_running is false */
smpte_start_time = timestamp;
/* so that when it finally starts, we'll be in sync */
}
}
/* 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;
}
/****************************************************************************
* showhelp
* Effect: print help text
****************************************************************************/
private void showhelp()
{
printf("\n");
printf("t toggles sending MIDI Time Code (MTC)\n");
printf("c toggles sending MIDI CLOCK (initially on)\n");
printf("m to set tempo (from 1bpm to 300bpm)\n");
printf("q quits\n");
printf("\n");
}
/****************************************************************************
* doascii
* Inputs:
* char c: input character
* Effect: interpret to control output
****************************************************************************/
private void doascii(char c)
{
if (isupper(c)) c = tolower(c);
if (c == 'q') done = true;
else if (c == 'c') {
printf("%s MIDI CLOCKs\n", (clock_running ? "Stopping" : "Starting"));
send_start_stop = true;
} else if (c == 't') {
printf("%s MIDI Time Code\n",
(time_code_running ? "Stopping" : "Starting"));
time_code_running = !time_code_running;
} else if (c == 'm') {
int input_tempo = get_number("Enter new tempo (bpm): ");
if (input_tempo >= 1 && input_tempo <= 300) {
printf("Changing tempo to %d\n", input_tempo);
tempo = (float) input_tempo;
} else {
printf("Tempo range is 1 to 300, current tempo is %g bpm\n",
tempo);
}
} else {
showhelp();
}
}
/* main - prompt for parameters, start processing */
/*
* Prompt user to type return.
* Then send START and MIDI CLOCK for 60 beats/min.
* Commands:
* t - toggle sending MIDI Time Code (MTC)
* c - toggle sending MIDI CLOCK
* m - set tempo
* q - quit
*/
int main(int argc, char **argv)
{
char s[STRING_MAX]; /* console input */
int outp;
PmError err;
int i;
if (argc > 1) {
printf("Warning: command line arguments ignored\n");
}
showhelp();
/* use porttime callback to send midi */
Pt_Start(1, timer_poll, 0);
/* list device information */
printf("MIDI output devices:\n");
for (i = 0; i < Pm_CountDevices(); i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
if (info->output) printf("%d: %s, %s\n", i, info->interf, info->name);
}
outp = get_number("Type output device number: ");
err = Pm_OpenOutput(&midi, outp, DRIVER_INFO, OUTPUT_BUFFER_SIZE,
TIME_PROC, TIME_INFO, LATENCY);
if (err) {
printf(Pm_GetErrorText(err));
goto error_exit_no_device;
}
active = true;
printf("Type RETURN to start MIDI CLOCK:\n");
if (!fgets(s, STRING_MAX, stdin)) goto error_exit;
send_start_stop = true; /* send START and then CLOCKs */
while (!done) {
if (fgets(s, STRING_MAX, stdin)) {
doascii(s[0]);
}
}
error_exit:
active = false;
Pt_Sleep(2); /* this is to allow callback to complete -- it's
real time, so it's either ok and it runs on
time, or there's no point to synchronizing
with it */
/* now we "own" portmidi again */
Pm_Close(midi);
error_exit_no_device:
Pt_Stop();
Pm_Terminate();
exit(0);
}
portmidi/pm_test/CMakeLists.txt 0000644 0000000 0000000 00000001134 11445664134 015625 0 ustar root root # pm_test
# set the build directory to be in portmidi, not in portmidi/pm_test
# this is required for Xcode:
if(APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
endif(APPLE)
if(WIN32)
include(../pm_win/static.cmake)
endif(WIN32)
macro(make_a_test name)
add_executable(${name} ${name}.c)
target_link_libraries(${name} portmidi-static ${PM_NEEDED_LIBS})
add_dependencies(${name} portmidi-static)
endmacro(make_a_test)
make_a_test(test)
make_a_test(midithread)
make_a_test(midithru)
make_a_test(sysex)
make_a_test(latency)
make_a_test(mm)
make_a_test(midiclock)
make_a_test(qtest)
portmidi/pm_test/midithru.c 0000755 0000000 0000000 00000033021 11252403336 015050 0 ustar root root /* 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 */
PmTimestamp 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) {
int status;
PmError rslt = Pm_Read(midi_in, &buffer, 1);
if (rslt == pmBufferOverflow)
continue;
assert(rslt == 1);
/* 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 */
int status = Pm_MessageStatus(next->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 */
}
portmidi/pm_test/latency.vcproj 0000755 0000000 0000000 00000011307 11274243554 015756 0 ustar root root
portmidi/ZERO_CHECK.vcproj 0000644 0000000 0000000 00000006730 11274243554 014321 0 ustar root root
portmidi/pm_java/ 0000755 0000000 0000000 00000000000 11453601074 013021 5 ustar root root portmidi/pm_java/pmdefaults-setup-script.iss 0000644 0000000 0000000 00000004160 11453600330 020340 0 ustar root root ; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{5094958B-3CD7-4780-A883-69C9E5B95AEF}
AppName=PmDefaults
AppVerName=PmDefaults
AppPublisher=Roger Dannenberg - Carnegie Mellon University
AppPublisherURL=http://portmedia.sourceforge.net/
AppSupportURL=http://portmedia.sourceforge.net/
AppUpdatesURL=http://portmedia.sourceforge.net/
DefaultDirName={pf}\PmDefaults
DefaultGroupName=PmDefaults
LicenseFile=C:\Users\rbd\portmedia\portmidi\pm_java\win32\license.txt
OutputBaseFilename=setup
SetupIconFile=C:\Users\rbd\portmedia\portmidi\pm_java\pmdefaults\pmdefaults.ico
Compression=lzma
SolidCompression=yes
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "C:\Users\rbd\portmedia\portmidi\pm_java\win32\pmdefaults.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\rbd\portmedia\portmidi\pm_java\win32\pmdefaults.jar"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\rbd\portmedia\portmidi\pm_java\win32\pmjni.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "C:\Users\rbd\portmedia\portmidi\pm_java\win32\license.txt"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{group}\PmDefaults"; Filename: "{app}\pmdefaults.exe"
Name: "{commondesktop}\PmDefaults"; Filename: "{app}\pmdefaults.exe"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\PmDefaults"; Filename: "{app}\pmdefaults.exe"; Tasks: quicklaunchicon
[Run]
Filename: "{app}\pmdefaults.exe"; Description: "{cm:LaunchProgram,PmDefaults}"; Flags: nowait postinstall skipifsilent
portmidi/pm_java/UpdateRsrcJavaExe.exe 0000644 0000000 0000000 00000310000 11127373026 017037 0 ustar root root MZ @ !L!This program cannot be run in DOS mode.
$ q