/* Copyright (C) 2000-2002 Stefan Westerfeld stefan@space.twc.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "midiclient_impl.h" #include "midimanager_impl.h" #include "midimanagerport_impl.h" #include "midisyncgroup_impl.h" #include "timestampmath.h" #undef DEBUG_SYNC_DRIFT using namespace Arts; using namespace std; MidiClient_impl::MidiClient_impl(const MidiClientInfo& info, MidiManager_impl *manager) :_info(info), manager(manager), syncGroup(0) { } MidiClient_impl::~MidiClient_impl() { while(!_info.connections.empty()) disconnect(manager->findClient(_info.connections[0])); if(syncGroup) { syncGroup->clientDied(this); syncGroup = 0; } manager->removeClient(this); } MidiClientInfo MidiClient_impl::info() { return _info; } void MidiClient_impl::title(const string &newvalue) { _info.title = newvalue; } string MidiClient_impl::title() { return _info.title; } void MidiClient_impl::addInputPort(MidiPort port) { assert(_info.direction == mcdRecord); ports.push_back(port); // FIXME: should we synchronize inputPorts at all rebuildConnections(); } MidiPort MidiClient_impl::addOutputPort() { assert(_info.direction == mcdPlay); MidiPort port = MidiPort::_from_base(new MidiManagerPort_impl(this)); ports.push_back(port); rebuildConnections(); return port; } void MidiClient_impl::removePort(MidiPort port) { list::iterator i = ports.begin(); while(i != ports.end()) { if (i->_isEqual(port)) i = ports.erase(i); else i++; } rebuildConnections(); } void MidiClient_impl::rebuildConnections() { _connections.clear(); vector::iterator li; for(li = _info.connections.begin(); li != _info.connections.end(); li++) { MidiClient_impl *other = manager->findClient(*li); assert(other); list::iterator pi; for(pi = other->ports.begin(); pi != other->ports.end(); pi++) { MidiClientConnection mcc; mcc.offset = TimeStamp(0,0); mcc.port = *pi; _connections.push_back(mcc); } } adjustSync(); } list *MidiClient_impl::connections() { return &_connections; } static void removeElement(vector& vec, long el) { vector tmp; vec.swap(tmp); vector::iterator i; for(i = tmp.begin(); i != tmp.end(); i++) if(*i != el) vec.push_back(*i); } void MidiClient_impl::connect(MidiClient_impl *dest) { assert(_info.direction != dest->_info.direction); disconnect(dest); _info.connections.push_back(dest->ID()); dest->_info.connections.push_back(ID()); list::iterator pi; /* add the other clients ports to our connection list */ for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++) { MidiClientConnection mcc; mcc.offset = TimeStamp(0,0); mcc.port = *pi; _connections.push_back(mcc); } /* add our ports to the other clients connection list */ for(pi = ports.begin(); pi != ports.end(); pi++) { MidiClientConnection mcc; mcc.offset = TimeStamp(0,0); mcc.port = *pi; dest->_connections.push_back(mcc); } adjustSync(); } void MidiClient_impl::disconnect(MidiClient_impl *dest) { assert(_info.direction != dest->_info.direction); removeElement(_info.connections,dest->ID()); removeElement(dest->_info.connections,ID()); list::iterator pi; /* remove the other clients ports from our connection list */ for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++) { list::iterator ci = _connections.begin(); while(ci != _connections.end()) { if(ci->port._isEqual(*pi)) ci = _connections.erase(ci); else ci++; } } /* remove our ports from the other clients connection list */ for(pi = ports.begin(); pi != ports.end(); pi++) { list::iterator ci = dest->_connections.begin(); while(ci != dest->_connections.end()) { if(ci->port._isEqual(*pi)) ci = dest->_connections.erase(ci); else ci++; } } adjustSync(); } void MidiClient_impl::synchronizeTo(const TimeStamp& time) { list::iterator i; for(i = _connections.begin(); i != _connections.end(); i++) { MidiClientConnection& mcc = *i; #ifdef DEBUG_SYNC_DRIFT TimeStamp drift = mcc.offset; // debug drift #endif mcc.offset = mcc.port.playTime(); timeStampDec(mcc.offset, time); #ifdef DEBUG_SYNC_DRIFT timeStampDec(drift,mcc.offset); // debug drift printf("SYNC DRIFT %30s %30s: %f\n", mcc.port._interfaceName().c_str(), _info.title.c_str(), timeStampToDouble(drift)); #endif } } void MidiClient_impl::setSyncGroup(MidiSyncGroup_impl *newSyncGroup) { syncGroup = newSyncGroup; } void MidiClient_impl::adjustSync() { if(syncGroup) syncGroup->clientChanged(this); else synchronizeTo(systemMidiTimer.time()); } TimeStamp MidiClient_impl::time() { if(syncGroup) return syncGroup->time(); else return clientTime(); } TimeStamp MidiClient_impl::playTime() { if(syncGroup) return syncGroup->playTime(); else return systemMidiTimer.time(); } TimeStamp MidiClient_impl::clientTime() { TimeStamp result = playTime(); list::iterator i; for(i = _connections.begin(); i != _connections.end(); i++) { TimeStamp time = i->port.time(); timeStampDec(time, i->offset); result = timeStampMax(result, time); } return result; }