mame/3rdparty/portmidi/pm_test/fastrcv.c
2025-04-20 08:16:03 +10:00

256 lines
7.5 KiB
C

/* fastrcv.c -- send many MIDI messages very fast.
*
* This is a stress test created to explore reports of
* pm_write() call blocking (forever) on Linux when
* sending very dense MIDI sequences.
*
* Modified 8 Aug 2017 with -n to send expired timestamps
* to test a theory about why Linux ALSA hangs in Audacity.
*
* Roger B. Dannenberg, Aug 2017
*/
#include "portmidi.h"
#include "porttime.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "assert.h"
#define INPUT_BUFFER_SIZE 1000 /* big to avoid losing any input */
#define DEVICE_INFO NULL
#define DRIVER_INFO NULL
#define TIME_PROC ((PmTimeProcPtr) 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 */
// need to get declaration for Sleep()
#ifdef WIN32
#include "windows.h"
#else
#include <unistd.h>
#define Sleep(n) usleep(n * 1000)
#endif
int deviceno = -9999;
int verbose = FALSE;
static void prompt_and_exit(void)
{
printf("type ENTER...");
while (getchar() != '\n') ;
/* this will clean up open ports: */
exit(-1);
}
static PmError checkerror(PmError err)
{
if (err == pmHostError) {
/* it seems pointless to allocate memory and copy the string,
* so I will do the work of Pm_GetHostErrorText directly
*/
char errmsg[80];
Pm_GetHostErrorText(errmsg, 80);
printf("PortMidi found host error...\n %s\n", errmsg);
prompt_and_exit();
} else if (err < 0) {
printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err));
prompt_and_exit();
}
return err;
}
/* read a number from console */
/**/
int get_number(const char *prompt)
{
int n = 0, i;
fputs(prompt, stdout);
while (n != 1) {
n = scanf("%d", &i);
while (getchar() != '\n') ;
}
return i;
}
void fastrcv_test()
{
PmStream * midi;
PmError status, length;
PmEvent buffer[1];
PmTimestamp start;
/* every 10ms read all messages, keep counts */
/* every 1000ms, print report */
int msgcnt = 0;
/* expect repeating sequence of 60 through 71, alternating on/off */
int expected_pitch = 60;
int expected_on = TRUE;
int report_time;
PmTimestamp last_timestamp = -1;
PmTimestamp last_delta = -1;
/* It is recommended to start timer before PortMidi */
TIME_START;
/* open output device */
if (deviceno == Pm_CountDevices()) {
int id = Pm_CreateVirtualInput("fastrcv", NULL, DEVICE_INFO);
if (id < 0) checkerror(id); /* error reporting */
checkerror(Pm_OpenInput(&midi, id, DRIVER_INFO,
INPUT_BUFFER_SIZE, TIME_PROC, TIME_INFO));
} else {
Pm_OpenInput(&midi, deviceno, DRIVER_INFO, INPUT_BUFFER_SIZE,
TIME_PROC, TIME_INFO);
}
printf("Midi Input opened.\n");
/* wait a sec after printing previous line */
start = Pt_Time() + 1000;
while (start > Pt_Time()) {
Sleep(10);
}
report_time = Pt_Time() + 1000; /* report every 1s */
while (TRUE) {
PmTimestamp now = Pt_Time();
status = Pm_Poll(midi);
if (status == TRUE) {
length = Pm_Read(midi, buffer, 1);
if (length > 0) {
int status = Pm_MessageStatus(buffer[0].message);
if (status == 0x80) { /* convert NoteOff to NoteOn, vel=0 */
status = 0x90;
buffer[0].message = Pm_Message(status,
Pm_MessageData1(buffer[0].message), 0);
}
/* only listen to NOTEON messages */
if (status == 0x90) {
int pitch = Pm_MessageData1(buffer[0].message);
int vel = Pm_MessageData2(buffer[0].message);
int is_on = (vel > 0);
if (verbose) {
printf("Note pitch %d vel %d\n", pitch, vel);
}
msgcnt++;
if (pitch != expected_pitch || expected_on != is_on) {
printf("Unexpected note-on: pitch %d vel %d, "
"expected: pitch %d Note%s\n", pitch, vel,
expected_pitch, (expected_on ? "On" : "Off"));
}
if (is_on) {
expected_on = FALSE;
expected_pitch = pitch;
} else {
expected_on = TRUE;
expected_pitch = (pitch + 1) % 72;
if (expected_pitch < 60) expected_pitch = 60;
}
if (last_timestamp >= 0) {
last_delta = buffer[0].timestamp - last_timestamp;
}
last_timestamp = buffer[0].timestamp;
}
}
}
if (now >= report_time) {
printf("%d msgs/sec", msgcnt);
/* if available, print the last timestamp and last delta time */
if (last_timestamp >= 0) {
printf(" last timestamp %d", (int) last_timestamp);
last_timestamp = -1;
}
if (last_delta >= 0) {
printf(" last delta time %d", (int) last_delta);
last_delta = -1;
}
printf("\n");
report_time += 1000;
msgcnt = 0;
}
}
}
void show_usage()
{
printf("Usage: fastrcv [-h] [-v] [-d device], where\n"
"device is the PortMidi device number,\n"
"-h means help,\n"
"-v means verbose (print messages)\n");
}
int main(int argc, char *argv[])
{
int default_in;
int default_out;
char *deflt;
int i = 0, n = 0;
int test_input = 0, test_output = 0, test_both = 0, somethingStupid = 0;
int stream_test = 0;
int device_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");
if (argc <= 1) {
show_usage();
} else {
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0) {
show_usage();
} else if (strcmp(argv[i], "-v") == 0) {
verbose = TRUE;
} else if (strcmp(argv[i], "-d") == 0) {
i = i + 1;
deviceno = atoi(argv[i]);
printf("Device will be %d\n", deviceno);
} else {
show_usage();
}
}
}
/* list device information */
default_in = Pm_GetDefaultInputDeviceID();
default_out = Pm_GetDefaultOutputDeviceID();
for (i = 0; i < Pm_CountDevices(); i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
if (!info->output) {
printf("%d: %s, %s", i, info->interf, info->name);
if (i == deviceno) {
device_valid = TRUE;
deflt = "selected ";
} else if (i == default_out) {
deflt = "default ";
} else {
deflt = "";
}
printf(" (%sinput)\n", deflt);
}
}
printf("%d: Create virtual port named \"fastrcv\"", i);
if (i == deviceno) {
device_valid = TRUE;
deflt = "selected ";
} else {
deflt = "";
}
printf(" (%sinput)\n", deflt);
if (!device_valid) {
deviceno = get_number("Input device number: ");
}
fastrcv_test();
return 0;
}