Browse Source

WinMME: Use waitable timers, which have a resolution of 100 ns. Use timeBeginPeriod() and timeEndPeriod() for greater multimedia timer resolution.

tags/1.9.8
Devin Anderson 14 years ago
parent
commit
7dc3a0fc8c
4 changed files with 84 additions and 43 deletions
  1. +34
    -1
      windows/winmme/JackWinMMEDriver.cpp
  2. +1
    -0
      windows/winmme/JackWinMMEDriver.h
  3. +41
    -35
      windows/winmme/JackWinMMEOutputPort.cpp
  4. +8
    -7
      windows/winmme/JackWinMMEOutputPort.h

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

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


@@ -139,13 +146,25 @@ 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_inputs %d", num_potential_inputs);
jack_info("JackWinMMEDriver::Open - num_potential_outputs %d", num_potential_outputs); jack_info("JackWinMMEDriver::Open - num_potential_outputs %d", num_potential_outputs);


// Get the best minimum timer resolution possible.
for (period = 1; i < 1000; i++) {
if (timeBeginPeriod(period) == TIMERR_NOERROR) {
jack_info("JackWinMMEDriver::Open - timer resolution set to %d "
"milliseconds.", period);
goto open_inputs;
}
}
jack_error("JackWinMMEDriver::Open - could not set any timer resolution. "
"Continuing anyway ...");

open_inputs:
if (num_potential_inputs) { if (num_potential_inputs) {
try { try {
input_ports = new JackWinMMEInputPort *[num_potential_inputs]; input_ports = new JackWinMMEInputPort *[num_potential_inputs];
} catch (std::exception e) { } catch (std::exception e) {
jack_error("JackWinMMEDriver::Open - while creating input port " jack_error("JackWinMMEDriver::Open - while creating input port "
"array: %s", e.what()); "array: %s", e.what());
return -1;
goto unset_timer_resolution;
} }
for (int i = 0; i < num_potential_inputs; i++) { for (int i = 0; i < num_potential_inputs; i++) {
try { try {
@@ -196,6 +215,13 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels,
return 0; 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: destroy_input_ports:
if (input_ports) { if (input_ports) {
for (int i = 0; i < input_count; i++) { for (int i = 0; i < input_count; i++) {
@@ -204,6 +230,13 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels,
delete[] input_ports; delete[] input_ports;
input_ports = 0; input_ports = 0;
} }
unset_timer_resolution:
if (period != 1000) {
if (timeEndPeriod(period) != TIMERR_NOERROR) {
jack_error("JackWinMMEDriver::Open - failed to unset timer "
"resolution.");
}
}
return -1; return -1;
} }




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

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


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


public: public:




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

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

jack_midi_event_t *event = thread_queue->DequeueEvent(); jack_midi_event_t *event = thread_queue->DequeueEvent();
if (! event) { if (! event) {
break; break;
} }
jack_time_t frame_time = GetTimeFromFrames(event->time); 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; break;
} }


if (sleep_time < 1000) {
sleep_time = 1000;
if (! Wait(timer)) {
break;
} }
JackSleep(sleep_time);
} }
jack_midi_data_t *data = event->buffer; jack_midi_data_t *data = event->buffer;
DWORD message = 0; DWORD message = 0;
@@ -216,6 +210,15 @@ JackWinMMEOutputPort::Execute()
return false; 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 void
JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1,
DWORD_PTR param2) DWORD_PTR param2)
@@ -289,15 +292,23 @@ JackWinMMEOutputPort::Signal(HANDLE semaphore)
bool bool
JackWinMMEOutputPort::Start() 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 bool
@@ -326,6 +337,10 @@ JackWinMMEOutputPort::Stop()
jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing " jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing "
"thread.", verb); "thread.", verb);
} }
if (! CloseHandle(timer)) {
WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle");
result = -1;
}
return ! result; return ! result;
} }


@@ -346,15 +361,6 @@ JackWinMMEOutputPort::Wait(HANDLE semaphore)
return false; 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 void
JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func, JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func,
MMRESULT result) MMRESULT result)


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

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


void
GetOutErrorString(MMRESULT error, LPTSTR text);

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


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


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

HMIDIOUT handle; HMIDIOUT handle;
JackMidiBufferReadQueue *read_queue; JackMidiBufferReadQueue *read_queue;
HANDLE sysex_semaphore; HANDLE sysex_semaphore;
JackThread *thread; JackThread *thread;
JackMidiAsyncQueue *thread_queue; JackMidiAsyncQueue *thread_queue;
HANDLE thread_queue_semaphore; 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: public:




Loading…
Cancel
Save