aRts audio server
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.

virtualports.cc 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /*
  2. Copyright (C) 2000 Stefan Westerfeld
  3. stefan@space.twc.de
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. #include "virtualports.h"
  18. #include <algorithm>
  19. #include <stdio.h>
  20. using namespace Arts;
  21. using namespace std;
  22. #undef VPORT_DEBUG
  23. /* virtual port connections */
  24. /*
  25. Port virtualization is used in the following case: suppose you have a module
  26. M which has an input port and an output port like that:
  27. input
  28. V
  29. -----
  30. M
  31. -----
  32. V
  33. output
  34. But suppose the module M is implement as combination of other modules, for
  35. instance the effect of M is achieved by passing the signal first through an
  36. A and then through an B module. Clients connecting M should "really" connect
  37. A,B. For this virtualization is used.
  38. There are two kinds:
  39. - masquerading: which means (like in our case), the input of a module is really
  40. implemented with the input of another module. So M's input could be really
  41. implemented by A's input
  42. there is also output masquerading, which would be for instance that M's
  43. output is really implemented by B's output
  44. - forwarding: if M in our example would choose to do nothing at all, it could
  45. simply forward its input to its output
  46. The interface for the user:
  47. MCOP will show the virtualize function to the user, which the user can use
  48. to delegate the services of a port to another module onto another port.
  49. - masquerading: in our case, for instance the user could call
  50. m._node()->virtualize("inputport",a._node(),"inputport");
  51. which would forward all input m gets on "inputport" to a's "inputport"
  52. - forwarding: in the same way, the user could call
  53. m._node()->virtualize("inputport",m._node(),"outputport");
  54. which would make m forward its input directly to its output
  55. The implementation:
  56. Virtualization is implemented here, inside the flow system, using a fairly
  57. complex forwarding strategy, which will have a graph which contains
  58. - "user-made" connections (made with connect())
  59. - "virtualize-made" connections, which can be either forwarding
  60. (input to output port) or masquerading (input to input or output to output)
  61. Out of all these, the algorithm builds "real" connections, which are
  62. then really performed inside the flow system. If you change the "user-made"
  63. or "virtualize-made" connections, the "real" connections are recalculated.
  64. The "real" connections are created by the expandHelper function. They are
  65. called vcTransport here.
  66. The strategy expandHelper uses is to go back to a port which is only output
  67. port (non forwarded, non masqueraded), and then follow the graph recursively
  68. over vcMasquerade and vcForward edges until it reaches a port which is only
  69. input. Then it creates a real connection.
  70. Some tweaks are there which allow that not on any change at the graph, all
  71. real connections will be removed, but only these that could possibly be
  72. affected by this change, and then not all real connections are created new,
  73. but only those that could possibly be created by this virtual connection.
  74. Every VPort contains a pointer to the "real" port, to let the flow system
  75. know where the "real" connections where real data will flow must be made.
  76. */
  77. VPortConnection::VPortConnection(VPort *source, VPort *dest, Style style)
  78. :source(source),dest(dest),style(style)
  79. {
  80. if(style != vcTransport)
  81. {
  82. list<VPortConnection *>::iterator i;
  83. // remove transport connections ending at "source" (they will
  84. // probably be forwarded/masqueraded elsewhere by this VPortConnection)
  85. i = source->incoming.begin();
  86. while(i != source->incoming.end())
  87. {
  88. if((*i)->style == vcTransport)
  89. {
  90. delete *i;
  91. i = source->incoming.begin();
  92. }
  93. else i++;
  94. }
  95. // remove transport connections starting at "dest" (they will
  96. // probably be forwarded/masqueraded elsewhere by this VPortConnection)
  97. i = dest->outgoing.begin();
  98. while(i != dest->outgoing.end())
  99. {
  100. if((*i)->style == vcTransport)
  101. {
  102. delete *i;
  103. i = dest->outgoing.begin();
  104. }
  105. else i++;
  106. }
  107. }
  108. // add to the connection lists
  109. source->outgoing.push_back(this);
  110. dest->incoming.push_back(this);
  111. if(style == vcTransport)
  112. {
  113. #ifdef VPORT_DEBUG
  114. arts_debug("emit a connection consumer = %s, producer = %s",
  115. dest->name(), source->name());
  116. #endif
  117. dest->port->connect(source->port);
  118. }
  119. else
  120. {
  121. source->makeTransport(this);
  122. }
  123. }
  124. VPortConnection::~VPortConnection()
  125. {
  126. #ifdef VPORT_DEBUG
  127. cout << "~VPortConnection" << endl;
  128. #endif
  129. if(style != vcTransport)
  130. {
  131. // remove transport connection which go through this connection
  132. source->removeTransport(this);
  133. }
  134. // remove this connection from the lists
  135. list<VPortConnection *>::iterator ci;
  136. ci = find(source->outgoing.begin(),source->outgoing.end(),this);
  137. assert(ci != source->outgoing.end());
  138. source->outgoing.erase(ci);
  139. ci = find(dest->incoming.begin(),dest->incoming.end(),this);
  140. assert(ci != dest->incoming.end());
  141. dest->incoming.erase(ci);
  142. if(style == vcTransport)
  143. {
  144. #ifdef VPORT_DEBUG
  145. arts_debug("delete connection %s -> %s",dest->name(), source->name());
  146. #endif
  147. dest->port->disconnect(source->port);
  148. }
  149. // reestablish all connections which started/ended here before
  150. if(style != vcTransport)
  151. {
  152. list<VPortConnection *>::iterator i;
  153. stack<VPortConnection *> todo;
  154. // reestablish transport connections which were ending at source...
  155. for(i = source->incoming.begin(); i != source->incoming.end(); i++)
  156. if((*i)->style != vcTransport) todo.push(*i);
  157. // ... and starting at dest
  158. for(i = dest->outgoing.begin(); i != dest->outgoing.end(); i++)
  159. if((*i)->style != vcTransport) todo.push(*i);
  160. // we need to do this with the stack as makeTransport can affect the
  161. // incoming/outgoing lists by adding new vcTransport connections
  162. while(!todo.empty())
  163. {
  164. todo.top()->source->makeTransport(todo.top());
  165. todo.pop();
  166. }
  167. }
  168. #ifdef VPORT_DEBUG
  169. cout << "~VPortConnection done" << endl;
  170. #endif
  171. }
  172. /*---------------------- virtual port implementation ----------------------*/
  173. VPort::VPort(Port *port) :port(port)
  174. {
  175. #ifdef VPORT_DEBUG
  176. cout << "VPort: " << name() << endl;
  177. #endif
  178. }
  179. VPort::~VPort()
  180. {
  181. #ifdef VPORT_DEBUG
  182. cout << "~VPort: " << name() << endl;
  183. #endif
  184. while(!incoming.empty()) delete *incoming.begin();
  185. while(!outgoing.empty()) delete *outgoing.begin();
  186. #ifdef VPORT_DEBUG
  187. cout << "~VPort done" << endl;
  188. #endif
  189. }
  190. bool VPort::makeVirtualizeParams(VPort *forward, VPort*& source, VPort*& dest,
  191. VPortConnection::Style& style)
  192. {
  193. source = dest = 0;
  194. // masquerading
  195. if((port->flags() & streamIn) && (forward->port->flags() & streamIn))
  196. {
  197. // input: data flow direction is from us to the "forward" port
  198. // XXX?
  199. source = this;
  200. dest = forward;
  201. style = VPortConnection::vcMasquerade;
  202. }
  203. else if((port->flags() & streamOut) && (forward->port->flags() & streamOut))
  204. {
  205. // output: data flow direction is from the "forward" port to us
  206. // XXX?
  207. source = forward;
  208. dest = this;
  209. style = VPortConnection::vcMasquerade;
  210. }
  211. // forwarding
  212. else if((port->flags() & streamIn) && (forward->port->flags() & streamOut))
  213. {
  214. source = this;
  215. dest = forward;
  216. style = VPortConnection::vcForward;
  217. }
  218. else if((port->flags() & streamOut) && (forward->port->flags() & streamIn))
  219. {
  220. source = forward;
  221. dest = this;
  222. style = VPortConnection::vcForward;
  223. }
  224. return source != 0;
  225. }
  226. /**
  227. * a->virtualize(b) means, that the functionality that port a should provide
  228. * (e.g. produce or consume data) is really provided by port b
  229. */
  230. void VPort::virtualize(VPort *forward)
  231. {
  232. VPort *source, *dest;
  233. VPortConnection::Style style;
  234. if(makeVirtualizeParams(forward,source,dest,style))
  235. {
  236. #ifdef VPORT_DEBUG
  237. cout << "virtualize ... source (producer) is " << source->name() <<
  238. " dest (consumer) is " << dest->name() << endl;
  239. #endif
  240. new VPortConnection(source,dest,style);
  241. }
  242. }
  243. void VPort::devirtualize(VPort *forward)
  244. {
  245. VPort *source, *dest;
  246. VPortConnection::Style style;
  247. // XXX?
  248. if(makeVirtualizeParams(forward,source,dest,style))
  249. {
  250. list<VPortConnection *>::iterator i;
  251. for(i = source->outgoing.begin(); i != source->outgoing.end(); i++)
  252. {
  253. if((*i)->source == source && (*i)->dest == dest
  254. && (*i)->style == style)
  255. {
  256. delete (*i);
  257. return;
  258. }
  259. }
  260. }
  261. }
  262. void VPort::setFloatValue(float value)
  263. {
  264. if(outgoing.empty())
  265. {
  266. AudioPort *aport = port->audioPort();
  267. assert(aport);
  268. aport->setFloatValue(value);
  269. }
  270. else
  271. {
  272. list<VPortConnection *>::iterator i;
  273. for(i=outgoing.begin();i != outgoing.end(); i++)
  274. {
  275. VPortConnection *conn = *i;
  276. assert(conn->style == VPortConnection::vcMasquerade);
  277. conn->dest->setFloatValue(value);
  278. }
  279. }
  280. }
  281. void VPort::connect(VPort *dest)
  282. {
  283. VPortConnection *conn;
  284. if(port->flags() & streamOut)
  285. {
  286. conn = new VPortConnection(this,dest,VPortConnection::vcConnect);
  287. }
  288. else
  289. {
  290. conn = new VPortConnection(dest,this,VPortConnection::vcConnect);
  291. }
  292. }
  293. void VPort::disconnect(VPort *dest)
  294. {
  295. if(port->flags() & streamOut)
  296. {
  297. list<VPortConnection *>::iterator ci = outgoing.begin();
  298. while(ci != outgoing.end())
  299. {
  300. assert((*ci)->source == this);
  301. if((*ci)->dest == dest && (*ci)->style==VPortConnection::vcConnect)
  302. {
  303. delete (*ci); // will remove itself from the outgoing list
  304. return;
  305. }
  306. ci++;
  307. }
  308. }
  309. else
  310. {
  311. if(dest->port->flags() & streamOut)
  312. {
  313. dest->disconnect(this);
  314. return;
  315. }
  316. }
  317. }
  318. void VPort::expandHelper(VPortConnection *conn, int state, VPort *current,
  319. VPort *source, VPort *dest, bool remove)
  320. {
  321. list<VPortConnection *>::iterator ci;
  322. #ifdef VPORT_DEBUG
  323. cout << "expandhelper state " << state << " name " << current->name() << endl;
  324. #endif
  325. if(state == 1) /* state 1: scan backward for output ports */
  326. {
  327. if(current->incoming.empty())
  328. {
  329. if(current->port->flags() & streamOut)
  330. expandHelper(conn,2,current,current,dest,remove);
  331. }
  332. for(ci = current->incoming.begin(); ci != current->incoming.end();ci++)
  333. {
  334. assert((*ci)->style != VPortConnection::vcTransport);
  335. expandHelper(conn,1,(*ci)->source,source,dest,remove);
  336. }
  337. }
  338. else if(state == 2) /* state 2: output port expansion */
  339. {
  340. assert(current->port->flags() & streamOut);
  341. for(ci = current->outgoing.begin(); ci != current->outgoing.end();ci++)
  342. {
  343. /* xconn=0 ensures that only paths are counted which contain conn */
  344. VPortConnection *xconn = conn;
  345. if(*ci == conn) xconn = 0;
  346. if((*ci)->style == VPortConnection::vcMasquerade)
  347. {
  348. expandHelper(xconn,2,(*ci)->dest,source,dest,remove);
  349. }
  350. else if((*ci)->style == VPortConnection::vcConnect)
  351. {
  352. expandHelper(xconn,3,(*ci)->dest,source,(*ci)->dest,remove);
  353. }
  354. }
  355. }
  356. else if(state == 3) /* state 3: input port expansion */
  357. {
  358. assert(current->port->flags() & streamIn);
  359. for(ci = current->outgoing.begin(); ci != current->outgoing.end();ci++)
  360. {
  361. /* xconn=0 ensures that only paths are counted which contain conn */
  362. VPortConnection *xconn = conn;
  363. if(*ci == conn) xconn = 0;
  364. if((*ci)->style == VPortConnection::vcMasquerade)
  365. {
  366. // XXX ?
  367. expandHelper(xconn,3,(*ci)->dest,source,(*ci)->dest,remove);
  368. }
  369. else if((*ci)->style == VPortConnection::vcForward)
  370. {
  371. expandHelper(xconn,2,(*ci)->dest,source,dest,remove);
  372. }
  373. }
  374. if(current->outgoing.empty() && conn == 0)
  375. {
  376. if(remove)
  377. {
  378. // delete exactly one transport connection
  379. bool removeOk = false;
  380. ci = current->incoming.begin();
  381. while(ci != current->incoming.end() && !removeOk)
  382. {
  383. if((*ci)->source == source && (*ci)->dest == dest
  384. && (*ci)->style == VPortConnection::vcTransport)
  385. {
  386. delete (*ci);
  387. removeOk = true;
  388. }
  389. else ci++;
  390. }
  391. assert(removeOk);
  392. }
  393. else
  394. {
  395. new VPortConnection(source,dest,VPortConnection::vcTransport);
  396. }
  397. }
  398. }
  399. }
  400. void VPort::makeTransport(VPortConnection *conn)
  401. {
  402. expandHelper(conn,1,this,0,0,false);
  403. }
  404. void VPort::removeTransport(VPortConnection *conn)
  405. {
  406. expandHelper(conn,1,this,0,0,true);
  407. }
  408. const char *VPort::name()
  409. {
  410. if(_name.empty())
  411. {
  412. _name = port->parent->object()->_interfaceName() + "." +
  413. port->name();
  414. }
  415. return _name.c_str();
  416. }