Browse Source

Merge branch 'newer-midi'

git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@4334 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/1.9.8
sletz 14 years ago
parent
commit
04f7bf87fd
5 changed files with 259 additions and 91 deletions
  1. +167
    -48
      example-clients/midi_latency_test.c
  2. +42
    -1
      windows/winmme/JackWinMMEDriver.cpp
  3. +1
    -0
      windows/winmme/JackWinMMEDriver.h
  4. +41
    -35
      windows/winmme/JackWinMMEOutputPort.cpp
  5. +8
    -7
      windows/winmme/JackWinMMEOutputPort.h

+ 167
- 48
example-clients/midi_latency_test.c View File

@@ -85,6 +85,8 @@ const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore";
const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore";

jack_client_t *client;
semaphore_t connect_semaphore;
volatile int connections_established;
const char *error_message;
const char *error_source;
jack_nframes_t highest_latency;
@@ -111,6 +113,8 @@ char *program_name;
jack_port_t *remote_in_port;
jack_port_t *remote_out_port;
size_t samples;
const char *target_in_port_name;
const char *target_out_port_name;
int timeout;
jack_nframes_t total_latency;
jack_time_t total_latency_time;
@@ -133,6 +137,11 @@ set_process_error(const char *source, const char *message);
static int
signal_semaphore(semaphore_t semaphore);

static jack_port_t *
update_connection(jack_port_t *remote_port, int connected,
jack_port_t *local_port, jack_port_t *current_port,
const char *target_name);

static int
wait_semaphore(semaphore_t semaphore, int block);

@@ -217,6 +226,53 @@ handle_info(const char *message)
/* Suppress info */
}

static void
handle_port_connection_change(jack_port_id_t port_id_1,
jack_port_id_t port_id_2, int connected,
void *arg)
{
jack_port_t *port_1;
jack_port_t *port_2;
if ((remote_in_port != NULL) && (remote_out_port != NULL)) {
return;
}
port_1 = jack_port_by_id(client, port_id_1);
port_2 = jack_port_by_id(client, port_id_2);

/* The 'update_connection' call is not RT-safe. It calls
'jack_port_get_connections' and 'jack_free'. This might be a problem
with JACK 1, as this callback runs in the process thread in JACK 1. */

if (port_1 == in_port) {
remote_in_port = update_connection(port_2, connected, in_port,
remote_in_port,
target_in_port_name);
} else if (port_2 == in_port) {
remote_in_port = update_connection(port_1, connected, in_port,
remote_in_port,
target_in_port_name);
} else if (port_1 == out_port) {
remote_out_port = update_connection(port_2, connected, out_port,
remote_out_port,
target_out_port_name);
} else if (port_2 == out_port) {
remote_out_port = update_connection(port_1, connected, out_port,
remote_out_port,
target_out_port_name);
}
if ((remote_in_port != NULL) && (remote_out_port != NULL)) {
connections_established = 1;
if (! signal_semaphore(connect_semaphore)) {
/* Sigh ... */
die("post_semaphore", get_semaphore_error());
}
if (! signal_semaphore(init_semaphore)) {
/* Sigh ... */
die("post_semaphore", get_semaphore_error());
}
}
}

static int
handle_process(jack_nframes_t frames, void *arg)
{
@@ -342,6 +398,10 @@ static void
handle_signal(int sig)
{
process_state = -2;
if (! signal_semaphore(connect_semaphore)) {
/* Sigh ... */
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
}
if (! signal_semaphore(process_semaphore)) {
/* Sigh ... */
die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error());
@@ -364,12 +424,14 @@ output_error(const char *source, const char *message)
static void
output_usage(void)
{
fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n"
fprintf(stderr, "Usage: %s [options] [out-port-name in-port-name]\n\n"
"\t-h, --help print program usage\n"
"\t-m, --message-size=size set size of MIDI messages to send\n"
"\t-s, --samples=n number of MIDI messages to send\n"
"\t-t, --timeout=seconds wait time before giving up on message\n"
"\n", program_name);
"\t-m, --message-size=size set size of MIDI messages to send "
"(default: 3)\n"
"\t-s, --samples=n number of MIDI messages to send "
"(default: 1024)\n"
"\t-t, --timeout=seconds message timeout (default: 5)\n\n",
program_name);
}

static unsigned long
@@ -445,6 +507,46 @@ signal_semaphore(semaphore_t semaphore)

}

static jack_port_t *
update_connection(jack_port_t *remote_port, int connected,
jack_port_t *local_port, jack_port_t *current_port,
const char *target_name)
{
if (connected) {
if (current_port) {
return current_port;
}
if (target_name) {
if (strcmp(target_name, jack_port_name(remote_port))) {
return NULL;
}
}
return remote_port;
}
if (! strcmp(jack_port_name(remote_port), jack_port_name(current_port))) {
const char **port_names;
if (target_name) {
return NULL;
}
port_names = jack_port_get_connections(local_port);
if (port_names == NULL) {
return NULL;
}

/* If a connected port is disconnected and other ports are still
connected, then we take the first port name in the array and use it
as our remote port. It's a dumb implementation. */
current_port = jack_port_by_name(client, port_names[0]);

jack_free(port_names);
if (current_port == NULL) {
/* Sigh */
die("jack_port_by_name", "failed to get port by name");
}
}
return current_port;
}

static int
wait_semaphore(semaphore_t semaphore, int block)
{
@@ -494,11 +596,15 @@ main(int argc, char **argv)
{"samples", 1, NULL, 's'},
{"timeout", 1, NULL, 't'}
};
size_t name_arg_count;
char *option_string = "hm:s:t:";
int show_usage = 0;
connections_established = 0;
error_message = NULL;
message_size = 3;
program_name = argv[0];
remote_in_port = 0;
remote_out_port = 0;
samples = 1024;
timeout = 5;

@@ -536,7 +642,17 @@ main(int argc, char **argv)
}
}
parse_port_names:
if ((argc - optind) != 2) {
name_arg_count = argc - optind;
switch (name_arg_count) {
case 2:
target_in_port_name = argv[optind + 1];
target_out_port_name = argv[optind];
break;
case 0:
target_in_port_name = 0;
target_out_port_name = 0;
break;
default:
output_usage();
return EXIT_FAILURE;
}
@@ -599,18 +715,6 @@ main(int argc, char **argv)
error_source = "jack_client_open";
goto free_message_2;
}
remote_in_port = jack_port_by_name(client, argv[optind + 1]);
if (remote_in_port == NULL) {
error_message = "invalid port name";
error_source = argv[optind + 1];
goto close_client;
}
remote_out_port = jack_port_by_name(client, argv[optind]);
if (remote_out_port == NULL) {
error_message = "invalid port name";
error_source = argv[optind];
goto close_client;
}
in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput, 0);
if (in_port == NULL) {
@@ -635,16 +739,29 @@ main(int argc, char **argv)
error_source = "jack_set_xrun_callback";
goto unregister_out_port;
}
if (jack_set_port_connect_callback(client, handle_port_connection_change,
NULL)) {
error_message = "failed to set port connection callback";
error_source = "jack_set_port_connect_callback";
goto unregister_out_port;
}
jack_on_shutdown(client, handle_shutdown, NULL);
jack_set_info_function(handle_info);
process_state = 0;
init_semaphore = create_semaphore(0);
if (init_semaphore == NULL) {

connect_semaphore = create_semaphore(0);
if (connect_semaphore == NULL) {
error_message = get_semaphore_error();
error_source = "create_semaphore";
goto unregister_out_port;
}
process_semaphore = create_semaphore(1);
init_semaphore = create_semaphore(1);
if (init_semaphore == NULL) {
error_message = get_semaphore_error();
error_source = "create_semaphore";
goto destroy_connect_semaphore;;
}
process_semaphore = create_semaphore(2);
if (process_semaphore == NULL) {
error_message = get_semaphore_error();
error_source = "create_semaphore";
@@ -655,33 +772,39 @@ main(int argc, char **argv)
error_source = "jack_activate";
goto destroy_process_semaphore;
}
if (jack_connect(client, jack_port_name(out_port),
jack_port_name(remote_out_port))) {
error_message = "could not connect MIDI out port";
error_source = "jack_connect";
goto deactivate_client;
}
if (jack_connect(client, jack_port_name(remote_in_port),
jack_port_name(in_port))) {
error_message = "could not connect MIDI in port";
error_source = "jack_connect";
goto deactivate_client;
}
if (! signal_semaphore(init_semaphore)) {
error_message = get_semaphore_error();
error_source = "post_semaphore";
goto deactivate_client;
if (name_arg_count) {
if (jack_connect(client, jack_port_name(out_port),
target_out_port_name)) {
error_message = "could not connect MIDI out port";
error_source = "jack_connect";
goto deactivate_client;
}
if (jack_connect(client, target_in_port_name,
jack_port_name(in_port))) {
error_message = "could not connect MIDI in port";
error_source = "jack_connect";
goto deactivate_client;
}
}
if (! register_signal_handler(handle_signal)) {
error_message = strerror(errno);
error_source = "register_signal_handler";
goto deactivate_client;
}
if (wait_semaphore(process_semaphore, 1) == -1) {
printf("Waiting for connections ...\n");
if (wait_semaphore(connect_semaphore, 1) == -1) {
error_message = get_semaphore_error();
error_source = "wait_semaphore";
goto deactivate_client;
}
if (connections_established) {
printf("Waiting for test completion ...\n\n");
if (wait_semaphore(process_semaphore, 1) == -1) {
error_message = get_semaphore_error();
error_source = "wait_semaphore";
goto deactivate_client;
}
}
if (! register_signal_handler(SIG_DFL)) {
error_message = strerror(errno);
error_source = "register_signal_handler";
@@ -766,24 +889,20 @@ main(int argc, char **argv)
}
deactivate_client:
jack_deactivate(client);

/* Output this information after deactivation to prevent two threads
from accessing data at the same time. */
if (process_state != 2) {
printf("\nMessages sent: %d\nMessages received: %d\n", messages_sent,
messages_received);
}
printf("\nMessages sent: %d\nMessages received: %d\n", messages_sent,
messages_received);
if (unexpected_messages) {
printf("Unexpected messages received: %d\n", unexpected_messages);
}
if (xrun_count) {
printf("Xruns: %d\n", xrun_count);
}

destroy_process_semaphore:
destroy_semaphore(process_semaphore, 1);
destroy_semaphore(process_semaphore, 2);
destroy_init_semaphore:
destroy_semaphore(init_semaphore, 0);
destroy_semaphore(init_semaphore, 1);
destroy_connect_semaphore:
destroy_semaphore(connect_semaphore, 0);
unregister_out_port:
jack_port_unregister(client, out_port);
unregister_in_port:


+ 42
- 1
windows/winmme/JackWinMMEDriver.cpp View File

@@ -32,6 +32,7 @@ JackWinMMEDriver::JackWinMMEDriver(const char *name, const char *alias,
fPlaybackChannels = 0;
input_ports = 0;
output_ports = 0;
period = 0;
}

JackWinMMEDriver::~JackWinMMEDriver()
@@ -119,6 +120,13 @@ JackWinMMEDriver::Close()
delete[] output_ports;
output_ports = 0;
}
if (period) {
if (timeEndPeriod(period) != TIMERR_NOERROR) {
jack_error("JackWinMMEDriver::Close - failed to unset timer "
"resolution.");
result = -1;
}
}
return result;
}

@@ -139,13 +147,32 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels,
jack_info("JackWinMMEDriver::Open - num_potential_inputs %d", num_potential_inputs);
jack_info("JackWinMMEDriver::Open - num_potential_outputs %d", num_potential_outputs);

period = 0;
TIMECAPS caps;
if (timeGetDevCaps(&caps, sizeof(TIMECAPS)) != TIMEERR_NOERROR) {
jack_error("JackWinMMEDriver::Open - could not get timer device "
"capabilities. Continuing anyway ...");
} else {
period = caps.wPeriodMin;
if (timeBeginPeriod(period) != TIMERR_NOERROR) {
jack_error("JackWinMMEDriver::Open - could not set minimum timer "
"resolution. Continuing anyway ...");
period = 0;
} else {

jack_info("JackWinMMEDriver::Open - multimedia timer resolution "
"set to %d milliseconds.", period);

}
}

if (num_potential_inputs) {
try {
input_ports = new JackWinMMEInputPort *[num_potential_inputs];
} catch (std::exception e) {
jack_error("JackWinMMEDriver::Open - while creating input port "
"array: %s", e.what());
return -1;
goto unset_timer_resolution;
}
for (int i = 0; i < num_potential_inputs; i++) {
try {
@@ -196,6 +223,13 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels,
return 0;
}

if (output_ports) {
for (int i = 0; i < output_count; i++) {
delete output_ports[i];
}
delete[] output_ports;
output_ports = 0;
}
destroy_input_ports:
if (input_ports) {
for (int i = 0; i < input_count; i++) {
@@ -204,6 +238,13 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels,
delete[] input_ports;
input_ports = 0;
}
unset_timer_resolution:
if (period) {
if (timeEndPeriod(period) != TIMERR_NOERROR) {
jack_error("JackWinMMEDriver::Open - failed to unset timer "
"resolution.");
}
}
return -1;
}



+ 1
- 0
windows/winmme/JackWinMMEDriver.h View File

@@ -33,6 +33,7 @@ namespace Jack {

JackWinMMEInputPort **input_ports;
JackWinMMEOutputPort **output_ports;
UINT period;

public:



+ 41
- 35
windows/winmme/JackWinMMEOutputPort.cpp View File

@@ -130,37 +130,31 @@ bool
JackWinMMEOutputPort::Execute()
{
for (;;) {
if (! Wait(thread_queue_semaphore)) {
if (! Wait(thread_queue_semaphore)) {
jack_log("JackWinMMEOutputPort::Execute BREAK");
break;
}

jack_midi_event_t *event = thread_queue->DequeueEvent();
if (! event) {
break;
}
jack_time_t frame_time = GetTimeFromFrames(event->time);
for (jack_time_t current_time = GetMicroSeconds();
frame_time > current_time; current_time = GetMicroSeconds()) {
jack_time_t sleep_time = frame_time - current_time;
jack_time_t current_time = GetMicroSeconds();
if (frame_time > current_time) {
LARGE_INTEGER due_time;

// Windows has a millisecond sleep resolution for its Sleep calls.
// This is unfortunate, as MIDI timing often requires a higher
// resolution. For now, we attempt to compensate by letting an
// event be sent if we're less than 500 microseconds from sending
// the event. We assume that it's better to let an event go out
// 499 microseconds early than let an event go out 501 microseconds
// late. Of course, that's assuming optimal sleep times, which is
// a whole different Windows issue ...
if (sleep_time < 500) {
// 100 ns resolution
due_time.QuadPart = - ((frame_time - current_time) * 10);
if (! SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0)) {
WriteOSError("JackWinMMEOutputPort::Execute",
"ChangeTimerQueueTimer");
break;
}

if (sleep_time < 1000) {
sleep_time = 1000;
if (! Wait(timer)) {
break;
}
JackSleep(sleep_time);
}
jack_midi_data_t *data = event->buffer;
DWORD message = 0;
@@ -216,6 +210,15 @@ JackWinMMEOutputPort::Execute()
return false;
}

void
JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text)
{
MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH);
if (result != MMSYSERR_NOERROR) {
snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error);
}
}

void
JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1,
DWORD_PTR param2)
@@ -289,15 +292,23 @@ JackWinMMEOutputPort::Signal(HANDLE semaphore)
bool
JackWinMMEOutputPort::Start()
{
bool result = thread->GetStatus() != JackThread::kIdle;
if (! result) {
result = ! thread->StartSync();
if (! result) {
jack_error("JackWinMMEOutputPort::Start - failed to start MIDI "
"processing thread.");
}
if (thread->GetStatus() != JackThread::kIdle) {
return true;
}
return result;
timer = CreateWaitableTimer(NULL, FALSE, NULL);
if (! timer) {
WriteOSError("JackWinMMEOutputPort::Start", "CreateWaitableTimer");
return false;
}
if (! thread->StartSync()) {
return true;
}
jack_error("JackWinMMEOutputPort::Start - failed to start MIDI processing "
"thread.");
if (! CloseHandle(timer)) {
WriteOSError("JackWinMMEOutputPort::Start", "CloseHandle");
}
return false;
}

bool
@@ -326,6 +337,10 @@ JackWinMMEOutputPort::Stop()
jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing "
"thread.", verb);
}
if (! CloseHandle(timer)) {
WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle");
result = -1;
}
return ! result;
}

@@ -346,15 +361,6 @@ JackWinMMEOutputPort::Wait(HANDLE semaphore)
return false;
}

void
JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text)
{
MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH);
if (result != MMSYSERR_NOERROR) {
snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error);
}
}

void
JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func,
MMRESULT result)


+ 8
- 7
windows/winmme/JackWinMMEOutputPort.h View File

@@ -36,6 +36,9 @@ namespace Jack {
HandleMessageEvent(HMIDIOUT handle, UINT message, DWORD_PTR port,
DWORD_PTR param1, DWORD_PTR param2);

void
GetOutErrorString(MMRESULT error, LPTSTR text);

void
HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2);

@@ -45,19 +48,17 @@ namespace Jack {
bool
Wait(HANDLE semaphore);

void
WriteOutError(const char *jack_func, const char *mm_func,
MMRESULT result);

HMIDIOUT handle;
JackMidiBufferReadQueue *read_queue;
HANDLE sysex_semaphore;
JackThread *thread;
JackMidiAsyncQueue *thread_queue;
HANDLE thread_queue_semaphore;

void
GetOutErrorString(MMRESULT error, LPTSTR text);

void
WriteOutError(const char *jack_func, const char *mm_func,
MMRESULT result);
HANDLE timer;

public:



Loading…
Cancel
Save