/* Copyright (C) 2013 Waves Audio Ltd. 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 #include "pbd/error.h" #include "pbd/debug.h" #include "pbd/compose.h" #include "pbd/stacktrace.h" #include "waves_midi_device.h" #include "waves_midi_event.h" // use non-zero latency because we want output to be timestapmed #define LATENCY 0 #define QUEUE_LENGTH 1024 using namespace ARDOUR; using namespace PBD; WavesMidiDevice::WavesMidiDevice (const std::string& device_name) : _pm_input_id (pmNoDevice) , _pm_output_id (pmNoDevice) , _name (device_name) , _input_queue (NULL) , _output_queue (NULL) , _input_pm_stream (NULL) , _output_pm_stream (NULL) , _incomplete_waves_midi_event (NULL) { validate (); } WavesMidiDevice::~WavesMidiDevice () { DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::~WavesMidiDevice (): %1\n", name())); close (); } void WavesMidiDevice::validate () { _pm_input_id = _pm_output_id = pmNoDevice; int count = Pm_CountDevices (); for (int i = 0; i < count; i++) { const PmDeviceInfo* pm_device_info = Pm_GetDeviceInfo (i); if (pm_device_info == NULL) { continue; } if (name () == pm_device_info->name) { if (pm_device_info->input){ _pm_input_id = i; } if (pm_device_info->output){ _pm_output_id = i; } } } } int WavesMidiDevice::open (PmTimeProcPtr time_proc, void* time_info) { DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::open (): %1", name ())); if (is_input () && !_input_pm_stream) { if (pmNoError != Pm_OpenInput (&_input_pm_stream, _pm_input_id, NULL, 1024, time_proc, time_info)) { char* err_msg = new char[256]; Pm_GetHostErrorText(err_msg, 256); std::cerr << "WavesMidiDevice::open (): Pm_OpenInput () failed for " << _pm_input_id << "-[" << name () << "]!" << std::endl; std::cerr << "Error: " << err_msg << std::endl; _input_pm_stream = NULL; _pm_input_id = pmNoDevice; return -1; } _input_queue = Pm_QueueCreate (QUEUE_LENGTH, sizeof (const WavesMidiEvent*)); if (NULL == _input_queue) { std::cerr << "WavesMidiDevice::open (): _input_queue = Pm_QueueCreate () failed for " << _pm_input_id << "-[" << name () << "]!" << std::endl; close (); return -1; } } if (is_output () && !_output_pm_stream) { if (pmNoError != Pm_OpenOutput (&_output_pm_stream, _pm_output_id, NULL, 1024, time_proc, time_info, LATENCY)) { char* err_msg = new char[256]; Pm_GetHostErrorText(err_msg, 256); std::cerr << "WavesMidiDevice::open (): Pm_OpenOutput () failed for " << _pm_input_id << "-[" << name () << "]!" << std::endl; std::cerr << "Error: " << err_msg << std::endl; _output_pm_stream = NULL; _pm_output_id = pmNoDevice; return -1; } _output_queue = Pm_QueueCreate (QUEUE_LENGTH, sizeof (const WavesMidiEvent*)); if (NULL == _output_queue) { std::cerr << "WavesMidiDevice::open (): _output_queue = Pm_QueueCreate () failed for " << _pm_output_id << "-[" << name () << "]!" << std::endl; close (); return -1; } } return 0; } void WavesMidiDevice::close () { DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::close (): %1\n", name ())); WavesMidiEvent *waves_midi_event; if (_input_pm_stream) { Pm_Close (_input_pm_stream); while (1 == Pm_Dequeue (_input_queue, &waves_midi_event)) { delete waves_midi_event; // XXX possible dup free in ~WavesMidiBuffer() (?) } Pm_QueueDestroy (_input_queue); _input_queue = NULL; _input_pm_stream = NULL; _pm_input_id = pmNoDevice; } if ( _output_pm_stream ) { Pm_Close (_output_pm_stream); while (1 == Pm_Dequeue (_output_queue, &waves_midi_event)) { delete waves_midi_event; // XXX possible dup free in ~WavesMidiBuffer() (?) } Pm_QueueDestroy (_output_queue); _output_queue = NULL; _output_pm_stream = NULL; _pm_output_id = pmNoDevice; } } void WavesMidiDevice::do_io () { read_midi (); write_midi (); } void WavesMidiDevice::read_midi () { if (NULL == _input_pm_stream) { return; } while (Pm_Poll (_input_pm_stream) > 0) { PmEvent pm_event; // just one message at a time int result = Pm_Read (_input_pm_stream, &pm_event, 1); if (result < 0) { DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("Pm_Read failed for (): [%1]\n", name())); break; } DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] evt-tm: %2\n", name(), pm_event.timestamp)); if (_incomplete_waves_midi_event == NULL ) { DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] new incomplete_waves_midi_event\n", name())); _incomplete_waves_midi_event = new WavesMidiEvent (pm_event.timestamp); } WavesMidiEvent *nested_pm_event = _incomplete_waves_midi_event->append_data (pm_event); if (nested_pm_event) { Pm_Enqueue (_input_queue, &nested_pm_event); DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] : Pm_Enqueue (_input_queue, nested_pm_event)\n", name())); } switch ( _incomplete_waves_midi_event->state ()) { case WavesMidiEvent::BROKEN: delete _incomplete_waves_midi_event; _incomplete_waves_midi_event = NULL; DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] : case WavesMidiEvent::BROKEN:\n", name())); break; case WavesMidiEvent::COMPLETE: DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] : Pm_Enqueue (_input_queue, _incomplete_waves_midi_event); %3\n", name (), _incomplete_waves_midi_event)); if (pmNoError != Pm_Enqueue (_input_queue, &_incomplete_waves_midi_event) ) { char* err_msg = new char[256]; Pm_GetHostErrorText(err_msg, 256); std::cerr << "WavesMidiDevice::read_midi (): Pm_Enqueue () failed for [" << name () << "]!" << std::endl; std::cerr << "Error: " << err_msg << std::endl; } _incomplete_waves_midi_event = NULL; break; default: break; } } } void WavesMidiDevice::write_midi () { if (NULL == _output_pm_stream) { return; } PmError err; WavesMidiEvent *waves_midi_event; while (1 == Pm_Dequeue (_output_queue, &waves_midi_event)) { if (waves_midi_event->sysex ()) { // LATENCY compensation err = Pm_WriteSysEx (_output_pm_stream, waves_midi_event->timestamp () - LATENCY, waves_midi_event->data ()); if (0 > err) { std::cout << "WavesMidiDevice::write_event_to_device (): [" << name () << "] Pm_WriteSysEx () failed (" << err << ")!" << std::endl; }; DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_write_midi (): SYSEX used, ev->tm: %1", waves_midi_event->timestamp () - LATENCY)); } else { err = Pm_WriteShort (_output_pm_stream, waves_midi_event->timestamp () - LATENCY, * (PmMessage*)waves_midi_event->data ()); if (0 > err) { error << "WavesMidiDevice::write_event_to_device (): [" << name () << "] Pm_WriteShort () failed (" << err << ")!" << endmsg; } DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_write_midi (): SHORTMSG used, ev->tm: %1\n", waves_midi_event->timestamp () - LATENCY)); } } return; } int WavesMidiDevice::enqueue_output_waves_midi_event (const WavesMidiEvent* waves_midi_event) { DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::enqueue_output_waves_midi_event () [%1]\n", name())); if (waves_midi_event == NULL) { error << "WavesMidiDevice::put_event_to_callback (): 'waves_midi_event' is NULL!" << endmsg; return -1; } PmError err = Pm_Enqueue (_output_queue, &waves_midi_event); if (0 > err) { error << "WavesMidiDevice::put_event_to_callback (): Pm_Enqueue () failed (" << err << ")!" << endmsg; return -1; }; return 0; } WavesMidiEvent* WavesMidiDevice::dequeue_input_waves_midi_event () { WavesMidiEvent* waves_midi_event; if (Pm_Dequeue (_input_queue, &waves_midi_event) == 1) { return waves_midi_event; } return NULL; }