Add functions for get/set the Multimedia timer resolution, although we are really only interested in the minimum, this will facilitate testing Put timer utility functions inside nested namespaces as they are platform specific
298 lines
6.5 KiB
C++
298 lines
6.5 KiB
C++
/*
|
|
* Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com>
|
|
*
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include "pbd/error.h"
|
|
#include "pbd/compose.h"
|
|
|
|
#include "winmmemidi_io.h"
|
|
#include "win_utils.h"
|
|
#include "debug.h"
|
|
|
|
#include "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 ×tamp,
|
|
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");
|
|
|
|
PBD::MMTIMERS::set_min_resolution();
|
|
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);
|
|
|
|
PBD::MMTIMERS::reset_resolution();
|
|
}
|
|
|
|
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::create_input_devices ()
|
|
{
|
|
int srcCount = midiInGetNumDevs ();
|
|
|
|
DEBUG_MIDI (string_compose ("MidiIn count: %1\n", srcCount));
|
|
|
|
for (int i = 0; i < srcCount; ++i) {
|
|
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) {
|
|
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);
|
|
}
|