WinMME: Use waitable timers, which have a resolution of 100 ns. Use timeBeginPeriod() and timeEndPeriod() for greater multimedia timer resolution.
This commit is contained in:
parent
323640c233
commit
7dc3a0fc8c
|
@ -119,6 +119,13 @@ JackWinMMEDriver::Close()
|
|||
delete[] output_ports;
|
||||
output_ports = 0;
|
||||
}
|
||||
if (period != 1000) {
|
||||
if (timeEndPeriod(period) != TIMERR_NOERROR) {
|
||||
jack_error("JackWinMMEDriver::Close - failed to unset timer "
|
||||
"resolution.");
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
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_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) {
|
||||
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 +215,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 +230,13 @@ JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels,
|
|||
delete[] input_ports;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Jack {
|
|||
|
||||
JackWinMMEInputPort **input_ports;
|
||||
JackWinMMEOutputPort **output_ports;
|
||||
UINT period;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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…
Reference in New Issue