You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
5.9 KiB
275 lines
5.9 KiB
/*
|
|
|
|
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<MidiPort>::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<long>::iterator li;
|
|
for(li = _info.connections.begin(); li != _info.connections.end(); li++)
|
|
{
|
|
MidiClient_impl *other = manager->findClient(*li);
|
|
assert(other);
|
|
|
|
list<MidiPort>::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<MidiClientConnection> *MidiClient_impl::connections()
|
|
{
|
|
return &_connections;
|
|
}
|
|
|
|
static void removeElement(vector<long>& vec, long el)
|
|
{
|
|
vector<long> tmp;
|
|
vec.swap(tmp);
|
|
vector<long>::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<MidiPort>::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<MidiPort>::iterator pi;
|
|
|
|
/* remove the other clients ports from our connection list */
|
|
for(pi = dest->ports.begin(); pi != dest->ports.end(); pi++)
|
|
{
|
|
list<MidiClientConnection>::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<MidiClientConnection>::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<MidiClientConnection>::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<MidiClientConnection>::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;
|
|
}
|