13
0
livetrax/libs/backends/portaudio/winmmemidi_io.cc
Robin Gareus c24b456211
Windows: unconditionally request high timer resolution
Previously timeBeginPeriod() was only called when MIDI
system was set to WinMME. It was also possible that
it was never unset in case starting the engine failed.

This significantly speeds up freewheel export which uses
Glib::usleep(100) when MIDI is disabled.

see also: https://randomascii.wordpress.com/2020/10/04/windows-timer-resolution-the-great-rule-change/
2023-06-05 00:02:54 +02:00

429 lines
9.7 KiB
C++

/*
* Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <windows.h>
#include <mmsystem.h>
#include <glibmm.h>
#include <sstream>
#include <set>
#include "pbd/error.h"
#include "pbd/compose.h"
#include "winmmemidi_io.h"
#include "debug.h"
#include "pbd/i18n.h"
using namespace ARDOUR;
WinMMEMidiIO::WinMMEMidiIO()
: m_active (false)
, m_enabled (true)
, m_run (false)
, m_changed_callback (0)
, m_changed_arg (0)
{
pthread_mutex_init (&m_device_lock, 0);
}
WinMMEMidiIO::~WinMMEMidiIO()
{
pthread_mutex_lock (&m_device_lock);
cleanup();
pthread_mutex_unlock (&m_device_lock);
pthread_mutex_destroy (&m_device_lock);
}
void
WinMMEMidiIO::cleanup()
{
DEBUG_MIDI ("MIDI cleanup\n");
m_active = false;
destroy_input_devices ();
destroy_output_devices ();
}
bool
WinMMEMidiIO::dequeue_input_event (uint32_t port,
uint64_t timestamp_start,
uint64_t timestamp_end,
uint64_t &timestamp,
uint8_t *d,
size_t &s)
{
if (!m_active) {
return false;
}
assert(port < m_inputs.size());
// m_inputs access should be protected by trylock
return m_inputs[port]->dequeue_midi_event (
timestamp_start, timestamp_end, timestamp, d, s);
}
bool
WinMMEMidiIO::enqueue_output_event (uint32_t port,
uint64_t timestamp,
const uint8_t *d,
const size_t s)
{
if (!m_active) {
return false;
}
assert(port < m_outputs.size());
// m_outputs access should be protected by trylock
return m_outputs[port]->enqueue_midi_event (timestamp, d, s);
}
std::string
WinMMEMidiIO::port_id (uint32_t port, bool input)
{
std::stringstream ss;
if (input) {
ss << "system:midi_capture_";
ss << port;
} else {
ss << "system:midi_playback_";
ss << port;
}
return ss.str();
}
std::string WinMMEMidiIO::port_name(uint32_t port, bool input)
{
if (input) {
if (port < m_inputs.size ()) {
return m_inputs[port]->name ();
}
} else {
if (port < m_outputs.size ()) {
return m_outputs[port]->name ();
}
}
return "";
}
void
WinMMEMidiIO::start ()
{
if (m_run) {
DEBUG_MIDI ("MIDI driver already started\n");
return;
}
m_run = true;
DEBUG_MIDI ("Starting MIDI driver\n");
discover();
start_devices ();
}
void
WinMMEMidiIO::stop ()
{
if (!m_run) {
DEBUG_MIDI ("MIDI driver already stopped\n");
return;
}
DEBUG_MIDI ("Stopping MIDI driver\n");
m_run = false;
stop_devices ();
pthread_mutex_lock (&m_device_lock);
cleanup ();
pthread_mutex_unlock (&m_device_lock);
}
void
WinMMEMidiIO::start_devices ()
{
for (std::vector<WinMMEMidiInputDevice*>::iterator i = m_inputs.begin ();
i < m_inputs.end();
++i) {
if (!(*i)->start ()) {
PBD::error << string_compose (_("Unable to start MIDI input device %1\n"),
(*i)->name ()) << endmsg;
}
}
for (std::vector<WinMMEMidiOutputDevice*>::iterator i = m_outputs.begin ();
i < m_outputs.end();
++i) {
if (!(*i)->start ()) {
PBD::error << string_compose (_ ("Unable to start MIDI output device %1\n"),
(*i)->name ()) << endmsg;
}
}
}
void
WinMMEMidiIO::stop_devices ()
{
for (std::vector<WinMMEMidiInputDevice*>::iterator i = m_inputs.begin ();
i < m_inputs.end();
++i) {
if (!(*i)->stop ()) {
PBD::error << string_compose (_ ("Unable to stop MIDI input device %1\n"),
(*i)->name ()) << endmsg;
}
}
for (std::vector<WinMMEMidiOutputDevice*>::iterator i = m_outputs.begin ();
i < m_outputs.end();
++i) {
if (!(*i)->stop ()) {
PBD::error << string_compose (_ ("Unable to stop MIDI output device %1\n"),
(*i)->name ()) << endmsg;
}
}
}
void
WinMMEMidiIO::clear_device_info ()
{
for (std::vector<MidiDeviceInfo*>::iterator i = m_device_info.begin();
i != m_device_info.end();
++i) {
delete *i;
}
m_device_info.clear();
}
bool
WinMMEMidiIO::get_input_name_from_index (int index, std::string& name)
{
MIDIINCAPS capabilities;
MMRESULT result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
if (result == MMSYSERR_NOERROR) {
DEBUG_MIDI(string_compose("Input Device: name : %1, mid : %2, pid : %3\n",
capabilities.szPname,
capabilities.wMid,
capabilities.wPid));
name = Glib::locale_to_utf8 (capabilities.szPname);
return true;
} else {
DEBUG_MIDI ("Unable to get WinMME input device capabilities\n");
}
return false;
}
bool
WinMMEMidiIO::get_output_name_from_index (int index, std::string& name)
{
MIDIOUTCAPS capabilities;
MMRESULT result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities));
if (result == MMSYSERR_NOERROR) {
DEBUG_MIDI(string_compose("Output Device: name : %1, mid : %2, pid : %3\n",
capabilities.szPname,
capabilities.wMid,
capabilities.wPid));
name = Glib::locale_to_utf8 (capabilities.szPname);
return true;
} else {
DEBUG_MIDI ("Unable to get WinMME output device capabilities\n");
}
return false;
}
void
WinMMEMidiIO::update_device_info ()
{
std::set<std::string> device_names;
int in_count = midiInGetNumDevs ();
for (int i = 0; i < in_count; ++i) {
std::string input_name;
if (get_input_name_from_index(i, input_name)) {
device_names.insert(input_name);
}
}
int out_count = midiOutGetNumDevs ();
for (int i = 0; i < out_count; ++i) {
std::string output_name;
if (get_output_name_from_index(i, output_name)) {
device_names.insert(output_name);
}
}
clear_device_info ();
for (std::set<std::string>::const_iterator i = device_names.begin();
i != device_names.end();
++i) {
m_device_info.push_back(new MidiDeviceInfo(*i));
}
}
MidiDeviceInfo*
WinMMEMidiIO::get_device_info (const std::string& name)
{
for (std::vector<MidiDeviceInfo*>::const_iterator i = m_device_info.begin();
i != m_device_info.end();
++i) {
if ((*i)->device_name == name) {
return *i;
}
}
return 0;
}
void
WinMMEMidiIO::create_input_devices ()
{
int srcCount = midiInGetNumDevs ();
DEBUG_MIDI (string_compose ("MidiIn count: %1\n", srcCount));
for (int i = 0; i < srcCount; ++i) {
std::string input_name;
if (!get_input_name_from_index (i, input_name)) {
DEBUG_MIDI ("Unable to get MIDI input name from index\n");
continue;
}
MidiDeviceInfo* info = get_device_info (input_name);
if (!info) {
DEBUG_MIDI ("Unable to MIDI device info from name\n");
continue;
}
if (!info->enable) {
DEBUG_MIDI(string_compose(
"MIDI input device %1 not enabled, not opening device\n", input_name));
continue;
}
try {
WinMMEMidiInputDevice* midi_input = new WinMMEMidiInputDevice (i);
if (midi_input) {
m_inputs.push_back (midi_input);
}
}
catch (...) {
DEBUG_MIDI ("Unable to create MIDI input\n");
continue;
}
}
}
void
WinMMEMidiIO::create_output_devices ()
{
int dstCount = midiOutGetNumDevs ();
DEBUG_MIDI (string_compose ("MidiOut count: %1\n", dstCount));
for (int i = 0; i < dstCount; ++i) {
std::string output_name;
if (!get_output_name_from_index (i, output_name)) {
DEBUG_MIDI ("Unable to get MIDI output name from index\n");
continue;
}
MidiDeviceInfo* info = get_device_info (output_name);
if (!info) {
DEBUG_MIDI ("Unable to MIDI device info from name\n");
continue;
}
if (!info->enable) {
DEBUG_MIDI(string_compose(
"MIDI output device %1 not enabled, not opening device\n", output_name));
continue;
}
try {
WinMMEMidiOutputDevice* midi_output = new WinMMEMidiOutputDevice(i);
if (midi_output) {
m_outputs.push_back(midi_output);
}
} catch(...) {
DEBUG_MIDI ("Unable to create MIDI output\n");
continue;
}
}
}
void
WinMMEMidiIO::destroy_input_devices ()
{
while (!m_inputs.empty ()) {
WinMMEMidiInputDevice* midi_input = m_inputs.back ();
// assert(midi_input->stopped ());
m_inputs.pop_back ();
delete midi_input;
}
}
void
WinMMEMidiIO::destroy_output_devices ()
{
while (!m_outputs.empty ()) {
WinMMEMidiOutputDevice* midi_output = m_outputs.back ();
// assert(midi_output->stopped ());
m_outputs.pop_back ();
delete midi_output;
}
}
void
WinMMEMidiIO::discover()
{
if (!m_run) {
return;
}
if (pthread_mutex_trylock (&m_device_lock)) {
return;
}
cleanup ();
create_input_devices ();
create_output_devices ();
if (!(m_inputs.size () || m_outputs.size ())) {
DEBUG_MIDI ("No midi inputs or outputs\n");
pthread_mutex_unlock (&m_device_lock);
return;
}
DEBUG_MIDI (string_compose ("Discovered %1 inputs and %2 outputs\n",
m_inputs.size (),
m_outputs.size ()));
if (m_changed_callback) {
m_changed_callback(m_changed_arg);
}
m_active = true;
pthread_mutex_unlock (&m_device_lock);
}