Browse Source

Optimize timing of 'alsarawmiidi' driver. Set parameters on 'rawmidi' ports. Minor fixes to 'jack_midi_latency_test'. Add latency plot to 'jack_midi_latency_test'.

tags/1.9.8
Devin Anderson 14 years ago
parent
commit
ccbf581d19
4 changed files with 57 additions and 42 deletions
  1. +34
    -7
      example-clients/midi_latency_test.c
  2. +16
    -34
      linux/alsarawmidi/JackALSARawMidiDriver.cpp
  3. +1
    -1
      linux/alsarawmidi/JackALSARawMidiDriver.h
  4. +6
    -0
      linux/alsarawmidi/JackALSARawMidiPort.cpp

+ 34
- 7
example-clients/midi_latency_test.c View File

@@ -44,10 +44,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* suite. * suite.
* *
* To port this program to non-POSIX platforms, you'll have to include * To port this program to non-POSIX platforms, you'll have to include
* implementations for mutexes, semaphores, and command-line argument handling.
* implementations for semaphores and command-line argument handling.
*/ */


#include <errno.h> #include <errno.h>
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -72,10 +73,12 @@ typedef sem_t *semaphore_t;
#endif #endif


const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer"; const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer";
const char *ERROR_SHUTDOWN = "the JACK server has been shutdown";
const char *ERROR_TIMEOUT = "timed out while waiting for MIDI message"; const char *ERROR_TIMEOUT = "timed out while waiting for MIDI message";


const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve"; const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve";
const char *SOURCE_PROCESS = "handle_process"; const char *SOURCE_PROCESS = "handle_process";
const char *SOURCE_SHUTDOWN = "handle_shutdown";
const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore"; const char *SOURCE_SIGNAL_SEMAPHORE = "signal_semaphore";
const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore"; const char *SOURCE_WAIT_SEMAPHORE = "wait_semaphore";


@@ -180,7 +183,7 @@ destroy_semaphore(semaphore_t semaphore, int id)
} }


static void static void
die(char *source, char *error_message)
die(const char *source, const char *error_message)
{ {
output_error(source, error_message); output_error(source, error_message);
output_usage(); output_usage();
@@ -305,7 +308,6 @@ handle_process(jack_nframes_t frames, void *arg)
frame = frames - 1; frame = frames - 1;
} }
port_buffer = jack_port_get_buffer(out_port, frames); port_buffer = jack_port_get_buffer(out_port, frames);
jack_midi_clear_buffer(port_buffer);
buffer = jack_midi_event_reserve(port_buffer, frame, message_size); buffer = jack_midi_event_reserve(port_buffer, frame, message_size);
if (buffer == NULL) { if (buffer == NULL) {
set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE); set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE);
@@ -331,7 +333,7 @@ handle_process(jack_nframes_t frames, void *arg)
static void static void
handle_shutdown(void *arg) handle_shutdown(void *arg)
{ {
set_process_error("handle_shutdown", "The JACK server has been shutdown");
set_process_error(SOURCE_SHUTDOWN, ERROR_SHUTDOWN);
} }


static int static int
@@ -446,6 +448,7 @@ int
main(int argc, char **argv) main(int argc, char **argv)
{ {
size_t jitter_plot[101]; size_t jitter_plot[101];
size_t latency_plot[101];
int long_index = 0; int long_index = 0;
struct option long_options[] = { struct option long_options[] = {
{"help", 0, NULL, 'h'}, {"help", 0, NULL, 'h'},
@@ -639,15 +642,26 @@ main(int argc, char **argv)
double average_latency = ((double) total_latency) / samples; double average_latency = ((double) total_latency) / samples;
double average_latency_time = total_latency_time / samples; double average_latency_time = total_latency_time / samples;
size_t i; size_t i;
double latency_plot_offset =
floor(((double) lowest_latency_time) / 100.0) / 10.0;
double sample_rate = (double) jack_get_sample_rate(client); double sample_rate = (double) jack_get_sample_rate(client);
jack_nframes_t total_jitter = 0; jack_nframes_t total_jitter = 0;
jack_time_t total_jitter_time = 0; jack_time_t total_jitter_time = 0;
for (i = 0; i <= 100; i++) { for (i = 0; i <= 100; i++) {
jitter_plot[i] = 0; jitter_plot[i] = 0;
latency_plot[i] = 0;
} }
for (i = 0; i < samples; i++) { for (i = 0; i < samples; i++) {
double latency_time_value = (double) latency_time_values[i];
double relational_latency_time =
latency_time_value - lowest_latency_time;
double jitter_time = ABS(average_latency_time - double jitter_time = ABS(average_latency_time -
((double) latency_time_values[i]));
latency_time_value);
if (relational_latency_time >= 10000.0) {
(latency_plot[100])++;
} else {
(latency_plot[(int) (relational_latency_time / 100.0)])++;
}
if (jitter_time >= 10000.0) { if (jitter_time >= 10000.0) {
(jitter_plot[100])++; (jitter_plot[100])++;
} else { } else {
@@ -660,8 +674,8 @@ main(int argc, char **argv)
printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n" printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n"
"Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n" "Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n"
"Average latency: %.2f ms (%.2f frames)\n" "Average latency: %.2f ms (%.2f frames)\n"
"Best latency: %.2f ms (%u frames)\n"
"Worst latency: %.2f ms (%u frames)\n"
"Lowest latency: %.2f ms (%u frames)\n"
"Highest latency: %.2f ms (%u frames)\n"
"Peak MIDI jitter: %.2f ms (%u frames)\n" "Peak MIDI jitter: %.2f ms (%u frames)\n"
"Average MIDI jitter: %.2f ms (%.2f frames)\n", "Average MIDI jitter: %.2f ms (%.2f frames)\n",
(out_latency_range.min / sample_rate) * 1000.0, (out_latency_range.min / sample_rate) * 1000.0,
@@ -687,6 +701,19 @@ main(int argc, char **argv)
if (jitter_plot[100]) { if (jitter_plot[100]) {
printf(" > 10 ms: %u\n", jitter_plot[100]); printf(" > 10 ms: %u\n", jitter_plot[100]);
} }
printf("\nLatency Plot:\n");
for (i = 0; i < 100; i++) {
if (latency_plot[i]) {
printf("%.1f - %.1f ms: %u\n",
latency_plot_offset + (((float) i) / 10.0),
latency_plot_offset + (((float) (i + 1)) / 10.0),
latency_plot[i]);
}
}
if (latency_plot[100]) {
printf(" > %.1f ms: %u\n", latency_plot_offset + 10.0,
latency_plot[100]);
}
} }
printf("\nMessages sent: %d\n" printf("\nMessages sent: %d\n"
"Messages received: %d\n", "Messages received: %d\n",


+ 16
- 34
linux/alsarawmidi/JackALSARawMidiDriver.cpp View File

@@ -133,18 +133,14 @@ JackALSARawMidiDriver::Execute()
jack_nframes_t timeout_frame = 0; jack_nframes_t timeout_frame = 0;
for (;;) { for (;;) {
jack_nframes_t process_frame; jack_nframes_t process_frame;
jack_time_t wait_time;
jack_time_t *wait_time_ptr;
unsigned short revents; unsigned short revents;
jack_nframes_t *timeout_frame_ptr;
if (! timeout_frame) { if (! timeout_frame) {
wait_time_ptr = 0;
timeout_frame_ptr = 0;
} else { } else {
jack_time_t next_time = GetTimeFromFrames(timeout_frame);
jack_time_t now = GetMicroSeconds();
wait_time = next_time <= now ? 0 : next_time - now;
wait_time_ptr = &wait_time;
timeout_frame_ptr = &timeout_frame;
} }
if (Poll(wait_time_ptr) == -1) {
if (Poll(timeout_frame_ptr) == -1) {
if (errno == EINTR) { if (errno == EINTR) {
continue; continue;
} }
@@ -392,43 +388,29 @@ JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels,
return -1; return -1;
} }


#ifdef HAVE_PPOLL

int int
JackALSARawMidiDriver::Poll(const jack_time_t *wait_time)
JackALSARawMidiDriver::Poll(const jack_nframes_t *wakeup_frame)
{ {
struct timespec timeout; struct timespec timeout;
struct timespec *timeout_ptr; struct timespec *timeout_ptr;
if (! wait_time) {
if (! wakeup_frame) {
timeout_ptr = 0; timeout_ptr = 0;
} else { } else {
timeout.tv_sec = (*wait_time) / 1000000;
timeout.tv_nsec = ((*wait_time) % 1000000) * 1000;
timeout_ptr = &timeout; timeout_ptr = &timeout;
}
return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);
}

#else

int
JackALSARawMidiDriver::Poll(const jack_time_t *wait_time)
{
int result = poll(poll_fds, poll_fd_count,
! wait_time ? -1 : (int) ((*wait_time) / 1000));
if ((! result) && wait_time) {
jack_time_t time_left = (*wait_time) % 1000;
if (time_left) {
// Cheap hack.
usleep(time_left);
result = poll(poll_fds, poll_fd_count, 0);
jack_time_t next_time = GetTimeFromFrames(*wakeup_frame);
jack_time_t now = GetMicroSeconds();
if (next_time <= now) {
timeout.tv_sec = 0;
timeout.tv_nsec = 0;
} else {
jack_time_t wait_time = next_time - now;
timeout.tv_sec = wait_time / 1000000;
timeout.tv_nsec = (wait_time % 1000000) * 1000;
} }
} }
return result;
return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);
} }


#endif

int int
JackALSARawMidiDriver::Read() JackALSARawMidiDriver::Read()
{ {


+ 1
- 1
linux/alsarawmidi/JackALSARawMidiDriver.h View File

@@ -53,7 +53,7 @@ namespace Jack {
int code); int code);


int int
Poll(const jack_time_t *wait_time);
Poll(const jack_nframes_t *wakeup_frame);


public: public:




+ 6
- 0
linux/alsarawmidi/JackALSARawMidiPort.cpp View File

@@ -82,6 +82,12 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
func = "snd_rawmidi_params_set_no_active_sensing"; func = "snd_rawmidi_params_set_no_active_sensing";
goto free_params; goto free_params;
} }
code = snd_rawmidi_params(rawmidi, params);
if (code) {
error_message = snd_strerror(code);
func = "snd_rawmidi_params";
goto free_params;
}
snd_rawmidi_params_free(params); snd_rawmidi_params_free(params);
num_fds = snd_rawmidi_poll_descriptors_count(rawmidi); num_fds = snd_rawmidi_poll_descriptors_count(rawmidi);
if (! num_fds) { if (! num_fds) {


Loading…
Cancel
Save