From ebd6cfca82369fc3376275f0a72c0d728d0a38a8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 22 May 2011 05:33:08 +0200 Subject: [PATCH 001/108] Start design work for the Player Subsystem --- wiki/renderengine.html | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b48372abb..4b7b80c02 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1632,6 +1632,26 @@ The main tool used to implement this separation is the [[Builder Pattern|http:// Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways. +
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
+
+!Starting point
+The idea is to start out with the design of the PlayerDummy and to //transform//  it into the full player subsystem.
+* the ~DisplayService in that dummy player design moves down into Proc-Layer and becomes the OutputManager
+* likewise, the ~DisplayerSlot is transformed into the interface OutputSlot, with various implementations to be registered with the OutputManager
+* the core idea of having a play controler act as the frontend and handle to an PlayProcess is retained.
+
+!!Stages of transition
+Reworking the dummy player into the PlayerSubsystem is a larger undertaking, and best broken up into several //stages.//
+# just moving the existing entities into the final location (typically down in the layer hierarchy)
+# introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
+# rework the ~ProcessImpl and the ~TickService to become the CalculationStream and use the real Engine Interface, but still with an mock implementation hooked up behind
+# extend and elaborate the handling of output, build the foundation of the real OutputManager
+# switch to the real Scheduler and Engine implementation
+
+!!Idea
+Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+
LayerSeparationInterface provided by the GUI.
 Access point especially for the playback. A render- or playback process uses the DisplayFacade to push media data up to the GUI for display within a viewer widget of full-screen display. This can be thought off as a callback mechanism. In order to use the DisplayFacade, client code needs a DisplayerSlot (handle), which needs to be set up by the UI first and will be provided when starting the render or playback process.
@@ -3177,7 +3197,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3193,7 +3213,7 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and — at the same time — by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and — at the same time — by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
 
 We should note that in both cases this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
@@ -4103,7 +4123,7 @@ The play process is an conceptual entity linking together several activities in
 * the RenderEngine has the ability to cary out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
-
+
Within Lumiera, »Player« denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// → [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
 Yet it adresses some central concerns:
@@ -4117,8 +4137,8 @@ Yet it adresses some central concerns:
 :the player translates continuous time values into discrete frame counts.
 :to perform this [[quantisation|TimeQuant]], the help of the session for building a TimeGrid for each output channel is required.
 
-!{{red{WIP 12/10}}} under construction
-The player subsystem is currently about to be designed and built up; some time ago, __Joel Holdsworth__ and __Ichthyo__ did a design study with a PlayerDummy, which is currently hooked up with the TransportControl in the Lumiera GUI.
+!{{red{WIP 5/2011}}} under construction
+The player subsystem is currently about to be designed and built up; some time ago, __Joel Holdsworth__ and __Ichthyo__ did a design study with a PlayerDummy, which is currently hooked up with the TransportControl in the Lumiera GUI. Starting from these experiences, and the general requirements of an NLE, the [[design of the Player subsystem|DesignPlayerSubsystem]] is being worked out.
 
From 899ffa60ca7afdd45ded9ee220c8b1c00f0f9c81 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 23 May 2011 04:43:56 +0200 Subject: [PATCH 002/108] WIP create (planned) new entities for the player subsystem --- src/backend/engine/scheduler-frontend.cpp | 33 ++ src/backend/engine/scheduler-frontend.hpp | 58 +++ src/proc/asset/viewer.cpp | 72 ++++ src/proc/asset/viewer.hpp | 115 ++++++ src/proc/engine/dispatcher.cpp | 42 ++ src/proc/engine/dispatcher.hpp | 64 +++ src/proc/engine/engine-service.cpp | 382 ++++++++++++++++++ src/proc/engine/engine-service.hpp | 161 ++++++++ .../worker}/dummy-image-generator.cpp | 0 .../worker}/dummy-image-generator.hpp | 0 .../{play => engine/worker}/tick-service.hpp | 0 src/proc/mobject/session/generator-mo.cpp | 86 ++++ src/proc/mobject/session/generator-mo.hpp | 110 +++++ src/proc/play/dummy-play-connection.cpp | 382 ++++++++++++++++++ src/proc/play/output-manager.cpp | 252 ++++++++++++ src/proc/play/output-manager.hpp | 190 +++++++++ src/proc/play/play-controller.hpp | 161 ++++++++ src/proc/play/play-process.cpp | 382 ++++++++++++++++++ src/proc/play/play-process.hpp | 161 ++++++++ src/proc/play/play-service.cpp | 382 ++++++++++++++++++ src/proc/play/play-service.hpp | 161 ++++++++ src/proc/play/sound/jack-output.cpp | 252 ++++++++++++ src/proc/play/sound/jack-output.hpp | 190 +++++++++ 23 files changed, 3636 insertions(+) create mode 100644 src/backend/engine/scheduler-frontend.cpp create mode 100644 src/backend/engine/scheduler-frontend.hpp create mode 100644 src/proc/asset/viewer.cpp create mode 100644 src/proc/asset/viewer.hpp create mode 100644 src/proc/engine/dispatcher.cpp create mode 100644 src/proc/engine/dispatcher.hpp create mode 100644 src/proc/engine/engine-service.cpp create mode 100644 src/proc/engine/engine-service.hpp rename src/proc/{play => engine/worker}/dummy-image-generator.cpp (100%) rename src/proc/{play => engine/worker}/dummy-image-generator.hpp (100%) rename src/proc/{play => engine/worker}/tick-service.hpp (100%) create mode 100644 src/proc/mobject/session/generator-mo.cpp create mode 100644 src/proc/mobject/session/generator-mo.hpp create mode 100644 src/proc/play/dummy-play-connection.cpp create mode 100644 src/proc/play/output-manager.cpp create mode 100644 src/proc/play/output-manager.hpp create mode 100644 src/proc/play/play-controller.hpp create mode 100644 src/proc/play/play-process.cpp create mode 100644 src/proc/play/play-process.hpp create mode 100644 src/proc/play/play-service.cpp create mode 100644 src/proc/play/play-service.hpp create mode 100644 src/proc/play/sound/jack-output.cpp create mode 100644 src/proc/play/sound/jack-output.hpp diff --git a/src/backend/engine/scheduler-frontend.cpp b/src/backend/engine/scheduler-frontend.cpp new file mode 100644 index 000000000..588d1276b --- /dev/null +++ b/src/backend/engine/scheduler-frontend.cpp @@ -0,0 +1,33 @@ +/* + RenderEngine - a complete network of processing nodes usable for rendering + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 "proc/engine/renderengine.hpp" + +namespace engine { + + + /** */ + + + +} // namespace engine diff --git a/src/backend/engine/scheduler-frontend.hpp b/src/backend/engine/scheduler-frontend.hpp new file mode 100644 index 000000000..c3030271f --- /dev/null +++ b/src/backend/engine/scheduler-frontend.hpp @@ -0,0 +1,58 @@ +/* + RENDERENGINE.hpp - a complete network of processing nodes usable for rendering + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + +#ifndef ENGINE_RENDERENGINE_H +#define ENGINE_RENDERENGINE_H + +#include + +#include "proc/engine/rendergraph.hpp" + + +using std::list; + + +namespace engine { + + + /** + * @todo this is planned to become the frontend + * to the render node network, which can be considered + * at the lower end of the middle layer; the actual + * render operations are mostly implemented by the backend + * ////////TODO WIP as of 12/2010 + */ + class RenderEngine : public RenderGraph + { + public: + ///// TODO: find out about the public operations + // note: the play controller lives in the proc-layer, + // but is a subsystem separate of the sesison. + + private: + list renderSegments; + + }; + +} // namespace engine +#endif diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp new file mode 100644 index 000000000..bb2f9e36e --- /dev/null +++ b/src/proc/asset/viewer.cpp @@ -0,0 +1,72 @@ +/* + Timeline - independent top-level element of the Session + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "proc/asset/timeline.hpp" +//#include "proc/mobject/session/track.hpp" +//#include "proc/mobject/placement.hpp" +//#include "proc/mobject/session/mobjectfactory.hpp" +#include "proc/mobject/session/binding.hpp" +#include "proc/assetmanager.hpp" + + +namespace asset { + + using lib::AutoRegistered; + + + + /** @todo anything significant to do here??? */ + Timeline::Timeline (const Asset::Ident& idi, RBinding const& sequenceBinding) + : Struct (idi) + , boundSequence_(sequenceBinding) + { + REQUIRE (boundSequence_); + } + + + PTimeline + Timeline::create (Asset::Ident const& idi, RBinding const& sequenceBinding) + { + REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); + + PTimeline newElement (AssetManager::instance().wrap (*new Timeline(idi, sequenceBinding))); + getRegistry().append (newElement); + + ENSURE (newElement); + ENSURE (getRegistry().isRegistered (*newElement)); + return newElement; + } + + + void + Timeline::unlink () + { + AutoRegistered::detach(); + boundSequence_.purge(); + Struct::unlink(); + } + + + + +} // namespace asset diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp new file mode 100644 index 000000000..7bf168e1e --- /dev/null +++ b/src/proc/asset/viewer.hpp @@ -0,0 +1,115 @@ +/* + TIMELINE.hpp - independent top-level element of the Session + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + + +/** @file timeline.hpp + ** Top level structural element within the session. + ** Each Lumiera session may contain multiple top level timeline containers, + ** which at the same time act as structural asset and as part of the public + ** session API exposed to clients for discovering the session contents. + ** Actually, Timelines are facade objects, delegating the implementation to + ** the BindingMO, the Axis and the Sequences/Tracks. + ** + ** Contrary to usual habits in video/sound editing software, in Lumiera the + ** tracks are \em not part of the timeline, but rather attached directly to + ** the sequence container. To be usable, a timeline needs a binding to refer + ** to such a sequence, but this sequence may be bound into multiple timelines + ** or even virtual clips simultaneously. + ** + ** Like every structural asset, the creation of timelines happens automatically + ** on referral; Timelines can be queried from the StructFactory, providing additional + ** requested capabilities. Commonly clients will retrieve a given timeline by query + ** on the name-ID of the timeline: \c Struct::retrieve(Query("id(theName).")) + ** Additionally, the binding to a specific sequence may be established alongside: + ** \c "timeline(theTimelineName),bindSequence(theTimelineName,sequenceID)." + ** + ** @see Session + ** @see Sequence + ** @see StructFactory + ** + */ + + +#ifndef ASSET_TIMELINE_H +#define ASSET_TIMELINE_H + +#include "proc/asset/struct.hpp" +//#include "proc/mobject/mobject.hpp" +//#include "proc/mobject/placement.hpp" +#include "proc/mobject/mobject-ref.hpp" +//#include "proc/mobject/session/binding.hpp" ////TODO avoidable?? +#include "lib/p.hpp" +#include "lib/element-tracker.hpp" + + +//#include +//#include + +//using std::vector; +//using std::string; + +namespace mobject { +namespace session { + + class Binding; + typedef MORef RBinding; +}} + + +namespace asset { + + +// using lumiera::P; + class Timeline; + typedef lumiera::P PTimeline; + + + /** + * TODO type comment + */ + class Timeline + : public Struct + , public lib::AutoRegistered + { + typedef mobject::session::RBinding RBinding; + + RBinding boundSequence_; + + Timeline (Ident const&, RBinding const&); + + public: + /** create and register a new Timeline instance */ + static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); + + protected: + virtual void unlink (); + + }; + + + + +///////////////////////////TODO currently just fleshing the API + + +} // namespace asset +#endif diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp new file mode 100644 index 000000000..fefaf7c63 --- /dev/null +++ b/src/proc/engine/dispatcher.cpp @@ -0,0 +1,42 @@ +/* + RenderGraph - render network corresponding to one segment of the timeline + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 "proc/engine/rendergraph.hpp" +#include "lib/frameid.hpp" +#include "proc/state.hpp" + +namespace lumiera { + + /** storage for the unique node-ID counter */ + ulong NodeID::currID (0); +} + + +namespace engine { + + /** */ + + + + +} // namespace engine diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp new file mode 100644 index 000000000..a77eac68b --- /dev/null +++ b/src/proc/engine/dispatcher.hpp @@ -0,0 +1,64 @@ +/* + RENDERGRAPH.hpp - render network corresponding to one segment of the timeline + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + +#ifndef ENGINE_RENDERGRAPH_H +#define ENGINE_RENDERGRAPH_H + +#include "proc/common.hpp" +#include "proc/state.hpp" +#include "lib/time/timevalue.hpp" + + + +namespace engine { + + using lib::time::TimeSpan; + using lib::time::FSecs; + using lib::time::Time; + + class ExitNode; + + /** + * @todo likely to be reworked into the engine backbone /////////////TODO WIP as of 12/2010 + */ + class RenderGraph + { + protected: + ExitNode * output; + + /** timerange covered by this RenderGraph */ + TimeSpan segment_; + + public: + RenderGraph() + : segment_(Time::ZERO, FSecs(5)) + { + UNIMPLEMENTED ("anything regarding the Fixture datastructure"); + } + + }; + + + +} // namespace engine +#endif diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/engine/engine-service.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "proc/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/engine/engine-service.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + +/** @file dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/dummy-image-generator.cpp b/src/proc/engine/worker/dummy-image-generator.cpp similarity index 100% rename from src/proc/play/dummy-image-generator.cpp rename to src/proc/engine/worker/dummy-image-generator.cpp diff --git a/src/proc/play/dummy-image-generator.hpp b/src/proc/engine/worker/dummy-image-generator.hpp similarity index 100% rename from src/proc/play/dummy-image-generator.hpp rename to src/proc/engine/worker/dummy-image-generator.hpp diff --git a/src/proc/play/tick-service.hpp b/src/proc/engine/worker/tick-service.hpp similarity index 100% rename from src/proc/play/tick-service.hpp rename to src/proc/engine/worker/tick-service.hpp diff --git a/src/proc/mobject/session/generator-mo.cpp b/src/proc/mobject/session/generator-mo.cpp new file mode 100644 index 000000000..426cd8e57 --- /dev/null +++ b/src/proc/mobject/session/generator-mo.cpp @@ -0,0 +1,86 @@ +/* + Clip - a Media Clip + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 "proc/mobject/session/clip.hpp" +#include "proc/assetmanager.hpp" +#include "proc/asset/media.hpp" +#include "proc/asset/clip.hpp" +#include "lib/time/mutation.hpp" +#include "lib/util.hpp" + +using lib::time::Mutation; +using util::isnil; + +namespace mobject { +namespace session { + + /** new clip-MO linked with the given asset::Clip. + * Initially, this clip will cover the whole source media length. + */ + Clip::Clip (const asset::Clip& clipDef, const Media& mediaDef) + : mediaDef_(mediaDef) + , clipDef_(clipDef) + { + setupLength(); + throwIfInvalid(); + } + + + + /** implementing the common MObject self test. + * Length definition is consitent, underlying + * media def is accessible etc. */ + bool + Clip::isValid () const + { + TODO ("check consistency of clip length def, implies accessing the underlying media def"); + return !isnil(length_); + } + + + void + Clip::setupLength() + { + TODO ("really calculate the length of a clip and set length field"); + this->length_.accept (Mutation::changeDuration(mediaDef_.getLength())); + } + + + PMedia + Clip::getMedia () const + { + return asset::AssetManager::wrap (mediaDef_); + } + + + PClipAsset + Clip::findClipAsset () const + { + return asset::AssetManager::wrap (clipDef_); + } + + + + +}} // namespace mobject::session + diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp new file mode 100644 index 000000000..52e134926 --- /dev/null +++ b/src/proc/mobject/session/generator-mo.hpp @@ -0,0 +1,110 @@ +/* + CLIP.hpp - a Media Clip + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + +#ifndef MOBJECT_SESSION_CLIP_H +#define MOBJECT_SESSION_CLIP_H + +#include "proc/mobject/session/abstractmo.hpp" +#include "lib/time/timevalue.hpp" + + +namespace asset { + class Media; + class Clip; +} + +namespace mobject { +namespace session { + + using asset::Media; + using lib::time::TimeVar; + + typedef P PMedia; + typedef P PClipAsset; + + + /** + * A user visible/editable Clip is a reference to a contiguous + * sequence of media data loaded as Asset into the current Session. + * As such, it is a virtual (non destructive) cut or edit of the + * source material and can be placed into the Session to be rendered + * into the output. The actual media type of a clip will be derived + * at runtime by resolving this reference to the underlying Asset. + * + * @todo define how to denote Time positions /lengths. This is tricky, + * because it depends on the actual media type, and we want to encapsulate + * all these details as much as possible. + */ + class Clip + : public AbstractMO + { + string + initShortID() const + { + return buildShortID("Clip"); + } + + void setupLength(); + + + + protected: + /** start position in source */ + TimeVar start_; + + /** @todo using a mere ref here is against the scheme and only + done as temporal solution, until we work out how to handle + multichannel clips. It should be a smart pointer of some kind + and the unlink() function of the asset should take it into + account when breaking circular references. + */ + + const Media & mediaDef_; + const asset::Clip & clipDef_; + + Clip (const asset::Clip&, const Media&); + friend class MObjectFactory; + + + public: + bool isValid() const; + + /** access the underlying media asset */ + PMedia getMedia () const; + + /** locate the corresponding asset + * representing this clip or the whole + * compound in case of a multichannel clip + */ + PClipAsset findClipAsset () const; + + DEFINE_PROCESSABLE_BY (builder::BuilderTool); + + }; + + typedef Placement PClipMO; + + + +}} // namespace mobject::session +#endif diff --git a/src/proc/play/dummy-play-connection.cpp b/src/proc/play/dummy-play-connection.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/play/dummy-play-connection.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "proc/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp new file mode 100644 index 000000000..96600bf3b --- /dev/null +++ b/src/proc/play/output-manager.cpp @@ -0,0 +1,252 @@ +/* + DisplayService - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "gui/display-service.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + + +namespace gui { + + + + namespace { // hidden local details of the service implementation.... + + + + /* ================== define an lumieraorg_Display instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DisplayFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Display"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", + const char*, (LumieraInterface ifa), + { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... + + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 + ,lumieraorg_DisplayService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + try + { + _instance->allocate (slotHandle,true); + } + catch (lumiera::Error&){ /* error state remains set */ } + } + ) + , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + _instance->allocate (slotHandle,false); + } + ) + , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", + void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), + { + //skipping full checks for performance reasons + REQUIRE (_instance && !lumiera_error_peek()); + + REQUIRE (slotHandle); + DisplayerSlot& slot = _instance->resolve (slotHandle); + slot.put (frame); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DisplayService::DisplayService() + : error_("") + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) + { + INFO (progress, "Display Facade opened."); + } + + + + LumieraDisplaySlot + DisplayService::setUp (FrameDestination const& outputDestination) + { + DisplayerTab& slots (_instance->slots_); + return &slots.manage (new DisplayerSlot (outputDestination)); + } + + + + void + DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) + { + REQUIRE (handle); + if (doAllocate) + { + if (handle->put_) + throw lumiera::error::Logic("slot already allocated for output"); + else + // Mark the handle as "allocated" and ready for output: + // Place the function pointer from the C interface into the handle struct. + // calling it will invoke the implementing instance's "put" function + // (see the LUMIERA_INTERFACE_INLINE above in this file!) + handle->put_ = serviceInstance_.get().put; + } + else + handle->put_ = 0; + } + + + + DisplayerSlot& + DisplayService::resolve (LumieraDisplaySlot handle) + { + REQUIRE (handle); + REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); + + return *static_cast (handle); + } + + + + + + /* === DisplayerSlot Implementation === */ + + + DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) + : currBuffer_(0) + { + put_ = 0; // mark as not allocated + hasFrame_.connect (outputDestination); + dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); + } + + + DisplayerSlot::~DisplayerSlot() + { + TRACE (gui_dbg, "Displayer Slot closing..."); + } + + + void + DisplayerSlot::displayCurrentFrame() + { + hasFrame_.emit (currBuffer_); + } + + +} // namespace proc diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp new file mode 100644 index 000000000..1dec2c192 --- /dev/null +++ b/src/proc/play/output-manager.hpp @@ -0,0 +1,190 @@ +/* + DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + +/** @file display-service.hpp + ** A public service provided by the GUI, implementing the lumiera::Display facade interface. + ** It serves two purposes: + ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points + ** and allow to receive frames and dispatch them to the GTK main event loop thread. + ** Conceptually, creating such a slot means providing a possible display for output. + ** - It provides the actual implementation of the Display facade interface, i.e. the function + ** which is to invoked periodically by the playback processes to dispose a new frame into + ** the display. + ** + ** This service is the implementation of a layer separation facade interface. This header defines + ** the interface used to \em provide this service, not to access it. Clients get a specific + ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using + ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access + ** an implementation instance of this service in order to push actual frames up. + ** + ** @see lumiera::Display + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef GUI_DISPLAY_SERVICE_H +#define GUI_DISPLAY_SERVICE_H + + +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include +#include +#include +#include +#include + + +namespace gui { + + using std::string; + using std::vector; + using lumiera::Display; + using Glib::Dispatcher; + + + typedef sigc::slot FrameDestination; + typedef sigc::signal FrameSignal; + + + + /******************************************************************** + * Actual implementation of a single displayer slot. Internally, + * it is connected via the Glib::Dispatcher for outputting frames + * to a viewer widget, which executes within the GTK event thread. + * @note must be created from the GTK event thread. + */ + class DisplayerSlot + : public lumiera_displaySlot, + boost::noncopyable + { + Dispatcher dispatcher_; + FrameSignal hasFrame_; + + LumieraDisplayFrame currBuffer_; + + + public: + DisplayerSlot (FrameDestination const&) ; + ~DisplayerSlot () ; + + /* Implementation-level API to be used by DisplayService */ + + /** receive a frame to be displayed */ + inline void put (LumieraDisplayFrame); + + + private: + /** internal: activated via Dispatcher + * and running in GTK main thread */ + void displayCurrentFrame(); + + }; + + typedef lib::ScopedPtrVect DisplayerTab; + + + + /****************************************************** + * Actual implementation of the DisplayService. + * Creating an instance of this class automatically + * registers the interface lumieraorg_Display with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + * \par + * In addition to the Display interface, this class + * implements an additional service for the GUI, + * allowing actually to set up display slots, which + * then can be handed out to client code in the + * course of the play process for outputting frames. + */ + class DisplayService + : boost::noncopyable + { + + string error_; + DisplayerTab slots_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) + , lumiera::Display + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + + public: + DisplayService(); + ~DisplayService() { + INFO (proc_dbg, "Display service dying..."); + + } + + + /** open a new display, sending frames to the given output destination + * @return handle for this slot, can be used to start a play process. + * NULL handle in case of any error. */ + static LumieraDisplaySlot setUp (FrameDestination const&); + + + /** prepare and the given slot for output + * @param doAllocate allocate when true, else release it + * @throw lumiera::error::Logic when already in use */ + void allocate (LumieraDisplaySlot, bool doAllocate); + + + /** resolve the given display slot handle to yield a ref + * to an actual implementation object. In order to be resolvable, + * the DisplayerSlot needs to be locked (=allocated) for output use. */ + DisplayerSlot& resolve (LumieraDisplaySlot); + + }; + + + + + void + DisplayerSlot::put(LumieraDisplayFrame newFrame) + { + if (newFrame != currBuffer_) + { + currBuffer_ = newFrame; + dispatcher_.emit(); + } + else + { + TRACE (render, "frame dropped?"); + } + } + + + +} // namespace gui +#endif diff --git a/src/proc/play/play-controller.hpp b/src/proc/play/play-controller.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/play/play-controller.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + +/** @file dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/play/play-process.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "proc/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/play/play-process.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + +/** @file dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp new file mode 100644 index 000000000..ea443f709 --- /dev/null +++ b/src/proc/play/play-service.cpp @@ -0,0 +1,382 @@ +/* + DummyPlayerService - access point and service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "proc/play/dummy-player-service.hpp" +#include "proc/play/dummy-image-generator.hpp" +#include "proc/play/tick-service.hpp" +#include "lib/singleton.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + +#include +#include +#include +#include + + + +namespace proc { + namespace play{ + + using std::string; + using lumiera::Subsys; + using std::auto_ptr; + using boost::scoped_ptr; + using std::tr1::bind; + + + namespace { // hidden local details of the service implementation.... + + /** details of how the DummyPlayer service can be started + * and used as independent "subsystem" within main() */ + class DummyPlayerSubsysDescriptor + : public Subsys + { + operator string () const { return "Dummy-Player"; } + + + bool + shouldStart (lumiera::Option&) + { + return false; // for now the DummyPlayerService only comes "up" as dependency, + } // but doesn't start as a subsystem on it's own. + + bool + start (lumiera::Option&, Subsys::SigTerm terminationHandle) + { + ASSERT (!thePlayer_); + + thePlayer_.reset (new DummyPlayerService (terminationHandle)); + return true; + } + + /** manages the actual (single) instance of the player service impl */ + scoped_ptr thePlayer_; + + + void + triggerShutdown () throw() + { + thePlayer_.reset(0); + // note: shutdown of the DummyPlayerService instance may block + // for a short period, until termination of all tick services + } + + bool + checkRunningState () throw() + { + return (thePlayer_); + } + }; + + lib::Singleton theDummyPlayerDescriptor; + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DummyPlayerFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", + const char*, (LumieraInterface ifa), + { (void)ifa; return "DummyPlayer"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... + + typedef ProcessImpl* ProcP; + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 + ,lumieraorg_DummyPlayerService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", + LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return 0; + } + + return static_cast (_instance->start(viewerHandle)); + } + ) + , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", + void, (LumieraPlayProcess handle, bool doPlay), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + proc->doPlay(doPlay); + } + ) + , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", + void, (LumieraPlayProcess handle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (handle); + ProcP proc = static_cast (handle); + + ProcessImpl::terminate (proc); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) + : error_("") + , notifyTermination_(terminationHandle) + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) + { + INFO (progress, "DummyPlayer Facade opened."); + } + + + + + /** @par implementation note + * A new process (implementation) is created, configured + * and started here. This may include spawning a thread or + * allocating a timer. The newly created process is self-contained + * and will be just handed out, without caring for its lifecycle. + * If client code accesses this function via the plain C interface, + * the client is responsible for terminating this process, whereas + * when using the C++ interface, you'll get a Handle object which + * manages the lifecycle automatically. + */ + ProcessImpl* + DummyPlayerService::start (LumieraDisplaySlot viewerHandle) + { + auto_ptr newProcess (new ProcessImpl (viewerHandle)); + + REQUIRE (!newProcess->isActive()); + newProcess->setRate(25); + + return newProcess.release(); + } + + + + + + + /* === Process Implementation === */ + + + ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) + : fps_(0) + , play_(false) + , display_(Display::facade().getHandle (viewerHandle)) + , imageGen_(0) + , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) + { } + + + ProcessImpl::~ProcessImpl() + { + INFO (proc_dbg, "Playback process halted..."); + } + + + void + ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle + { + if (process) + delete process; + } + + + + DummyPlayer::Process + ProcessImpl::createHandle() + { + DummyPlayer::Process handle; + handle.activate(this, &terminate); // note the deleter function... + return handle; + } + + + + void + ProcessImpl::setRate (uint fps) + { + REQUIRE (fps==0 || fps_==0 ); + REQUIRE (fps==0 || !play_ ); + REQUIRE (tick_); + + fps_ = fps; + play_ = (fps != 0); + + if (play_) + imageGen_.reset(new DummyImageGenerator(fps)); + + // callbacks with given frequency, starting now + tick_->activate(fps); + } + + + + void + ProcessImpl::doPlay(bool yes) + { + REQUIRE (isActive()); + tick_->activate (yes? fps_:0); + play_ = yes; + } + + + + void + ProcessImpl::doFrame() + { + REQUIRE (isActive()); + ASSERT (imageGen_); + + if (play_) + display_(imageGen_->next()); + else + display_(imageGen_->current()); + } + + + + } // namespace play + +} // namespace proc + + + + + +namespace lumiera { /* === Forwarding function(s) on the Process handle === */ + + void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } + + + + + + /** @internal intended for use by main(). */ + lumiera::Subsys& + DummyPlayer::getDescriptor() + { + return proc::play::theDummyPlayerDescriptor(); + } + + // emit the vtable here into this translation unit within liblumieraproc.so ... + DummyPlayer::~DummyPlayer() { } + + +} // namespace lumiera diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp new file mode 100644 index 000000000..75e0f3d78 --- /dev/null +++ b/src/proc/play/play-service.hpp @@ -0,0 +1,161 @@ +/* + DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + +/** @file dummy-player-service.hpp + ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. + ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The + ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding + ** the necessary handles and allocations and providing an uniform API to the client side. + ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and + ** it uses an output handle (functor) to push the generated frames up. + ** + ** This service is the implementation of a layer separation facade interface. Clients should use + ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_DUMMYPLAYER_SERVICE_H +#define PROC_DUMMYPLAYER_SERVICE_H + + +#include "include/dummy-player-facade.h" +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" + +#include +#include +#include + + +namespace proc { + namespace play { + + using std::string; + using lumiera::Subsys; + using lumiera::Display; + using lumiera::DummyPlayer; + + + class DummyImageGenerator; + class TickService; + + + /******************************************************************** + * Actual implementation of a single (dummy) playback process. + * The DummyPlayerService (see below) maintains a collection of such + * actively running playback processes, while the client code gets + * DummyPlayer::Process handles to track any ongoing use. Users of + * the plain C interface get a direct bare pointer to the respective + * ProcessImpl instance and have to manage the lifecycle manually. + */ + class ProcessImpl + : public lumiera_playprocess, + boost::noncopyable + { + uint fps_; + bool play_; + + Display::Sink display_; + boost::scoped_ptr imageGen_; + boost::scoped_ptr tick_; + + + public: + ProcessImpl(LumieraDisplaySlot) ; + ~ProcessImpl() ; + + + /* Implementation-level API */ + + /** activate a playback process + * with given specification */ + void setRate (uint fps); + + bool isActive () { return fps_ != 0; } + bool isPlaying() { return play_; } + + void doPlay(bool yes); + + + /* Lifecycle */ + + DummyPlayer::Process createHandle(); + static void terminate(ProcessImpl* process); + + private: + void doFrame (); ///< periodically invoked while playing + }; + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class DummyPlayerService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/sound/jack-output.cpp b/src/proc/play/sound/jack-output.cpp new file mode 100644 index 000000000..96600bf3b --- /dev/null +++ b/src/proc/play/sound/jack-output.cpp @@ -0,0 +1,252 @@ +/* + DisplayService - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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 "gui/display-service.hpp" + +extern "C" { +#include "common/interfacedescriptor.h" +} + + +namespace gui { + + + + namespace { // hidden local details of the service implementation.... + + + + /* ================== define an lumieraorg_Display instance ======================= */ + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 + ,lumieraorg_DisplayFacade_descriptor + , NULL, NULL, NULL + , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Display"; } + ) + , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", + const char*, (LumieraInterface ifa), + { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } + ) + , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", + const char*, (LumieraInterface ifa), + { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} + ) + , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", + const char*, (LumieraInterface ifa), + { (void)ifa; return "0.1~pre"; } + ) + , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Hermann Vosseler"; } + ) + , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", + const char*, (LumieraInterface ifa), + { (void)ifa; return "Ichthyostega@web.de"; } + ) + , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "Copyright (C) Lumiera.org\n" + " 2009 Hermann Vosseler "; + } + ) + , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", + const char*, (LumieraInterface ifa), + { + (void)ifa; + return + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; + } + ) + , LUMIERA_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", + int, (LumieraInterface ifa), + {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } + ) + , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", + int, (const char* a, const char* b), + {return 0;} ////////////////////////////////////////////TODO define version ordering + ) + ); + + + + + + using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; + typedef lib::SingletonRef::Accessor InstanceRef; + + InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... + + + + LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 + ,lumieraorg_DisplayService + , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) + , NULL /* on open */ + , NULL /* on close */ + , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + try + { + _instance->allocate (slotHandle,true); + } + catch (lumiera::Error&){ /* error state remains set */ } + } + ) + , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", + void, (LumieraDisplaySlot slotHandle), + { + if (!_instance) + { + lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); + return; + } + + REQUIRE (slotHandle); + _instance->allocate (slotHandle,false); + } + ) + , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", + void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), + { + //skipping full checks for performance reasons + REQUIRE (_instance && !lumiera_error_peek()); + + REQUIRE (slotHandle); + DisplayerSlot& slot = _instance->resolve (slotHandle); + slot.put (frame); + } + ) + ); + + + + + } // (End) hidden service impl details + + + + + DisplayService::DisplayService() + : error_("") + , implInstance_(this,_instance) + , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) + { + INFO (progress, "Display Facade opened."); + } + + + + LumieraDisplaySlot + DisplayService::setUp (FrameDestination const& outputDestination) + { + DisplayerTab& slots (_instance->slots_); + return &slots.manage (new DisplayerSlot (outputDestination)); + } + + + + void + DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) + { + REQUIRE (handle); + if (doAllocate) + { + if (handle->put_) + throw lumiera::error::Logic("slot already allocated for output"); + else + // Mark the handle as "allocated" and ready for output: + // Place the function pointer from the C interface into the handle struct. + // calling it will invoke the implementing instance's "put" function + // (see the LUMIERA_INTERFACE_INLINE above in this file!) + handle->put_ = serviceInstance_.get().put; + } + else + handle->put_ = 0; + } + + + + DisplayerSlot& + DisplayService::resolve (LumieraDisplaySlot handle) + { + REQUIRE (handle); + REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); + + return *static_cast (handle); + } + + + + + + /* === DisplayerSlot Implementation === */ + + + DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) + : currBuffer_(0) + { + put_ = 0; // mark as not allocated + hasFrame_.connect (outputDestination); + dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); + } + + + DisplayerSlot::~DisplayerSlot() + { + TRACE (gui_dbg, "Displayer Slot closing..."); + } + + + void + DisplayerSlot::displayCurrentFrame() + { + hasFrame_.emit (currBuffer_); + } + + +} // namespace proc diff --git a/src/proc/play/sound/jack-output.hpp b/src/proc/play/sound/jack-output.hpp new file mode 100644 index 000000000..1dec2c192 --- /dev/null +++ b/src/proc/play/sound/jack-output.hpp @@ -0,0 +1,190 @@ +/* + DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + + Copyright (C) Lumiera.org + 2009, Hermann Vosseler + + 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. + +*/ + +/** @file display-service.hpp + ** A public service provided by the GUI, implementing the lumiera::Display facade interface. + ** It serves two purposes: + ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points + ** and allow to receive frames and dispatch them to the GTK main event loop thread. + ** Conceptually, creating such a slot means providing a possible display for output. + ** - It provides the actual implementation of the Display facade interface, i.e. the function + ** which is to invoked periodically by the playback processes to dispose a new frame into + ** the display. + ** + ** This service is the implementation of a layer separation facade interface. This header defines + ** the interface used to \em provide this service, not to access it. Clients get a specific + ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using + ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access + ** an implementation instance of this service in order to push actual frames up. + ** + ** @see lumiera::Display + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef GUI_DISPLAY_SERVICE_H +#define GUI_DISPLAY_SERVICE_H + + +#include "include/display-facade.h" +#include "common/instancehandle.hpp" +#include "lib/singleton-ref.hpp" +#include "lib/scoped-ptrvect.hpp" + +#include +#include +#include +#include +#include + + +namespace gui { + + using std::string; + using std::vector; + using lumiera::Display; + using Glib::Dispatcher; + + + typedef sigc::slot FrameDestination; + typedef sigc::signal FrameSignal; + + + + /******************************************************************** + * Actual implementation of a single displayer slot. Internally, + * it is connected via the Glib::Dispatcher for outputting frames + * to a viewer widget, which executes within the GTK event thread. + * @note must be created from the GTK event thread. + */ + class DisplayerSlot + : public lumiera_displaySlot, + boost::noncopyable + { + Dispatcher dispatcher_; + FrameSignal hasFrame_; + + LumieraDisplayFrame currBuffer_; + + + public: + DisplayerSlot (FrameDestination const&) ; + ~DisplayerSlot () ; + + /* Implementation-level API to be used by DisplayService */ + + /** receive a frame to be displayed */ + inline void put (LumieraDisplayFrame); + + + private: + /** internal: activated via Dispatcher + * and running in GTK main thread */ + void displayCurrentFrame(); + + }; + + typedef lib::ScopedPtrVect DisplayerTab; + + + + /****************************************************** + * Actual implementation of the DisplayService. + * Creating an instance of this class automatically + * registers the interface lumieraorg_Display with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + * \par + * In addition to the Display interface, this class + * implements an additional service for the GUI, + * allowing actually to set up display slots, which + * then can be handed out to client code in the + * course of the play process for outputting frames. + */ + class DisplayService + : boost::noncopyable + { + + string error_; + DisplayerTab slots_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) + , lumiera::Display + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + + public: + DisplayService(); + ~DisplayService() { + INFO (proc_dbg, "Display service dying..."); + + } + + + /** open a new display, sending frames to the given output destination + * @return handle for this slot, can be used to start a play process. + * NULL handle in case of any error. */ + static LumieraDisplaySlot setUp (FrameDestination const&); + + + /** prepare and the given slot for output + * @param doAllocate allocate when true, else release it + * @throw lumiera::error::Logic when already in use */ + void allocate (LumieraDisplaySlot, bool doAllocate); + + + /** resolve the given display slot handle to yield a ref + * to an actual implementation object. In order to be resolvable, + * the DisplayerSlot needs to be locked (=allocated) for output use. */ + DisplayerSlot& resolve (LumieraDisplaySlot); + + }; + + + + + void + DisplayerSlot::put(LumieraDisplayFrame newFrame) + { + if (newFrame != currBuffer_) + { + currBuffer_ = newFrame; + dispatcher_.emit(); + } + else + { + TRACE (render, "frame dropped?"); + } + } + + + +} // namespace gui +#endif From cb6453afe1fce78bbbee3bb88d8c49b21df12a41 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 23 May 2011 05:46:40 +0200 Subject: [PATCH 003/108] detailed planning how to build the player subsystem key idea is to grow and rework the design of the DummyPlayer to yield the full featured Player --- src/backend/engine/scheduler-frontend.cpp | 9 +- src/backend/engine/scheduler-frontend.hpp | 21 +- src/proc/asset/viewer.cpp | 12 +- src/proc/asset/viewer.hpp | 29 +- src/proc/engine/dispatcher.cpp | 15 +- src/proc/engine/dispatcher.hpp | 28 +- src/proc/engine/engine-service.cpp | 359 +----------------- src/proc/engine/engine-service.hpp | 195 ++++------ .../builder/fixture-change-detector.hpp | 2 +- src/proc/mobject/output-designation.hpp | 4 +- src/proc/mobject/session/generator-mo.cpp | 61 +-- src/proc/mobject/session/generator-mo.hpp | 35 +- src/proc/play/dummy-play-connection.cpp | 358 +---------------- src/proc/play/dummy-play-connection.hpp | 69 ++++ src/proc/play/output-manager.cpp | 223 +---------- src/proc/play/output-manager.hpp | 160 ++------ src/proc/play/play-controller.hpp | 122 +----- src/proc/play/play-process.cpp | 355 +---------------- src/proc/play/play-process.hpp | 123 +----- src/proc/play/play-service.cpp | 292 +------------- src/proc/play/play-service.hpp | 115 +----- src/proc/play/sound/jack-output.cpp | 237 +----------- src/proc/play/sound/jack-output.hpp | 175 +-------- wiki/renderengine.html | 138 +++++-- 24 files changed, 472 insertions(+), 2665 deletions(-) create mode 100644 src/proc/play/dummy-play-connection.hpp diff --git a/src/backend/engine/scheduler-frontend.cpp b/src/backend/engine/scheduler-frontend.cpp index 588d1276b..3cf1698ab 100644 --- a/src/backend/engine/scheduler-frontend.cpp +++ b/src/backend/engine/scheduler-frontend.cpp @@ -1,8 +1,8 @@ /* - RenderEngine - a complete network of processing nodes usable for rendering + SchedulerFrontend - access point to the scheduler within the renderengine Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,8 +21,9 @@ * *****************************************************/ -#include "proc/engine/renderengine.hpp" +#include "backend/engine/scheduler-frontend.hpp" +namespace backend{ namespace engine { @@ -30,4 +31,4 @@ namespace engine { -} // namespace engine +}} // namespace backend::engine diff --git a/src/backend/engine/scheduler-frontend.hpp b/src/backend/engine/scheduler-frontend.hpp index c3030271f..887435191 100644 --- a/src/backend/engine/scheduler-frontend.hpp +++ b/src/backend/engine/scheduler-frontend.hpp @@ -1,8 +1,8 @@ /* - RENDERENGINE.hpp - a complete network of processing nodes usable for rendering + SCHEDULER-FRONTEND.hpp - access point to the scheduler within the renderengine Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,17 +21,15 @@ */ -#ifndef ENGINE_RENDERENGINE_H -#define ENGINE_RENDERENGINE_H - -#include - -#include "proc/engine/rendergraph.hpp" +#ifndef BACKEND_ENGINE_SCHEDULER_FRONTEND_H +#define BACKEND_ENGINE_SCHEDULER_FRONTEND_H -using std::list; + +//using std::list; +namespace backend{ namespace engine { @@ -42,7 +40,7 @@ namespace engine { * render operations are mostly implemented by the backend * ////////TODO WIP as of 12/2010 */ - class RenderEngine : public RenderGraph + class SchedulerFrontend { public: ///// TODO: find out about the public operations @@ -50,9 +48,8 @@ namespace engine { // but is a subsystem separate of the sesison. private: - list renderSegments; }; -} // namespace engine +}} // namespace backend::engine #endif diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp index bb2f9e36e..b5c74ad30 100644 --- a/src/proc/asset/viewer.cpp +++ b/src/proc/asset/viewer.cpp @@ -1,8 +1,8 @@ /* - Timeline - independent top-level element of the Session + Viewer - asset corresponding to a viewer element in the GUI Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,17 +21,17 @@ * *****************************************************/ -#include "proc/asset/timeline.hpp" +#include "proc/asset/viewer.hpp" //#include "proc/mobject/session/track.hpp" //#include "proc/mobject/placement.hpp" //#include "proc/mobject/session/mobjectfactory.hpp" -#include "proc/mobject/session/binding.hpp" -#include "proc/assetmanager.hpp" +//#include "proc/mobject/session/binding.hpp" +//#include "proc/assetmanager.hpp" namespace asset { - using lib::AutoRegistered; +// using lib::AutoRegistered; diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp index 7bf168e1e..5e5890552 100644 --- a/src/proc/asset/viewer.hpp +++ b/src/proc/asset/viewer.hpp @@ -1,8 +1,8 @@ /* - TIMELINE.hpp - independent top-level element of the Session + VIEWER.hpp - asset corresponding to a viewer element in the GUI Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,25 +21,8 @@ */ -/** @file timeline.hpp - ** Top level structural element within the session. - ** Each Lumiera session may contain multiple top level timeline containers, - ** which at the same time act as structural asset and as part of the public - ** session API exposed to clients for discovering the session contents. - ** Actually, Timelines are facade objects, delegating the implementation to - ** the BindingMO, the Axis and the Sequences/Tracks. - ** - ** Contrary to usual habits in video/sound editing software, in Lumiera the - ** tracks are \em not part of the timeline, but rather attached directly to - ** the sequence container. To be usable, a timeline needs a binding to refer - ** to such a sequence, but this sequence may be bound into multiple timelines - ** or even virtual clips simultaneously. - ** - ** Like every structural asset, the creation of timelines happens automatically - ** on referral; Timelines can be queried from the StructFactory, providing additional - ** requested capabilities. Commonly clients will retrieve a given timeline by query - ** on the name-ID of the timeline: \c Struct::retrieve(Query("id(theName).")) - ** Additionally, the binding to a specific sequence may be established alongside: +/** @file viewer.hpp + ** structural element within the session. ** \c "timeline(theTimelineName),bindSequence(theTimelineName,sequenceID)." ** ** @see Session @@ -49,8 +32,8 @@ */ -#ifndef ASSET_TIMELINE_H -#define ASSET_TIMELINE_H +#ifndef ASSET_VIEWER_H +#define ASSET_VIEWER_H #include "proc/asset/struct.hpp" //#include "proc/mobject/mobject.hpp" diff --git a/src/proc/engine/dispatcher.cpp b/src/proc/engine/dispatcher.cpp index fefaf7c63..795b56c68 100644 --- a/src/proc/engine/dispatcher.cpp +++ b/src/proc/engine/dispatcher.cpp @@ -1,8 +1,8 @@ /* - RenderGraph - render network corresponding to one segment of the timeline + Dispatcher - translating calculation streams into frame jobs Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,15 +21,10 @@ * *****************************************************/ -#include "proc/engine/rendergraph.hpp" -#include "lib/frameid.hpp" -#include "proc/state.hpp" +#include "proc/engine/dispatcher.hpp" +//#include "lib/frameid.hpp" +//#include "proc/state.hpp" -namespace lumiera { - - /** storage for the unique node-ID counter */ - ulong NodeID::currID (0); -} namespace engine { diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index a77eac68b..5b4509953 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -1,8 +1,8 @@ /* - RENDERGRAPH.hpp - render network corresponding to one segment of the timeline + DISPATCHER.hpp - translating calculation streams into frame jobs Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,8 +21,8 @@ */ -#ifndef ENGINE_RENDERGRAPH_H -#define ENGINE_RENDERGRAPH_H +#ifndef PROC_ENGINE_DISPATCHER_H +#define PROC_ENGINE_DISPATCHER_H #include "proc/common.hpp" #include "proc/state.hpp" @@ -32,28 +32,26 @@ namespace engine { - using lib::time::TimeSpan; - using lib::time::FSecs; - using lib::time::Time; - - class ExitNode; +// using lib::time::TimeSpan; +// using lib::time::FSecs; +// using lib::time::Time; +// +// class ExitNode; /** - * @todo likely to be reworked into the engine backbone /////////////TODO WIP as of 12/2010 + * @todo */ - class RenderGraph + class Dispatcher { protected: - ExitNode * output; - /** timerange covered by this RenderGraph */ TimeSpan segment_; public: - RenderGraph() + Dispatcher() : segment_(Time::ZERO, FSecs(5)) { - UNIMPLEMENTED ("anything regarding the Fixture datastructure"); + UNIMPLEMENTED ("anything regarding the Engine backbone"); } }; diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index ea443f709..23078f68c 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + EngineService - primary service access point for using the renderengine Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,362 +21,33 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" -#include "lib/singleton.hpp" +#include "proc/engine/engine-service.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} - -#include -#include -#include -#include +//#include +//#include +//#include +//#include namespace proc { - namespace play{ +namespace engine{ - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; namespace { // hidden local details of the service implementation.... - - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ) - , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - - } // (End) hidden service impl details - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } + /** */ - - } // namespace play - -} // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera +}} // namespace proc::engine diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 75e0f3d78..599f4fe40 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + ENGINE-SERVICE.hpp - primary service access point for using the renderengine Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,142 +20,83 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. +/** @file engine-service.hpp + ** A public service provided by ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_ENGINE_ENGINE_SERVICE_H +#define PROC_ENGINE_ENGINE_SERVICE_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" - -#include -#include -#include +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include +//#include +//#include namespace proc { - namespace play { - - using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - - class DummyImageGenerator; - class TickService; - - - /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. - */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; - - - - /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - */ - class DummyPlayerService - : boost::noncopyable - { - - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); - - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); - - }; - - - - - } // namespace play +namespace play { +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class EngineService + : boost::noncopyable + { + + string error_; + Subsys::SigTerm notifyTermination_; + + + /* === Interface Lifecycle === */ + + typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) + , DummyPlayer + > ServiceInstanceHandle; + + lib::SingletonRef implInstance_; + ServiceInstanceHandle serviceInstance_; + + public: + DummyPlayerService(Subsys::SigTerm terminationHandle); + + ~DummyPlayerService() { notifyTermination_(&error_); } + + + + /** conceptually, this serves as implementation + * of the DummyPlayer#start() function. But because + * this function sits \em behind the interface, it + * just returns an impl pointer. */ + ProcessImpl* start (LumieraDisplaySlot viewerHandle); + + }; + + + + +} // namespace play } // namespace proc #endif diff --git a/src/proc/mobject/builder/fixture-change-detector.hpp b/src/proc/mobject/builder/fixture-change-detector.hpp index bf836d7d1..e2da29419 100644 --- a/src/proc/mobject/builder/fixture-change-detector.hpp +++ b/src/proc/mobject/builder/fixture-change-detector.hpp @@ -30,7 +30,7 @@ ** and specific segments of the fixture. ** Together, these allow to identify those ongoing processes which need to be cancelled ** or restarted, because their results might be tainted by the changes induced by the - ** build process. Typically, these detection process runs just before commiting the + ** build process. Typically, these detection process runs just before committing the ** newly built fixture datastructure. ** ** @todo WIP-WIP-WIP as of 12/2010 diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp index 43aa6f5b8..fd571e56a 100644 --- a/src/proc/mobject/output-designation.hpp +++ b/src/proc/mobject/output-designation.hpp @@ -46,7 +46,7 @@ namespace mobject { /** * Descriptor to denote the desired target of produced media data. * OutputDesignation is always an internal and relative specification - * and boils down to referring an asset::Pipe by ID. In order to get + * and boils down to referring an asset::Pipe by ID. In order to become * actually effective, some object within the model additionally * needs to \em claim this pipe-ID, meaning that this object * states to root and represent this pipe. When the builder @@ -54,7 +54,7 @@ namespace mobject { * an actual stream connection will be wired in the * processing node network. * - * @todo couldn't the inline buffer be "downgraded" to InPlaceBuffer ?? + * @todo couldn't the inline buffer be "downgraded" to InPlaceBuffer or PolymorphicValue?? * Seemingly we never-ever need to re-discover the erased type of the embedded spec. * Thus for this to work, we'd just need to add an "empty" spec ///////////////////TICKET #723 */ diff --git a/src/proc/mobject/session/generator-mo.cpp b/src/proc/mobject/session/generator-mo.cpp index 426cd8e57..728e58b94 100644 --- a/src/proc/mobject/session/generator-mo.cpp +++ b/src/proc/mobject/session/generator-mo.cpp @@ -1,8 +1,8 @@ /* - Clip - a Media Clip + GeneratorMO - a (Test)data generator Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,15 +21,15 @@ * *****************************************************/ -#include "proc/mobject/session/clip.hpp" -#include "proc/assetmanager.hpp" -#include "proc/asset/media.hpp" -#include "proc/asset/clip.hpp" -#include "lib/time/mutation.hpp" -#include "lib/util.hpp" +#include "proc/mobject/session/generator-mo.hpp" +//#include "proc/assetmanager.hpp" +//#include "proc/asset/media.hpp" +//#include "proc/asset/clip.hpp" +//#include "lib/time/mutation.hpp" +//#include "lib/util.hpp" -using lib::time::Mutation; -using util::isnil; +//using lib::time::Mutation; +//using util::isnil; namespace mobject { namespace session { @@ -37,47 +37,6 @@ namespace session { /** new clip-MO linked with the given asset::Clip. * Initially, this clip will cover the whole source media length. */ - Clip::Clip (const asset::Clip& clipDef, const Media& mediaDef) - : mediaDef_(mediaDef) - , clipDef_(clipDef) - { - setupLength(); - throwIfInvalid(); - } - - - - /** implementing the common MObject self test. - * Length definition is consitent, underlying - * media def is accessible etc. */ - bool - Clip::isValid () const - { - TODO ("check consistency of clip length def, implies accessing the underlying media def"); - return !isnil(length_); - } - - - void - Clip::setupLength() - { - TODO ("really calculate the length of a clip and set length field"); - this->length_.accept (Mutation::changeDuration(mediaDef_.getLength())); - } - - - PMedia - Clip::getMedia () const - { - return asset::AssetManager::wrap (mediaDef_); - } - - - PClipAsset - Clip::findClipAsset () const - { - return asset::AssetManager::wrap (clipDef_); - } diff --git a/src/proc/mobject/session/generator-mo.hpp b/src/proc/mobject/session/generator-mo.hpp index 52e134926..876a497f3 100644 --- a/src/proc/mobject/session/generator-mo.hpp +++ b/src/proc/mobject/session/generator-mo.hpp @@ -1,8 +1,8 @@ /* - CLIP.hpp - a Media Clip + GENERATOR-MO.hpp - a (Test)data generator Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_SESSION_CLIP_H -#define MOBJECT_SESSION_CLIP_H +#ifndef MOBJECT_SESSION_GENERATOR_MO_H +#define MOBJECT_SESSION_GENERATOR_MO_H #include "proc/mobject/session/abstractmo.hpp" #include "lib/time/timevalue.hpp" @@ -44,24 +44,15 @@ namespace session { /** - * A user visible/editable Clip is a reference to a contiguous - * sequence of media data loaded as Asset into the current Session. - * As such, it is a virtual (non destructive) cut or edit of the - * source material and can be placed into the Session to be rendered - * into the output. The actual media type of a clip will be derived - * at runtime by resolving this reference to the underlying Asset. - * - * @todo define how to denote Time positions /lengths. This is tricky, - * because it depends on the actual media type, and we want to encapsulate - * all these details as much as possible. + * A lksjaf */ - class Clip + class GeneratorMO : public AbstractMO { string initShortID() const { - return buildShortID("Clip"); + return buildShortID("Generator"); } void setupLength(); @@ -79,24 +70,14 @@ namespace session { account when breaking circular references. */ - const Media & mediaDef_; - const asset::Clip & clipDef_; - Clip (const asset::Clip&, const Media&); + GeneratorMO (); friend class MObjectFactory; public: bool isValid() const; - /** access the underlying media asset */ - PMedia getMedia () const; - - /** locate the corresponding asset - * representing this clip or the whole - * compound in case of a multichannel clip - */ - PClipAsset findClipAsset () const; DEFINE_PROCESSABLE_BY (builder::BuilderTool); diff --git a/src/proc/play/dummy-play-connection.cpp b/src/proc/play/dummy-play-connection.cpp index ea443f709..72dab3d41 100644 --- a/src/proc/play/dummy-play-connection.cpp +++ b/src/proc/play/dummy-play-connection.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + DummyPlayConnection.hpp - simplified test setup for playback Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,362 +21,38 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" -#include "lib/singleton.hpp" +#include "proc/play/dummy-play-connection.hpp" +//#include "proc/play/dummy-image-generator.hpp" +//#include "proc/play/tick-service.hpp" +//#include "lib/singleton.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} - -#include -#include -#include -#include +//#include +//#include +//#include +//#include namespace proc { namespace play{ - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; namespace { // hidden local details of the service implementation.... - - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ) - , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - - } // (End) hidden service impl details - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } - - + /** */ + } // namespace play } // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp new file mode 100644 index 000000000..7186987df --- /dev/null +++ b/src/proc/play/dummy-play-connection.hpp @@ -0,0 +1,69 @@ +/* + DUMMY-PLAY-CONNECTION.hpp - simplified test setup for playback + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + +/** @file dummy-play-connection.hpp + ** to \em provide this service, not to access it. + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#ifndef PROC_PLAY_DUMMY_PLAY_CONNECTION_H +#define PROC_PLAY_DUMMY_PLAY_CONNECTION_H + + +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include +//#include +//#include + + +namespace proc { + namespace play { + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + /******************************************************************** + */ + class DummyPlayConnection + : boost::noncopyable + { + + }; + + + + + } // namespace play + +} // namespace proc +#endif diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp index 96600bf3b..39887ec80 100644 --- a/src/proc/play/output-manager.cpp +++ b/src/proc/play/output-manager.cpp @@ -1,8 +1,8 @@ /* - DisplayService - service providing access to a display for outputting frames + OutputManager - handling all the real external output connections Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,232 +21,23 @@ * *****************************************************/ -#include "gui/display-service.hpp" - -extern "C" { -#include "common/interfacedescriptor.h" -} +#include "play/output-manager.hpp" -namespace gui { + +namespace play { namespace { // hidden local details of the service implementation.... - - - /* ================== define an lumieraorg_Display instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DisplayFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Display"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", - const char*, (LumieraInterface ifa), - { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ) - , LUMIERA_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... - - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 - ,lumieraorg_DisplayService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - try - { - _instance->allocate (slotHandle,true); - } - catch (lumiera::Error&){ /* error state remains set */ } - } - ) - , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - _instance->allocate (slotHandle,false); - } - ) - , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", - void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), - { - //skipping full checks for performance reasons - REQUIRE (_instance && !lumiera_error_peek()); - - REQUIRE (slotHandle); - DisplayerSlot& slot = _instance->resolve (slotHandle); - slot.put (frame); - } - ) - ); - - - - } // (End) hidden service impl details - DisplayService::DisplayService() - : error_("") - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) - { - INFO (progress, "Display Facade opened."); - } + OutputSlot::~OutputSlot() { } - LumieraDisplaySlot - DisplayService::setUp (FrameDestination const& outputDestination) - { - DisplayerTab& slots (_instance->slots_); - return &slots.manage (new DisplayerSlot (outputDestination)); - } - - - - void - DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) - { - REQUIRE (handle); - if (doAllocate) - { - if (handle->put_) - throw lumiera::error::Logic("slot already allocated for output"); - else - // Mark the handle as "allocated" and ready for output: - // Place the function pointer from the C interface into the handle struct. - // calling it will invoke the implementing instance's "put" function - // (see the LUMIERA_INTERFACE_INLINE above in this file!) - handle->put_ = serviceInstance_.get().put; - } - else - handle->put_ = 0; - } - - - - DisplayerSlot& - DisplayService::resolve (LumieraDisplaySlot handle) - { - REQUIRE (handle); - REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); - - return *static_cast (handle); - } - - - - - - /* === DisplayerSlot Implementation === */ - - - DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) - : currBuffer_(0) - { - put_ = 0; // mark as not allocated - hasFrame_.connect (outputDestination); - dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); - } - - - DisplayerSlot::~DisplayerSlot() - { - TRACE (gui_dbg, "Displayer Slot closing..."); - } - - - void - DisplayerSlot::displayCurrentFrame() - { - hasFrame_.emit (currBuffer_); - } - - -} // namespace proc +} // namespace play diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index 1dec2c192..b697f35d6 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -1,8 +1,8 @@ /* - DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + OUTPUT-MANAGER.hpp - handling all the real external output connections Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,171 +20,65 @@ */ -/** @file display-service.hpp - ** A public service provided by the GUI, implementing the lumiera::Display facade interface. - ** It serves two purposes: - ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points - ** and allow to receive frames and dispatch them to the GTK main event loop thread. - ** Conceptually, creating such a slot means providing a possible display for output. - ** - It provides the actual implementation of the Display facade interface, i.e. the function - ** which is to invoked periodically by the playback processes to dispose a new frame into - ** the display. - ** - ** This service is the implementation of a layer separation facade interface. This header defines - ** the interface used to \em provide this service, not to access it. Clients get a specific - ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using - ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access - ** an implementation instance of this service in order to push actual frames up. +/** @file output-manager.hpp + ** A global service to handle all external output connections. ** - ** @see lumiera::Display - ** @see lumiera::DummyPlayer - ** @see gui::PlaybackController usage example + ** @see output-manager-test.cpp ////TODO */ -#ifndef GUI_DISPLAY_SERVICE_H -#define GUI_DISPLAY_SERVICE_H +#ifndef PROC_PLAY_OUTPUT_MANAGER_H +#define PROC_PLAY_OUTPUT_MANAGER_H -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" -#include "lib/scoped-ptrvect.hpp" +#include "lib/error.hpp" -#include -#include #include -#include -#include +//#include +//#include -namespace gui { +namespace play { - using std::string; - using std::vector; - using lumiera::Display; - using Glib::Dispatcher; +//using std::string; +//using std::vector; - typedef sigc::slot FrameDestination; - typedef sigc::signal FrameSignal; /******************************************************************** - * Actual implementation of a single displayer slot. Internally, - * it is connected via the Glib::Dispatcher for outputting frames - * to a viewer widget, which executes within the GTK event thread. - * @note must be created from the GTK event thread. + * Interface: Generic output sink. + * + * @todo write type comment */ - class DisplayerSlot - : public lumiera_displaySlot, - boost::noncopyable + class OutputSlot + : boost::noncopyable { - Dispatcher dispatcher_; - FrameSignal hasFrame_; - - LumieraDisplayFrame currBuffer_; - - public: - DisplayerSlot (FrameDestination const&) ; - ~DisplayerSlot () ; - - /* Implementation-level API to be used by DisplayService */ - - /** receive a frame to be displayed */ - inline void put (LumieraDisplayFrame); - + virtual ~OutputSlot(); private: - /** internal: activated via Dispatcher - * and running in GTK main thread */ - void displayCurrentFrame(); }; - typedef lib::ScopedPtrVect DisplayerTab; +//typedef lib::ScopedPtrVect DisplayerTab; /****************************************************** - * Actual implementation of the DisplayService. - * Creating an instance of this class automatically - * registers the interface lumieraorg_Display with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - * \par - * In addition to the Display interface, this class - * implements an additional service for the GUI, - * allowing actually to set up display slots, which - * then can be handed out to client code in the - * course of the play process for outputting frames. + * Management of external Output connections. + * + * @todo write Type comment */ - class DisplayService + class OutputManager : boost::noncopyable { - - string error_; - DisplayerTab slots_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) - , lumiera::Display - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - - public: - DisplayService(); - ~DisplayService() { - INFO (proc_dbg, "Display service dying..."); - - } - - - /** open a new display, sending frames to the given output destination - * @return handle for this slot, can be used to start a play process. - * NULL handle in case of any error. */ - static LumieraDisplaySlot setUp (FrameDestination const&); - - - /** prepare and the given slot for output - * @param doAllocate allocate when true, else release it - * @throw lumiera::error::Logic when already in use */ - void allocate (LumieraDisplaySlot, bool doAllocate); - - - /** resolve the given display slot handle to yield a ref - * to an actual implementation object. In order to be resolvable, - * the DisplayerSlot needs to be locked (=allocated) for output use. */ - DisplayerSlot& resolve (LumieraDisplaySlot); - + OutputManager() {} }; + - - - void - DisplayerSlot::put(LumieraDisplayFrame newFrame) - { - if (newFrame != currBuffer_) - { - currBuffer_ = newFrame; - dispatcher_.emit(); - } - else - { - TRACE (render, "frame dropped?"); - } - } - - - -} // namespace gui +} // namespace play #endif diff --git a/src/proc/play/play-controller.hpp b/src/proc/play/play-controller.hpp index 75e0f3d78..239ffa0c5 100644 --- a/src/proc/play/play-controller.hpp +++ b/src/proc/play/play-controller.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + PLAY-CONTROLLER.hpp - frontend handle to control an play process Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,141 +20,49 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. +/** @file play-controller.hpp ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_PLAY_PLAY_CONTROLLER_H +#define PROC_PLAY_PLAY_CONTROLLER_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" +#include "proc/play/play-service.hpp" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" #include -#include -#include +//#include +//#include namespace proc { namespace play { using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - - class DummyImageGenerator; - class TickService; /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; - - - - /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - */ - class DummyPlayerService + class PlayController : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); - - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); + private: }; + + } // namespace play } // namespace proc diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index ea443f709..3859244a5 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + PlayProcess.hpp - state frame for an ongoing play/render process Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,212 +21,27 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" -#include "lib/singleton.hpp" +#include "proc/play/play-process.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} - -#include -#include -#include -#include +//#include +//#include +//#include +//#include namespace proc { namespace play{ - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; +// using std::string; +// using lumiera::Subsys; +// using std::auto_ptr; +// using boost::scoped_ptr; +// using std::tr1::bind; namespace { // hidden local details of the service implementation.... - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ) - , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - } // (End) hidden service impl details @@ -234,149 +49,7 @@ namespace proc { - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } + /** */ - - } // namespace play - -} // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera +}} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 75e0f3d78..f9d0ea85c 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + PLAY-PROCESS.hpp - state frame for an ongoing play/render process Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,95 +20,34 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. - ** +/** @file play-process.hpp + * ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_PLAY_PLAY_PROCESS_H +#define PROC_PLAY_PLAY_PROCESS_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" - -#include -#include -#include +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +// +//#include +//#include +//#include namespace proc { namespace play { - using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - - class DummyImageGenerator; - class TickService; - - - /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. - */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; @@ -120,35 +59,11 @@ namespace proc { * a forwarding proxy within the application core to * route calls through this interface. */ - class DummyPlayerService + class PlayerProcess : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); - - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index ea443f709..1b8cc463e 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -1,8 +1,8 @@ /* - DummyPlayerService - access point and service implementing a dummy test player + PlayService - interface: render- and playback control Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,19 +21,14 @@ * *****************************************************/ -#include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" +#include "proc/play/play-service.hpp" #include "lib/singleton.hpp" -extern "C" { -#include "common/interfacedescriptor.h" -} #include -#include -#include -#include +//#include +//#include +//#include @@ -99,284 +94,13 @@ namespace proc { /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DummyPlayerFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\305\162\202\240\075\316\146\100\314\152\075\343\372\065\226\307", - const char*, (LumieraInterface ifa), - { (void)ifa; return "DummyPlayer"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\317\045\366\076\064\072\156\274\220\346\262\207\062\367\057\232", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Proc Interface: dummy player to test integration with the GUI"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\136\225\033\362\161\251\300\256\117\072\171\102\235\004\235\200", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\212\146\344\127\124\116\101\205\211\174\322\241\162\122\023\165", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\064\226\072\300\054\345\042\357\337\226\155\025\306\051\117\105", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\041\075\220\112\246\304\261\135\003\135\060\202\230\327\303\206", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\232\305\163\271\174\025\270\075\012\201\331\256\327\375\066\210", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\136\136\073\173\145\357\151\062\040\013\323\272\051\352\305\060", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ) - , LUMIERA_INTERFACE_INLINE (state, "\224\251\004\001\165\140\116\246\126\311\115\234\023\026\331\350", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\267\155\303\046\353\222\323\014\145\027\043\100\370\311\257\126", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DummyPlayer implementation... - - typedef ProcessImpl* ProcP; - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_DummyPlayer, 0 - ,lumieraorg_DummyPlayerService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DummyPlayerFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (startPlay, "\143\323\102\155\051\006\235\004\037\310\354\121\176\142\342\210", - LumieraPlayProcess, (LumieraDisplaySlot viewerHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return 0; - } - - return static_cast (_instance->start(viewerHandle)); - } - ) - , LUMIERA_INTERFACE_INLINE (togglePlay, "\275\157\316\220\210\053\226\134\057\016\273\265\240\053\112\307", - void, (LumieraPlayProcess handle, bool doPlay), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - proc->doPlay(doPlay); - } - ) - , LUMIERA_INTERFACE_INLINE (terminate, "\005\265\115\021\076\143\010\215\373\252\370\174\235\136\340\004", - void, (LumieraPlayProcess handle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (handle); - ProcP proc = static_cast (handle); - - ProcessImpl::terminate (proc); - } - ) - ); - - - } // (End) hidden service impl details - DummyPlayerService::DummyPlayerService (Subsys::SigTerm terminationHandle) - : error_("") - , notifyTermination_(terminationHandle) - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_DummyPlayer, 0, lumieraorg_DummyPlayerService)) - { - INFO (progress, "DummyPlayer Facade opened."); - } - - - - - /** @par implementation note - * A new process (implementation) is created, configured - * and started here. This may include spawning a thread or - * allocating a timer. The newly created process is self-contained - * and will be just handed out, without caring for its lifecycle. - * If client code accesses this function via the plain C interface, - * the client is responsible for terminating this process, whereas - * when using the C++ interface, you'll get a Handle object which - * manages the lifecycle automatically. - */ - ProcessImpl* - DummyPlayerService::start (LumieraDisplaySlot viewerHandle) - { - auto_ptr newProcess (new ProcessImpl (viewerHandle)); - - REQUIRE (!newProcess->isActive()); - newProcess->setRate(25); - - return newProcess.release(); - } - - - - - - - /* === Process Implementation === */ - - - ProcessImpl::ProcessImpl(LumieraDisplaySlot viewerHandle) - : fps_(0) - , play_(false) - , display_(Display::facade().getHandle (viewerHandle)) - , imageGen_(0) - , tick_(new TickService (bind (&ProcessImpl::doFrame, this))) - { } - - - ProcessImpl::~ProcessImpl() - { - INFO (proc_dbg, "Playback process halted..."); - } - - - void - ProcessImpl::terminate (ProcessImpl* process) ///< deleter function for lib::Handle - { - if (process) - delete process; - } - - - - DummyPlayer::Process - ProcessImpl::createHandle() - { - DummyPlayer::Process handle; - handle.activate(this, &terminate); // note the deleter function... - return handle; - } - - - - void - ProcessImpl::setRate (uint fps) - { - REQUIRE (fps==0 || fps_==0 ); - REQUIRE (fps==0 || !play_ ); - REQUIRE (tick_); - - fps_ = fps; - play_ = (fps != 0); - - if (play_) - imageGen_.reset(new DummyImageGenerator(fps)); - - // callbacks with given frequency, starting now - tick_->activate(fps); - } - - - - void - ProcessImpl::doPlay(bool yes) - { - REQUIRE (isActive()); - tick_->activate (yes? fps_:0); - play_ = yes; - } - - - - void - ProcessImpl::doFrame() - { - REQUIRE (isActive()); - ASSERT (imageGen_); - - if (play_) - display_(imageGen_->next()); - else - display_(imageGen_->current()); - } + /** */ - - } // namespace play - -} // namespace proc - - - - - -namespace lumiera { /* === Forwarding function(s) on the Process handle === */ - - void DummyPlayer::Process::play(bool yes) { impl().doPlay(yes); } - - - - - - /** @internal intended for use by main(). */ - lumiera::Subsys& - DummyPlayer::getDescriptor() - { - return proc::play::theDummyPlayerDescriptor(); - } - - // emit the vtable here into this translation unit within liblumieraproc.so ... - DummyPlayer::~DummyPlayer() { } - - -} // namespace lumiera +}} // namespace proc::play diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index 75e0f3d78..ee9a04188 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -1,8 +1,8 @@ /* - DUMMY-PLAYER-SERVICE.hpp - service implementing a dummy test player + PLAY-SERVICE.hpp - interface: render- and playback control Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,35 +20,23 @@ */ -/** @file dummy-player-service.hpp - ** A public service provided by the Proc-Layer, implementing a dummy/mockup playback process. - ** This is a design sketch; Lumiera isn't able to generate rendered output as of 2/2009. The - ** idea is, that for each ongoing calculation process, there is a ProcessImpl instance holding - ** the necessary handles and allocations and providing an uniform API to the client side. - ** Especially, this ProcessImpl holds a TickService, which generates periodic callbacks, and - ** it uses an output handle (functor) to push the generated frames up. - ** - ** This service is the implementation of a layer separation facade interface. Clients should use - ** proc::play::DummyPlayer#facade to access this service. This header defines the interface used - ** to \em provide this service, not to access it. +/** @file play-service.hpp + ** Player subsystem...... ** ** @see lumiera::DummyPlayer ** @see gui::PlaybackController usage example */ -#ifndef PROC_DUMMYPLAYER_SERVICE_H -#define PROC_DUMMYPLAYER_SERVICE_H +#ifndef PROC_PLAY_PLAY_SERVICE_H +#define PROC_PLAY_PLAY_SERVICE_H -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" +#include "lib/error.hpp" #include -#include -#include +//#include +//#include namespace proc { @@ -60,95 +48,22 @@ namespace proc { using lumiera::DummyPlayer; - class DummyImageGenerator; - class TickService; - - - /******************************************************************** - * Actual implementation of a single (dummy) playback process. - * The DummyPlayerService (see below) maintains a collection of such - * actively running playback processes, while the client code gets - * DummyPlayer::Process handles to track any ongoing use. Users of - * the plain C interface get a direct bare pointer to the respective - * ProcessImpl instance and have to manage the lifecycle manually. - */ - class ProcessImpl - : public lumiera_playprocess, - boost::noncopyable - { - uint fps_; - bool play_; - - Display::Sink display_; - boost::scoped_ptr imageGen_; - boost::scoped_ptr tick_; - - - public: - ProcessImpl(LumieraDisplaySlot) ; - ~ProcessImpl() ; - - - /* Implementation-level API */ - - /** activate a playback process - * with given specification */ - void setRate (uint fps); - - bool isActive () { return fps_ != 0; } - bool isPlaying() { return play_; } - - void doPlay(bool yes); - - - /* Lifecycle */ - - DummyPlayer::Process createHandle(); - static void terminate(ProcessImpl* process); - - private: - void doFrame (); ///< periodically invoked while playing - }; +// class DummyImageGenerator; +// class TickService; /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. + * Interface: Player subsystem. */ - class DummyPlayerService + class PlayService : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - public: - DummyPlayerService(Subsys::SigTerm terminationHandle); + PlayService(Subsys::SigTerm terminationHandle); - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); + ~PlayService() { notifyTermination_(&error_); } }; diff --git a/src/proc/play/sound/jack-output.cpp b/src/proc/play/sound/jack-output.cpp index 96600bf3b..4fc576d0d 100644 --- a/src/proc/play/sound/jack-output.cpp +++ b/src/proc/play/sound/jack-output.cpp @@ -1,8 +1,8 @@ /* - DisplayService - service providing access to a display for outputting frames + JackOutput - Jack audio connection client Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,232 +21,11 @@ * *****************************************************/ -#include "gui/display-service.hpp" - -extern "C" { -#include "common/interfacedescriptor.h" -} - - -namespace gui { +#include "proc/play/sound/jack-output.hpp" + +namespace proc { +namespace play { +namespace sound { - - namespace { // hidden local details of the service implementation.... - - - - /* ================== define an lumieraorg_Display instance ======================= */ - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0 - ,lumieraorg_DisplayFacade_descriptor - , NULL, NULL, NULL - , LUMIERA_INTERFACE_INLINE (name, "\323\343\324\023\064\216\120\201\073\056\366\020\110\263\060\023", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Display"; } - ) - , LUMIERA_INTERFACE_INLINE (brief, "\305\026\070\133\033\357\014\202\203\270\174\072\341\256\226\235", - const char*, (LumieraInterface ifa), - { (void)ifa; return "UI Interface: service for outputting frames to a viewer or display"; } - ) - , LUMIERA_INTERFACE_INLINE (homepage, "\170\104\246\175\123\144\332\312\315\263\071\170\164\213\024\275", - const char*, (LumieraInterface ifa), - { (void)ifa; return "http://www.lumiera.org/develompent.html" ;} - ) - , LUMIERA_INTERFACE_INLINE (version, "\265\343\045\346\110\241\276\111\217\120\155\246\230\341\344\124", - const char*, (LumieraInterface ifa), - { (void)ifa; return "0.1~pre"; } - ) - , LUMIERA_INTERFACE_INLINE (author, "\302\027\122\045\301\166\046\236\257\253\144\035\105\166\070\103", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Hermann Vosseler"; } - ) - , LUMIERA_INTERFACE_INLINE (email, "\074\013\020\161\075\135\302\265\260\000\301\147\116\355\035\261", - const char*, (LumieraInterface ifa), - { (void)ifa; return "Ichthyostega@web.de"; } - ) - , LUMIERA_INTERFACE_INLINE (copyright, "\037\232\153\100\114\103\074\342\164\132\370\210\372\164\115\275", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "Copyright (C) Lumiera.org\n" - " 2009 Hermann Vosseler "; - } - ) - , LUMIERA_INTERFACE_INLINE (license, "\026\243\334\056\125\245\315\311\155\375\262\344\007\076\341\254", - const char*, (LumieraInterface ifa), - { - (void)ifa; - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ) - , LUMIERA_INTERFACE_INLINE (state, "\243\302\332\160\060\272\155\334\212\256\303\141\160\063\164\154", - int, (LumieraInterface ifa), - {(void)ifa; return LUMIERA_INTERFACE_EXPERIMENTAL; } - ) - , LUMIERA_INTERFACE_INLINE (versioncmp, "\363\125\123\060\231\147\053\017\131\341\105\157\231\273\334\136", - int, (const char* a, const char* b), - {return 0;} ////////////////////////////////////////////TODO define version ordering - ) - ); - - - - - - using lumiera::facade::LUMIERA_ERROR_FACADE_LIFECYCLE; - typedef lib::SingletonRef::Accessor InstanceRef; - - InstanceRef _instance; ///< a backdoor for the C Language impl to access the actual DisplayService implementation... - - - - LUMIERA_INTERFACE_INSTANCE (lumieraorg_Display, 0 - ,lumieraorg_DisplayService - , LUMIERA_INTERFACE_REF(lumieraorg_interfacedescriptor, 0, lumieraorg_DisplayFacade_descriptor) - , NULL /* on open */ - , NULL /* on close */ - , LUMIERA_INTERFACE_INLINE (allocate, "\177\221\146\253\255\161\160\137\015\005\263\362\307\022\243\365", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - try - { - _instance->allocate (slotHandle,true); - } - catch (lumiera::Error&){ /* error state remains set */ } - } - ) - , LUMIERA_INTERFACE_INLINE (release, "\166\374\106\313\011\142\115\161\111\110\376\016\346\115\240\364", - void, (LumieraDisplaySlot slotHandle), - { - if (!_instance) - { - lumiera_error_set(LUMIERA_ERROR_FACADE_LIFECYCLE, 0); - return; - } - - REQUIRE (slotHandle); - _instance->allocate (slotHandle,false); - } - ) - , LUMIERA_INTERFACE_INLINE (put, "\340\062\234\227\152\131\370\272\146\207\224\015\361\070\252\135", - void, (LumieraDisplaySlot slotHandle, LumieraDisplayFrame frame), - { - //skipping full checks for performance reasons - REQUIRE (_instance && !lumiera_error_peek()); - - REQUIRE (slotHandle); - DisplayerSlot& slot = _instance->resolve (slotHandle); - slot.put (frame); - } - ) - ); - - - - - } // (End) hidden service impl details - - - - - DisplayService::DisplayService() - : error_("") - , implInstance_(this,_instance) - , serviceInstance_( LUMIERA_INTERFACE_REF (lumieraorg_Display, 0, lumieraorg_DisplayService)) - { - INFO (progress, "Display Facade opened."); - } - - - - LumieraDisplaySlot - DisplayService::setUp (FrameDestination const& outputDestination) - { - DisplayerTab& slots (_instance->slots_); - return &slots.manage (new DisplayerSlot (outputDestination)); - } - - - - void - DisplayService::allocate (LumieraDisplaySlot handle, bool doAllocate) - { - REQUIRE (handle); - if (doAllocate) - { - if (handle->put_) - throw lumiera::error::Logic("slot already allocated for output"); - else - // Mark the handle as "allocated" and ready for output: - // Place the function pointer from the C interface into the handle struct. - // calling it will invoke the implementing instance's "put" function - // (see the LUMIERA_INTERFACE_INLINE above in this file!) - handle->put_ = serviceInstance_.get().put; - } - else - handle->put_ = 0; - } - - - - DisplayerSlot& - DisplayService::resolve (LumieraDisplaySlot handle) - { - REQUIRE (handle); - REQUIRE (handle->put_, "accessing a DisplayerSlot, which hasn't been locked for output"); - - return *static_cast (handle); - } - - - - - - /* === DisplayerSlot Implementation === */ - - - DisplayerSlot::DisplayerSlot (FrameDestination const& outputDestination) - : currBuffer_(0) - { - put_ = 0; // mark as not allocated - hasFrame_.connect (outputDestination); - dispatcher_.connect (sigc::mem_fun (this, &DisplayerSlot::displayCurrentFrame)); - } - - - DisplayerSlot::~DisplayerSlot() - { - TRACE (gui_dbg, "Displayer Slot closing..."); - } - - - void - DisplayerSlot::displayCurrentFrame() - { - hasFrame_.emit (currBuffer_); - } - - -} // namespace proc +}}} // namespace proc::play::sound diff --git a/src/proc/play/sound/jack-output.hpp b/src/proc/play/sound/jack-output.hpp index 1dec2c192..d793662d3 100644 --- a/src/proc/play/sound/jack-output.hpp +++ b/src/proc/play/sound/jack-output.hpp @@ -1,8 +1,8 @@ /* - DISPLAY-SERVICE.hpp - service providing access to a display for outputting frames + JACK-OUTPUT.hpp - Jack audio connection client Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,21 +20,7 @@ */ -/** @file display-service.hpp - ** A public service provided by the GUI, implementing the lumiera::Display facade interface. - ** It serves two purposes: - ** - It maintains a collection of DisplayerSlot objects, which are the actual connection points - ** and allow to receive frames and dispatch them to the GTK main event loop thread. - ** Conceptually, creating such a slot means providing a possible display for output. - ** - It provides the actual implementation of the Display facade interface, i.e. the function - ** which is to invoked periodically by the playback processes to dispose a new frame into - ** the display. - ** - ** This service is the implementation of a layer separation facade interface. This header defines - ** the interface used to \em provide this service, not to access it. Clients get a specific - ** LumieraDisplaySlot passed as parameter when initiating a playback process from the GUI. Using - ** this LumieraDisplaySlot handle, clients should then use lumiera::DummyPlayer#facade to access - ** an implementation instance of this service in order to push actual frames up. +/** @file jack-output.hpp ** ** @see lumiera::Display ** @see lumiera::DummyPlayer @@ -42,149 +28,26 @@ */ -#ifndef GUI_DISPLAY_SERVICE_H -#define GUI_DISPLAY_SERVICE_H +#ifndef PROC_PLAY_SOUND_JACK_OUTPUT_H +#define PROC_PLAY_SOUND_JACK_OUTPUT_H -#include "include/display-facade.h" -#include "common/instancehandle.hpp" -#include "lib/singleton-ref.hpp" -#include "lib/scoped-ptrvect.hpp" - -#include -#include -#include -#include -#include +//#include "include/display-facade.h" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/scoped-ptrvect.hpp" +// +//#include +//#include +//#include +//#include +//#include -namespace gui { - - using std::string; - using std::vector; - using lumiera::Display; - using Glib::Dispatcher; +namespace proc { +namespace play { +namespace sound { - typedef sigc::slot FrameDestination; - typedef sigc::signal FrameSignal; - - - - /******************************************************************** - * Actual implementation of a single displayer slot. Internally, - * it is connected via the Glib::Dispatcher for outputting frames - * to a viewer widget, which executes within the GTK event thread. - * @note must be created from the GTK event thread. - */ - class DisplayerSlot - : public lumiera_displaySlot, - boost::noncopyable - { - Dispatcher dispatcher_; - FrameSignal hasFrame_; - - LumieraDisplayFrame currBuffer_; - - - public: - DisplayerSlot (FrameDestination const&) ; - ~DisplayerSlot () ; - - /* Implementation-level API to be used by DisplayService */ - - /** receive a frame to be displayed */ - inline void put (LumieraDisplayFrame); - - - private: - /** internal: activated via Dispatcher - * and running in GTK main thread */ - void displayCurrentFrame(); - - }; - - typedef lib::ScopedPtrVect DisplayerTab; - - - - /****************************************************** - * Actual implementation of the DisplayService. - * Creating an instance of this class automatically - * registers the interface lumieraorg_Display with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - * \par - * In addition to the Display interface, this class - * implements an additional service for the GUI, - * allowing actually to set up display slots, which - * then can be handed out to client code in the - * course of the play process for outputting frames. - */ - class DisplayService - : boost::noncopyable - { - - string error_; - DisplayerTab slots_; - - - /* === Interface Lifecycle === */ - - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_Display, 0) - , lumiera::Display - > ServiceInstanceHandle; - - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; - - - public: - DisplayService(); - ~DisplayService() { - INFO (proc_dbg, "Display service dying..."); - - } - - - /** open a new display, sending frames to the given output destination - * @return handle for this slot, can be used to start a play process. - * NULL handle in case of any error. */ - static LumieraDisplaySlot setUp (FrameDestination const&); - - - /** prepare and the given slot for output - * @param doAllocate allocate when true, else release it - * @throw lumiera::error::Logic when already in use */ - void allocate (LumieraDisplaySlot, bool doAllocate); - - - /** resolve the given display slot handle to yield a ref - * to an actual implementation object. In order to be resolvable, - * the DisplayerSlot needs to be locked (=allocated) for output use. */ - DisplayerSlot& resolve (LumieraDisplaySlot); - - }; - - - - - void - DisplayerSlot::put(LumieraDisplayFrame newFrame) - { - if (newFrame != currBuffer_) - { - currBuffer_ = newFrame; - dispatcher_.emit(); - } - else - { - TRACE (render, "frame dropped?"); - } - } - - - -} // namespace gui +}}} // namespace proc::play::sound #endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 4b7b80c02..d9d884eb8 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1632,25 +1632,35 @@ The main tool used to implement this separation is the [[Builder Pattern|http:// Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
-
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
 
 !Starting point
-The idea is to start out with the design of the PlayerDummy and to //transform//&nbsp; it into the full player subsystem.
+The intention is to start out with the design of the PlayerDummy and to //transform//&nbsp; it into the full player subsystem.
 * the ~DisplayService in that dummy player design moves down into Proc-Layer and becomes the OutputManager
 * likewise, the ~DisplayerSlot is transformed into the interface OutputSlot, with various implementations to be registered with the OutputManager
 * the core idea of having a play controler act as the frontend and handle to an PlayProcess is retained.
 
+!!Idea
+Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+
 !!Stages of transition
-Reworking the dummy player into the PlayerSubsystem is a larger undertaking, and best broken up into several //stages.//
+Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger undertaking, and best broken up into several //stages.//
 # just moving the existing entities into the final location (typically down in the layer hierarchy)
 # introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
 # rework the ~ProcessImpl and the ~TickService to become the CalculationStream and use the real Engine Interface, but still with an mock implementation hooked up behind
 # extend and elaborate the handling of output, build the foundation of the real OutputManager
 # switch to the real Scheduler and Engine implementation
 
-!!Idea
-Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+!!!!new entities to build {{red{#805}}} +* the ~DisplayService needs to be generalised into an OutputManager interface +* we need to introduce a new kind of StructAsset, the ViewerAsset. +* then, of course, the PlayService interface needs to be created +* as a temporary soltion, a ''~DummyPlayConnection'' will be created hard-wired, within the ~DummyPlayer service. This dumy container is a test setup, explicitly creating a OutputManager implementation, a GeneratorMO, a GeneratorNode (thus omitting the not-yet-implemented Builder) and the corresponding ModelPort. This way, the ~DummyPlayer service can be changed to use the real PlayService for creating the dummy generated output data. It will then pass back the resulting PlayController to the existing GUI setup. + +The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach tries to circumvent the problems with occasional crashes on shutdown, due to dispatching frame output signals in the event thread while already in shutdown. + +
LayerSeparationInterface provided by the GUI.
@@ -1713,6 +1723,15 @@ When building the low-level model, the actual processing code is resolved and a
 Initially, only the parameter (descriptors) are present on the effect ~MObject, while the actual [[parameter providers|ParamProvider]] are created or wired (by the ConManager) on demand.
 
+
+
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalculationStream]].
+
+Below this facade, there is a thin adaptadion and forwarding layer, mainly talking to
+* the individual CalculationStream elements created for each PlayProcess.
+* the FrameDispatcher, which translates such streams into a series of RenderJob entries
+* the [[Scheduler]], which is responsible to perform these jobs in a timely fashion
+
+
A general identification scheme, ombining a human readable symbolic name, unique within a //specifically typed context,// and machine readable hash ID (LUID). ~Entry-IDs allow for asset-like position accounting and for type safe binding between configuration rules and model obects. They allow for creating an entry with symbolic id and distinct type, combined with an derived hash value, without the overhead in storage and instance management imposed by using a full-fledged Asset.
                                                                                       
@@ -1854,19 +1873,19 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
 :Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{<number_of_segments>}}} separate exit nodes, but of course an exit node may be //mute.//
 
-
+
Generally speaking, the [[here|Fixture]] comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
-This page focusses on the actual datastructure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
+This page focusses on the actual data structure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
 
 !transactional switch
-A key point to note is the fact that the fixture is frequently [[re-built||BuildFixture]] by the [[Builder]], while render processes may be going in in parallel. Thus, when a build process is finished, a transactional ''commit'' happens to ''hot swap'' the new parts of the model. This is complemented by a cleanup of tainted render processes; finally, storage can be relaimed.
+A key point to note is the fact that the fixture is frequently [[re-built||BuildFixture]] by the [[Builder]], while render processes may be going on in parallel. Thus, when a build process is finished, a transactional ''commit'' happens to ''hot swap'' the new parts of the model. This is complemented by a clean-up of tainted render processes; finally, storage can be reclaimed.
 
 To support this usage pattern, the Fixture implementation makes use of the [[PImpl pattern|http://c2.com/cgi/wiki?PimplIdiom]]
 
 !Collecting usage scenarios {{red{WIP 12/10}}}
 * ModelPort access
 ** get the model port for a given ~Pipe-ID
-** ennumerate the model ports for a Timeline
+** enumerate the model ports for a Timeline
 * rendering frame dispatch
 ** get or create the frame dispatcher table
 ** dispatch a single frame to yield the corresponding ExitNode
@@ -1875,16 +1894,16 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm
 ** create a new segmentation
 ** establish what segments actually need to be rebuilt
 ** dispatch a newly built segment into the transaction
-** schedule superseded segments and tainted process for cleanup
+** schedule superseded segments and tainted process for clean-up
 ** commit a transaction
  
 
 !Conclusions about the structure {{red{WIP 12/10}}}
 * the ~PImpl needs to be a single (language) pointer. This necessitates having a monolithic Fixture implementation holder
-* moreover, this necessitates a tight integration up to implementation level, both with the cleanup and the render processes themselves
+* moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
 
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
 Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes are known to be terminated.
 
@@ -1922,7 +1941,7 @@ But the primary question here is to judge the impact of such an implementation.
 
 !!!identifying tainted and disposable segments
 Above estimation hints at the necessity of frequently finding some 30 to 100 segments to be disposed, out of thousands, assumed the original reason for triggering the build process was a typically local change in the high-level model. We can only discard when all related processes are finished, but there is a larger number of segments as candidate for eviction. These candidates are rather easy to pinpoint -- they will be uncovered during a linear comparison pass prior to commiting the changed Fixture. Usually, the number of candidates will be low (localised changes), but global manipulations might invalidate thousands of segments.
-* if we frequently pick the segments actually to disposed, there is the danger of performance degeneration when the number if segments is high
+* if we frequently pick the segments actually to be disposed, there is the danger of performance degeneration when the number of segments is high
 * the other question is if we can afford just to keep all of those candidates around, as all of them are bound to get discardable eventually
 * and of course there is also the question how to detect //when// they're due.
 
@@ -1935,7 +1954,7 @@ Above estimation hints at the necessity of frequently finding some 30 to 100 seg
 //currently {{red{12/10}}} I tend to prefer Model B...// while the priority queue remains to be investigated in more detail for organising the actual build process.
 But actually I'm struck here, because of the yet limited knowledge about those render processes....
 * how do we //join// an aborted/changed rendering process to his successor, without creating a jerk in the output?
-* is it even possible to continue a process when parts of the covered timerange is affected by a build?
+* is it even possible to continue a process when parts of the covered timerange are affected by a build?
 If the latter question is answered with "No!", then the problem gets simple in solution, but maybe memory consuming: In that case, //all//&nbsp; processes linked to a timeline gets affected and thus tainted; we'd just dump them onto a pile and delay releasing all of the superseeded segments until all of them are known to be terminated.
 
@@ -1976,6 +1995,9 @@ Additionally, they may be used for resource management purposes by embedding a r #* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic)
+
+
An implementation facility within the RenderEngine, responsible for translating a logical CalculationStream (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
+
/***
 |Name|FullScreenPlugin|
@@ -2967,12 +2989,12 @@ These are used as token for dealing with other objects and have no identity of t
 
 
-
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
-A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
+A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination, while some other entity at the same time actually //uses this designation as a target,// either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 
-Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
+Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewConnection]]. Any render or display process happens at a model port.
 
 !formal specification
 Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being associated with a ~Pipe-ID.
@@ -3224,7 +3246,7 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa
 &rarr; see also the PlayerDummy
 
-
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
@@ -3235,8 +3257,8 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa
 :any Timeline produces a number of [[model ports|ModelPort]]. On the other hand, any viewer exposes a small number of output designations, representing the image and sound output(s).
 :Thus, in this case we resolve similar to a bus connection, possibly overridden by already pre-existing or predefined connections.
 ;switch board
-:a viewer might receive multiple outputs and overlays, necessitating a user operated control to select what's actually to displayed
-:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as defined by the switchboard
+:a viewer might receive multiple outputs and overlays, necessitating a user operated control to select what's actually to be displayed
+:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as defined by this viewer SwitchBoard
 ;global pipes or virtual media
 :when binding a Sequence as Timeline or VirtualClip, a mapping from output designations used within the Sequence to virtual channels or global pipes is required
 :Thus, in this case we need to associate output designations with ~Pipe-IDs encountered in the context according to some rules &mdash; again maybe overridden by pre-existing connections
@@ -4114,15 +4136,38 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 <<tasksum end>>
 
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
-The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls dispatched as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
+The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
-''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this stream of calculations happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
-* the [[Backend]] is responsible for scheduling and dispatching the calculations
+''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalculationStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
+* the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the CalculationStream
 * the RenderEngine has the ability to cary out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
+
+
The [[Player]] is an independent [[Subsystem]] within Lumiera, located at Proc-Layer level. A more precise term would be "rendering and playback coordination subsystem". It provides the capability to generate media data, based on a high-level model object, and send this generated data to an OutputDesignation, creating an continuous and timing controlled output stream. Clients may utilise these functionality through the ''play service'' interface.
+
+!provided services
+* creating a PlayProcess
+* managing existing play processes
+* convenience short-cuts for //performing//&nbsp; several kinds of high-level model objects
+
+!creating a play process
+This is the core service provided by the player subsystem. The purpose is to create a collaboration between several entities, creating media output
+;data producers
+:a set of ModelPort elements to ''pull'' for generating output. They can either be handed in direcly, or resolved from a set of OutputDesignation elements
+;data sinks
+:to be able to create output, the PlayProcess needs to cooperate with OutputSlot.s
+:physical outputs are never handled directly, rather, the playback or rendering needs an OutputManager to resolve the output designations into output slots
+;controller
+:when provided with these two prerequisites, the play service is able to build a PlayProcess.
+:for clients, this process can be accessed and maintained through a PlayController, which acts as (copyable) handle and front-end.
+;engine
+:the actual processing is done by the RenderEngine, which in itself is a compound of several services within [[Backend]] and Proc-Layer
+:any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
+
+
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
@@ -4722,9 +4767,10 @@ The link between ~MObject and Asset should be {{{const}}}, so the clip can't cha
 At first sight the link between asset and clip-MO is a simple logical relation between entities, but it is not strictly 1:1 because typical media are [[multichannel|MultichannelMedia]]. Even if the media is compound, there is //only one asset::Clip//, because in the logical view we have only one "clip-thing". On the other hand, in the session, we have a compound clip ~MObject comprised of several elementary clip objects, each of which will refer to its own sub-media (channel) within the compound media (and don't forget, this structure can be tree-like)
 {{red{open question:}}} do the clip-MO's of the individual channels refer directly to asset::Media? does this mean the relation is different from the top level, where we have a relation to a asset::Clip??
-
+
Conceptually, the Render Engine is the core of the application. But &mdash; surprisingly &mdash; we don't even have a distinct »~RenderEngine« component in our design. Rather, the engine is formed by the cooperation of several components spread over two layers (Backend and Proc-Layer): The [[Builder]] creates a network of [[render nodes|ProcNode]], which is used by the Backend to pull individual Frames.
 &rarr; OverviewRenderEngine
+&rarr; EngineFaçade 
 
@@ -4910,7 +4956,7 @@ We need to detect attaching and detaching of * root &harr; [[Track]]
-
+
//Segmentation of timeline// denotes a data structure and a step in the BuildProcess.
 When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Placements -- are grouped below each timeline using them; Placements are then to be resolved into [[explicit Placements|ExplicitPlacement]], resulting in a single well defined time interval for each object. This allows to cut this effective timeline into slices of constant wiring structure, which are represented through the ''Segmentation Datastructure'', a time axis with segments holding object placements and [[exit nodes|ExitNode]]. &nbsp;&rarr; see [[structure of the Fixture|Fixture]]
 * for each Timeline we get a Segmentation
@@ -4940,7 +4986,7 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla
 
 !!!conclusions
 The Fixture is mostly comprised of the Segementation datastructure, but some other facilities are involved too
-# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level handled by the Fixture
+# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level is handled by the Fixture
 # during the build process, there is a collection of placements; this can be discarded afterwards
 # the backbone of the segmentation is closely linked to an ordering by time. Initially it should support sorting, access by time interval search later on.
 # discarding a segment (or failing to do so) has an high impact on the whole application. We should employ a reliable mechanism for that.
@@ -5725,6 +5771,14 @@ h1,h2,h3,h4,h5,h6 {
 /*}}}*/
 
+
+
Viewers for displaying output are connected to the timelines or other elements to be displayed through a ViewConnection. This creates an additional mapping step (&rarr; BindingMO), similar to the attachment of a sequence or virtual clip to the [[global busses|GlobalBus]]. Yet the viewers don't provide such an elaborate mixing desk like view as the timelines -- rather there is only a simplified version of the global busses, exposed to the user as ''switch board''.
+
+The corresponding GUI control will allow to choose among possible data sources (esp. ProbePoint) for any given StreamType (more precisely, for every //prototype,// e.g. image, sound). Moreover, there are some (limited) overlay capabilities (split image, mix sound). By default, this additional control will likely be hidden, as there is a default 1:1 association between the master busses of the timeline and the usable output destinations.
+
+!Model and Interfaces
+Regarding the internal wiring of components, the Viewer with a switch board behaves similar as a timeline: At the output side, there are a small number of standard OutputDesignation elements for the selection of primary kinds of media handled within this session (typically Video and Audio). But contrary to a timeline, a Viewer element also exposes an OutputManager interface, which is backed by a specific OutputMapping element, which internally connects to the main OutputManager for the whole Application. This way, a Viewer has the capability to get a PlayController
+
<<timeline better:true maxDays:55 maxEntries:45>>
@@ -6874,7 +6928,7 @@ At the level of individual timecode formats, we're lacking a common denominator; &rarr; Quantiser [[implementation details|QuantiserImpl]]
-
+
the following collection of usage situations helps to shape the details of the time values and time quantisation design. &rarr; see also  [[time quantisation|TimeQuant]]
 
 ;time position of an object
@@ -6894,6 +6948,8 @@ At the level of individual timecode formats, we're lacking a common denominator;
 :because this operation might be done both in the quantised or non-quantised domain, and also the result might be (un)quantised
 ;updating the playback position
 :this can be seen as a practical application of the above; basically we can choose to show the wall clock time or we can advance the playback position in frame increments, thus denoting the frame currently in display. For video, these distinctions may look moot, but they are indeed relevant for precise audio editing, especially when combined with loop playback (recall that audio is processed block wise, but the individual sample frames and thus the possible loop positions are way finer than the processing block size)
+;dispatching individual frames for calculation
+:when a [[render or playback process|PlayProcess]] is created, at some point we need to translate this logical unit ("calculation stream") into individual frame job entries. This requires to break continuous time into individual frames, and then ennumerating these frames.
 ;displaying time intervals
 :for display, time intervals get //re-quantised// into display array coordinates.
 :While evidently the display coordinates are themselves quantised and we obviously don't want to cancel out the effect of an quantisation of the values or intervals to be displayed (which means, we get two quantisations chained up after each other), there remains the question if the display array coordinates should be aligned to the grid of the //elements to be displayed,// and especially if the allowed zoom factors should be limited. This decision isn't an easy one, as it has an immediate and tangible effect on what can be showed, how reversible and reproducible a view is and (especially note this!) on the actual values which can be set and changed through the GUI.
@@ -6915,6 +6971,8 @@ Another closely related problem is ''when to allow [[mutations|TimeMutation]]'',
 
 The ''problem with playback position'' is -- that indeed it's an attempt to conceptualise a non-existing entity. There is no such thing like "the" playback position. Yet most applications I'm aware off //do// employ this concept. Likely they got trapped by the metaphor of the tape head, again. We should do away with that. On playback, we should show a //projection of wall-clock time onto the expected playback range// -- not more, not less. It should be acknowledged that there is //no direct link to the ongoing playback processes,// besides the fact that they're assumed to sync to wall-clock time as well. Recall, typically there are multiple playback processes going on in compound, and each might run on a different update rate. If we really want a //visual out-of-sync indicator,// we should treat that as a separate reporting facility and display it apart of the playback cursor.
 
+An interesting point to note for the ''frame dispatch step'' is the fact, that in this case quantised values and quantisation are approached in the reverse direction, compared with the other uses. Here, after establishing a start point on the time scale, we proceed with ennumerating distinct frames and later on need to access the corresponding raw time, especially to find out about the [[timeline segment|Segmentation]] to address, or for retrieving parameter automation. &rarr; FrameDispatcher.
+
 Note that the ''display window might be treated as just an independent instance of quantisation''. This is similar to the approach taken above for modifying quantised time span values. We should provide a special kind of time grid, the display coordinates. The origin of these is always defined to the left (lower) side of the interval to be displayed, and they are gauged in screen units (pixels or similar, as used by the GUI toolkit set). The rest is handled by the general quantisation mechanisms. The problem of aligning the display should be transformed into a general facility to align grids, and solved for the general case. Doing so solves the remaining problems with quantised value changes and with ''specifying relative placements'' as well: If we choose to represent them as quantised values, we might (or might not) also choose to apply this //grid-alignment function.//
 
 !the complete time value usage cycle
@@ -6976,12 +7034,13 @@ Currently (1/11), the strategy is implemented according to (1) and (4) above, le
 Implementation of this strategy is still broken: it doesn't work properly when actually the change passing over the zero point happens by propagation from lower digits. Because then -- given the way the mutators are implemented -- the //new value of the wrapping digit hasn't been stored.// It seems the only sensible solution is to change the definition of the functors, so that any value will be changed by side-effect {{red{Question 4/11 -- isn't this done and fixed by now??}}}
 
-
+
Timeline is the top level element within the [[Session (Project)|Session]]. It is visible within a //timeline view// in the GUI and represents the effective (resulting) arrangement of media objects, to be rendered for output or viewed in a Monitor (viewer window). A timeline is comprised of:
 * a time axis in abolute time ({{red{WIP 1/10}}}: not clear if this is an entity or just a conceptual definition) 
-* a PlayControler ({{red{WIP Summer 2010: see discussion on ML. It seems rather that the controller will be attached}}})
 * a list of [[global Pipes|GlobalPipe]] representing the possible outputs (master busses)
 * //exactly one// top-level [[Sequence]], which in turn may contain further nested Sequences.
+* when used for Playback, a ViewConnection is necessary, allowing to get or connect to a PlayController
+
 Please note especially that following this design //a timeline doesn't define tracks.// [[Tracks form a Tree|Track]] and are part of the individual sequences, together with the media objects placed to these tracks. Thus sequences are independent entities which may exist stand-alone within the model, while a timeline is //always bound to hold a sequence.// &rarr; see ModelDependencies
 [>img[Fundamental object relations used in the session|uml/fig136453.png]]
 
@@ -7202,14 +7261,29 @@ For now, as of 6/10, we use specialised QueryResolver instances explicitly and d
 &rarr; QueryRegistration
 
-
+
+
For any kind of playback to happen, timeline elements (or similar model objects) need to be attached to a Viewer element through a special kind of [[binding|BindingMO]], called a ''view connection''. In the most general case, this creates an additional OutputMapping (and in the typical standard case, this boils down to a 1:1 association, sending the master bus of each media kind to the standard OutputDesignation for that kind).
+
+Establishing a ~ViewConnection is prerequisite for creating or attaching an PlayController through the PlayService. Multiple "play control" GUI elements can be associated with such a play controller, causing them to work as being linked together: if you e.g. push "play" on one of them, the button states of all linked GUI controls will reflect the state change of the underlying play controller.
+
+View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus "break") an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here "active" means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection.
+
+
+
+
A [[structural Asset|StructAsset]] corresponding to a Viewer element in the GUI.
+
+These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fulle resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
+
+When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
+
+
for showing output, three entities are involved
 * the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
 * by connecting to a viewer component, an actual output target is established
 * the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
 
 !the viewer connection
-A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
+A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
 
From 92301a3752bac4184105dab35a01c274c8acc2a1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 24 Jul 2011 02:51:41 +0200 Subject: [PATCH 004/108] SCons: adjust configure check for boost-filesystem to work for >= 1.42 NOTE: this breaks compatibility with Debian/Lenny --- SConstruct | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 250936bc7..318f3f53c 100644 --- a/SConstruct +++ b/SConstruct @@ -254,8 +254,10 @@ def configurePlatform(env): problems.append('We need boost::format (header).') if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): problems.append('We need boost::program_options (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_system-mt','boost/system/error_code.hpp','C++'): + problems.append('We need the boost::system support library (including binary lib).') if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): - problems.append('We need the boost::filesystem (including binary lib for linking).') + problems.append('We need the boost::filesystem lib (including binary lib for linking).') if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): problems.append('We need the boost regular expression lib (incl. binary lib for linking).') From 20f95ca26f6c4f280379f046a27c395eeced5aad Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 24 May 2011 03:46:32 +0200 Subject: [PATCH 005/108] draft the player facade interface --- src/common/instancehandle.hpp | 8 +- src/common/interfaceproxy.cpp | 146 ++++++++++-------- src/include/display-facade.h | 19 ++- src/include/dummy-player-facade.h | 1 - src/include/interfaceproxy.hpp | 2 +- src/include/play-facade.h | 141 +++++++++++++++++ src/lib/p.hpp | 28 ++-- .../play/dummy-player-interface-proxy.hpp | 4 +- src/proc/play/output-manager.hpp | 8 +- 9 files changed, 260 insertions(+), 97 deletions(-) create mode 100644 src/include/play-facade.h diff --git a/src/common/instancehandle.hpp b/src/common/instancehandle.hpp index d6c98e546..9f8004c65 100644 --- a/src/common/instancehandle.hpp +++ b/src/common/instancehandle.hpp @@ -184,7 +184,7 @@ namespace lumiera { public: /** Set up an InstanceHandle representing a plugin. - * Should be placed at the client side. + * Should be placed at the client side. * @param iName unmangled name of the interface * @param version major version * @param minminor minimum acceptable minor version number @@ -203,7 +203,7 @@ namespace lumiera { * registration and deregistration of interface(s). * Should be placed at the service providing side. * @param a (single) interface descriptor, which can be created with - * LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF + * LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF */ InstanceHandle (LumieraInterface descriptor) : desc_(descriptor) @@ -222,9 +222,9 @@ namespace lumiera { - /** act as smart pointer providing access through the facade. + /** act as smart pointer providing access through the facade. * @note we don't provide operator* */ - FA * operator-> () const { return &(facadeLink_(*this)); } + FA * operator-> () const { return &(facadeLink_(*this)); } /** directly access the instance via the CL interface */ I& get () const { ENSURE(instance_); return *instance_; } diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp index e403e40a5..611bdd97c 100644 --- a/src/common/interfaceproxy.cpp +++ b/src/common/interfaceproxy.cpp @@ -28,73 +28,87 @@ using util::cStr; -namespace lumiera { - namespace facade { - - - LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible"); - - - template - class Holder; - - template - class Holder > - : Accessor, - protected FA - { - protected: - typedef InstanceHandle IHandle; - typedef Holder THolder; - typedef Proxy TProxy; - typedef Accessor Access; - - I& _i_; - - Holder (IHandle const& iha) - : _i_(iha.get()) - { } - - public: - static TProxy& open(IHandle const& iha) - { - static char buff[sizeof(TProxy)]; - TProxy* p = new(buff) TProxy(iha); - Access::implProxy_ = p; - return *p; - } - - static void close() - { - if (!Access::implProxy_) return; - TProxy* p = static_cast (Access::implProxy_); - Access::implProxy_ = 0; - p->~TProxy(); - } - }; - - - template - FA* Accessor::implProxy_; - - - template - void - openProxy (IHA const& iha) - { - Proxy::open(iha); - } - - template - void - closeProxy () - { - Proxy::close(); - } - - } // namespace facade +namespace lumiera{ +namespace facade { -} // namespace lumiera + + LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible"); + + + /** + * Implementation Base + * for building Facade Proxy implementations. + * Typically the purpose of such a proxy is to route + * any calls through the C-Bindings of the Lumiera Interface system. + * The actual storage for the concrete proxy object is embedded, + * inline within the #open() function. For access by the clients, + * a frontend-object of type \c Accessor may be placed into + * the facade interface; this accessor-frontend is basically + * a concealed static pointer to the proxy, and will be set, + * when the interface is opened. This opening and closing + * of the interface itself is controlled by the + * InstanceHandle, which in turn is typically + * created and managed within the context + * of the service implementation. + */ + template + class Holder; + + template + class Holder > + : Accessor + , protected FA + { + protected: + typedef InstanceHandle IHandle; + typedef Holder THolder; + typedef Proxy TProxy; + typedef Accessor Access; + + I& _i_; + + Holder (IHandle const& iha) + : _i_(iha.get()) + { } + + public: + static TProxy& open(IHandle const& iha) + { + static char buff[sizeof(TProxy)]; + TProxy* p = new(buff) TProxy(iha); + Access::implProxy_ = p; + return *p; + } + + static void close() + { + if (!Access::implProxy_) return; + TProxy* p = static_cast (Access::implProxy_); + Access::implProxy_ = 0; + p->~TProxy(); + } + }; + + + template + FA* Accessor::implProxy_; + + + template + void + openProxy (IHA const& iha) + { + Proxy::open(iha); + } + + template + void + closeProxy () + { + Proxy::close(); + } + +}} // namespace lumiera::facade diff --git a/src/include/display-facade.h b/src/include/display-facade.h index d226fa6e5..66089287e 100644 --- a/src/include/display-facade.h +++ b/src/include/display-facade.h @@ -21,13 +21,18 @@ */ /** @file display-facade.h - ** Major public Interface of the Lumiera GUI. While, generally speaking, the GUI - ** controls the application and thus acts on its own, it exposes some services - ** to the lower layers. Especially the lumiera::Display interface serves to - ** hand over calculated frames to the GUI for displaying them in a viewer. - ** It's a first draft as of 1/2009, probably it can be factored out into - ** a more general display service in future. - ** + ** Experimental Interface, allowing the Dummy-Player to access the + ** video display widget in the GUI. While, generally speaking, the GUI + ** controls the application and thus acts on its own, it might expose some + ** services to the lower layers. + ** + ** In the Dummy-Player design study, the lumiera::Display interface serves + ** to hand over calculated frames to the GUI for displaying them in a viewer. + ** + ** This is a first draft as of 1/2009, and likely to be superseded by a + ** better design, where rather the \em provider of an output facility + ** registers with the OutputManager in the core. + ** ** @see gui::GuiFacade ** @see dummy-player-facade.h ** diff --git a/src/include/dummy-player-facade.h b/src/include/dummy-player-facade.h index 403ca2f35..af7ba31f7 100644 --- a/src/include/dummy-player-facade.h +++ b/src/include/dummy-player-facade.h @@ -35,7 +35,6 @@ #include "include/interfaceproxy.hpp" #include "lib/handle.hpp" -#include diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp index ea5677042..a488ce65f 100644 --- a/src/include/interfaceproxy.hpp +++ b/src/include/interfaceproxy.hpp @@ -33,7 +33,7 @@ ** Typically there is another subclass of the Facade interfaces sitting "on the other side" ** of the interface barrier and actually implementing the functionality. The template ** facade::Accessor can be thought of as a factory creating such a proxy instance of the - ** facade interface for the client code to use. Typically, in instance of the \em factory + ** facade interface for the client code to use. Typically, an instance of the \em factory ** is embedded (as a static functor member object) right within the otherwise abstract ** facade interface, this way allowing the client code to write e.g. \c XYZInterface::facade() ** to yield a reference to a proxy object implementing \c XYZInterface. diff --git a/src/include/play-facade.h b/src/include/play-facade.h new file mode 100644 index 000000000..82af1fddb --- /dev/null +++ b/src/include/play-facade.h @@ -0,0 +1,141 @@ +/* + PLAYER-FACADE.h - access point to the Lumiera player subsystem + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +#ifndef PROC_INTERFACE_PLAY_H +#define PROC_INTERFACE_PLAY_H + + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + +//#include "include/interfaceproxy.hpp" +#include "lib/handle.hpp" +#include "lib/iter-source.hpp" +#include "lib/time/timevalue.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/mobject/output-designation.hpp" +#include "proc/mobject/session/clip.hpp" +#include "proc/mobject/session/track.hpp" +#include "proc/play/output-manager.hpp" +#include "asset/timeline.hpp" +#include "asset/viewer.hpp" + + + +namespace proc { + namespace play { + + class PlayProcess; +} } + + +namespace lumiera { + + + /****************************************************************** + * Interface to the Player subsystem of Lumiera (Proc-Layer). + * Global access point for starting playback and render processes, + * calculating media data by running the render engine. + * + * @todo WIP-WIP-WIP 6/2011 + * @note Lumiera is not yet able actually to deliver rendered data. + * @todo there should be an accompanying CL Interface defined for + * the Lumiera interface system, so the player can be + * accessed from external clients. This was left out + * for now, as we don't have either plugins, nor + * any script running capabilities yet. (5/2011) + */ + class Play + { + public: + + /** get an implementation instance of this service */ + static lumiera::facade::Accessor facade; + + + /** + * Continuous playback process, which has been hooked up + * and started with a fixed set of output slots. started with a specific + * output size, format and framerate. It is a handle to a calculation process, + * which is about to produce a stream of frames and push them to the outputs. + * + * The Lifecycle of the referred playback process is managed automatically + * through this handle (by ref count). Client code is supposed to use the + * API on this handle to navigate and control the playback mode. + * + * @see handle.hpp + * @see player-service.cpp implementation + */ + class Controller + : public lib::Handle + { + public: + void play(bool); ///< play/pause toggle + void adjustSpeed(double); ///< playback speed control + void go(lib::time::Time); + /////////////////////////////TODO how to modify the Loop range, the playback mode, scrubbing? + }; + + + typedef lib::IterSource ModelPorts; + typedef lib::IterSource Pipes; + typedef proc::play::POutputManager Output; + typedef mobject::session::PClipMO Clip; + typedef mobject::PTrack Track; + typedef asset::PTimeline Timeline; + typedef asset::PViewer Viewer; + + /** create a new playback process outputting to the given viewer/display */ + virtual Controller connect(ModelPorts, Output) =0; + + + /* ==== convenience shortcuts for common use cases ==== */ + Controller perform(Pipes, Output); + Controller perform(Timeline); + Controller perform(Viewer); + Controller perform(Track); + Controller perform(Clip); + + protected: + virtual ~Player(); + }; + + +} // namespace lumiera + + + + +extern "C" { +#endif /* =========================== CL Interface ===================== */ + + +// #include "common/interface.h" +////////////////////////////////////TODO define a C binding for the Interface system here + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/p.hpp b/src/lib/p.hpp index 4fef50a36..cd7ccd776 100644 --- a/src/lib/p.hpp +++ b/src/lib/p.hpp @@ -79,51 +79,51 @@ namespace lumiera { P ( ) : BASE() {} template explicit P (Y* p) : BASE(p) {} template P (Y* p, D d) : BASE(p,d){} - + P (P const& r) : BASE(r) {} template P (shared_ptr const& r) : BASE(r) {} template explicit P (weak_ptr const& wr) : BASE(wr) {} - template explicit P (std::auto_ptr & ar) : BASE(ar) {} - + template explicit P (std::auto_ptr & ar) : BASE(ar) {} - P& operator= (P const& r) { BASE::operator= (r); return *this; } + + P& operator= (P const& r) { BASE::operator= (r); return *this; } template P& operator=(shared_ptr const& sr) { BASE::operator= (sr); return *this; } template P& operator=(std::auto_ptr & ar) { BASE::operator= (ar); return *this; } - + TAR* get() const { return dynamic_cast (BASE::get()); } TAR& operator*() const { return *get(); } TAR* operator->() const { return get(); } - + void swap(P& b) { BASE::swap (b);} - + private: /* === friend operators injected into enclosing namespace for ADL === */ template friend inline bool operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } - + template friend inline bool operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } - + template friend inline bool operator< (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p < *q; } ///< @note deliberately not allowing comparison on NIL ////TICKET #307 : problem with equality test in associative containers, where equal(a,b) := !(a < b) && !(b < a) - + template friend inline bool operator> (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *q < *p; } - + template friend inline bool operator<= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p <= *q;} - + template friend inline bool operator>= (P const& p, P<_O_> const& q) { REQUIRE (p && q); return *p >= *q;} }; - - + + } // namespace lumiera #endif diff --git a/src/proc/play/dummy-player-interface-proxy.hpp b/src/proc/play/dummy-player-interface-proxy.hpp index 3e4ccdcd4..4753250a2 100644 --- a/src/proc/play/dummy-player-interface-proxy.hpp +++ b/src/proc/play/dummy-player-interface-proxy.hpp @@ -53,8 +53,8 @@ namespace lumiera { namespace facade { typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , proc::play::DummyPlayer - > IHandle_DummyPlayer; + , proc::play::DummyPlayer + > IHandle_DummyPlayer; template<> diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index b697f35d6..df3b4e216 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -36,12 +36,15 @@ #include //#include //#include +#include +namespace proc { namespace play { //using std::string; //using std::vector; + using std::tr1::shared_ptr; @@ -77,8 +80,9 @@ namespace play { public: OutputManager() {} }; - + + typedef shared_ptr POutputManager; -} // namespace play +}} // namespace proc::play #endif From c091b8e2603121038a799c35f0f81afedb307a81 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 24 May 2011 03:48:49 +0200 Subject: [PATCH 006/108] define Player / Output subsystem stub --- src/lumiera/main.cpp | 8 +++++-- src/proc/facade.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++ src/proc/facade.hpp | 9 ++++++++ wiki/renderengine.html | 4 ++-- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index 14d039b7d..f738337bf 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -37,15 +37,15 @@ using lib::Cmdline; using lumiera::Subsys; using lumiera::AppState; -using lumiera::ON_GLOBAL_INIT; namespace { Subsys& engine = backend::EngineFacade::getDescriptor(); Subsys& netNode = backend::NetNodeFacade::getDescriptor(); Subsys& script = backend::ScriptRunnerFacade::getDescriptor(); - Subsys& player = lumiera::DummyPlayer::getDescriptor(); + Subsys& player = lumiera::DummyPlayer::getDescriptor(); ///////TODO: just a dummy, until we're able to render Subsys& builder = proc::Facade::getBuilderDescriptor(); Subsys& session = proc::Facade::getSessionDescriptor(); + Subsys& playOut = proc::Facade::getPlayOutDescriptor(); Subsys& lumigui = gui::GuiFacade::getDescriptor(); } @@ -66,13 +66,17 @@ main (int argc, const char* argv[]) session.depends (builder); netNode.depends (session); netNode.depends (engine); + playOut.depends (engine); + playOut.depends (session); // lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin // lumigui.depends (engine); + player.depends (playOut); //////TODO dummy player, until we're able to render lumigui.depends (player); script.depends (session); script.depends (engine); application.maybeStart (session); + application.maybeStart (playOut); application.maybeStart (netNode); application.maybeStart (lumigui); application.maybeStart (script); diff --git a/src/proc/facade.cpp b/src/proc/facade.cpp index 6b44fbd36..6ca6a84b6 100644 --- a/src/proc/facade.cpp +++ b/src/proc/facade.cpp @@ -103,9 +103,53 @@ namespace proc { } }; + + + class PlayOutSubsysDescriptor + : public Subsys + { + operator string () const { return "PlayOut"; } + + /** determine, if any output system is required to start up explicitly. + * Moreover, extract configuration variations for specific kinds of output + * @return true if any output system is required to start stand-alone. + * otherwise, the player and a default configured output connection + * is pulled up only when required by another subsystem (e.g. GUI) + * @todo actually define cmdline options and parse/decide here! + */ + bool + shouldStart (lumiera::Option&) + { + TODO ("extract options about specific output systems to be brought up"); + return false; + } + + bool + start (lumiera::Option&, Subsys::SigTerm termination) + { + UNIMPLEMENTED ("bring up and configure the output connections and the player"); + return false; + } + + void + triggerShutdown () throw() + { + UNIMPLEMENTED ("initiate playback/render halt and disconnect output"); + } + + bool + checkRunningState () throw() + { + //Lock guard (*this); + TODO ("implement detecting running state"); + return false; + } + }; + namespace { lib::Singleton theBuilderDescriptor; lib::Singleton theSessionDescriptor; + lib::Singleton thePlayOutDescriptor; } @@ -125,6 +169,14 @@ namespace proc { { return theSessionDescriptor(); } + + + /** @internal intended for use by main(). */ + Subsys& + Facade::getPlayOutDescriptor() + { + return thePlayOutDescriptor(); + } diff --git a/src/proc/facade.hpp b/src/proc/facade.hpp index 88ac62e56..e32227729 100644 --- a/src/proc/facade.hpp +++ b/src/proc/facade.hpp @@ -44,6 +44,8 @@ namespace proc { * @todo this is a dummy placeholder as of 1/2009. Currently, there * is only implementation-level code within the Proc-Layer and * the interfaces need to be worked out. + * @note at least the Play/Output subsystem slowly turns into + * something real, as of 6/2011 * */ struct Facade @@ -60,6 +62,13 @@ namespace proc { static lumiera::Subsys& getSessionDescriptor(); + /** provide a descriptor for lumiera::AppState, + * wired accordingly to allow main to bring up + * the render, playback coordination and + * output management subsystem. */ + static lumiera::Subsys& getPlayOutDescriptor(); + + //////////////////TODO: define the global access interfaces for the Proc-Layer //////////////////TODO: provide a function for accessing this interface //////////////////TODO: register similar proxy/facade interfaces for the GUI diff --git a/wiki/renderengine.html b/wiki/renderengine.html index d9d884eb8..b5da538ca 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -7269,10 +7269,10 @@ Establishing a ~ViewConnection is prerequisite for creating or attaching an Play View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus "break") an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here "active" means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection.
-
+
A [[structural Asset|StructAsset]] corresponding to a Viewer element in the GUI.
 
-These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fulle resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
+These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fully resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
 
 When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
From 4731afefb83e47282d248d90976672d9fcc6d6f4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 25 May 2011 02:41:04 +0200 Subject: [PATCH 007/108] continue drafting the Player control elements -- life timevalue changes --- src/include/play-facade.h | 17 ++++++- src/lib/time/control.hpp | 102 ++++++++++++++++++++++++++++++++++++++ wiki/renderengine.html | 23 +++++---- 3 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 src/lib/time/control.hpp diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 82af1fddb..01db5f5f5 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -92,9 +92,22 @@ namespace lumiera { { public: void play(bool); ///< play/pause toggle + void scrub(bool); ///< scrubbing playback void adjustSpeed(double); ///< playback speed control - void go(lib::time::Time); - /////////////////////////////TODO how to modify the Loop range, the playback mode, scrubbing? + void go(lib::time::Time); ///< skip to the given point in time + + void controlPlayhead (lib::time::TimeControl & ctrl); + void controlDuration (lib::time::TimeControl & ctrl); + void controlLooping (lib::time::TimeControl & ctrl); + + void useProxyMedia (bool); + void setQuality (uint); + + bool is_playing() const; + bool is_scrubbing() const; + double getSpeed() const; + uint getQuality() const; + bool usesProxy() const; }; diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp new file mode 100644 index 000000000..5422daf45 --- /dev/null +++ b/src/lib/time/control.hpp @@ -0,0 +1,102 @@ +/* + CONTROL.hpp - a life time control for feedback and mutation + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +/** @file control.hpp + ** Manipulating and monitoring time entities with life changes. + ** This is an control- and callback element to handle any kind of "running" + ** time entities. This element is to be provided by the client and then attached + ** to the target time entity as a time::Mutation. Internally, a life connection to + ** the target is built, allowing both to + ** - to manipulate the target by invoking the function operator + ** - to receive change notifications by installing a callback functor. + ** + ** The actual type of the changes and modifications is specified as template parameter; + ** when later attached to some time entity as a Mutation, the actual changes to be performed + ** depend both on this change type and the type of the target time entity (double dispatch). + ** The behaviour is similar to applying a static time::Mutation + ** + ** \par relevance + ** This control element is intended to be used for all kinds of editing and monitoring + ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker + ** which can be dragged, a modifiable selection or the animated playhead cursor. + ** + ** @todo WIP-WIP-WIP + ** + */ + +#ifndef LIB_TIME_CONTROL_H +#define LIB_TIME_CONTROL_H + +#include "lib/error.hpp" +#include "lib/time/mutation.hpp" +//#include "lib/symbol.hpp" + +//#include +//#include +//#include +#include +//#include + + +namespace lib { +namespace time { + +//using lib::Symbol; +//using std::string; +//using lib::Literal; + using std::tr1::function; + +//LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated + + typedef function timeSignal; + + /** + * Interface: controller-element for retrieving and + * changing running time values + * + * @see time::Mutation + * @see time::TimeSpan#accept(Mutation const&) + * @todo WIP-WIP-WIP + */ + template + class Control + : Mutation + , timeSignal + { + public: + /** install a callback functor to be invoked + * to notify for any changes to the observed + * time entity */ + void connectChangeNotification (timeSignal); + + /** disconnect from observed entity and + * cease any change notification */ + void disconnnect(); + + }; + + + + +}} // lib::time +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b5da538ca..1a36e067e 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6782,7 +6782,7 @@ Thus no server and no network connection is needed. Simply open the file in your * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
-
+
Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first relevant point to note is that for quantised time values, the effect of a change can be disproportional to the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the amount of the change itself might be related to a time grid.
 
 The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
@@ -6843,27 +6843,30 @@ As rationale, consider the following
 * by materialising a quantised change prior to applying it, we get the "chaining effect"
 
 ----------
-!!! solutions ideas{{red{WIP 12/10}}}
+this leads to the following solutions approach
 * time values are immutable (as far as possible)
-* only allow to assign a completely new setting
-* only accept raw time values for redefining a quantised interval
-* only accept an abstract //modification object.// &rarr;{{red{No! any kind of abstraction requires an indirection}}}
+* for simple time calculations the ~TimeVar is sufficient
+* but we allow to fed an abstract //modification object.//
+* ''nudge by unit(s)'' is subsumed here as a special case
 
-{{red{Question 3/11}}} -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)?
+As the actual change logic to apply depends both on the target value and the kind of the change, we end up with //double dispatch.//
+
+ -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)?
 Rationale: allowing mutations for Time bears the danger of making ~TimeVar obsolete
 * //4/11 yes, implementing it now this way....//
 * we have ~TimeVar, so the only thing that //needs// to be mutable is a whole object &rArr; ~TimeSpan
 * err, because MObject will include a Duration separate from the start time in the Placement, Duration needs to be mutable too
 
 !!!usage considerations
-Question is: how fine grained and configurable needs this to be?
-* for example, when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
-* likely we need a "nudge by unit(s)"
+{{red{Question 5/11}}} when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
 
-!!! design draft
+!!!design draft
 The special focus of this problem seems to lead itself to a __visitor pattern__ based implementation. Because the basic hierarchy of applicable types is fixed, but the behaviour is open ended (and not yet fully determined). A conventional implementation would scatter this behaviour over all the time entities, thus making it hard to understand and reason about. The classical ~GoF cyclic visitor solution to the contrary allows us to arrange closely related behaviour into thematically grouped visitor classes. As a plus, the concrete action can be bound dynamically, allowing for more flexibility when it comes to dealing with the intricate situations when a quantised time span (= a clip) recieves a quantised mutation (= is re-alligend to a possibly different frame grid)
 
 Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}} function, which is defined to reflect back into the (virtual) visitation functions of the Mutation Interface. Additionally, we provide some static convenience shortcuts right in the Mutation interface, performing the trivial mutations.
+
+!!!life value changes and monitoring
+Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
 
From 9004818d6353aac9154a3f2dc765174b23834e1a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 26 May 2011 03:05:52 +0200 Subject: [PATCH 008/108] further planning the features of time::Change --- src/lib/time/control.hpp | 11 +++ src/proc/play/play-service.hpp | 1 + tests/40components.tests | 5 + tests/lib/time/time-control-test.cpp | 137 +++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 tests/lib/time/time-control-test.cpp diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 5422daf45..29a16023a 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -40,6 +40,17 @@ ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker ** which can be dragged, a modifiable selection or the animated playhead cursor. ** + ** \par implementation notes + ** - the validity of a given combination of change and target is checked immediately, + ** when connecting to the target. Depending on the situation, the actual changes later + ** are subject to specific treatment (e.g. frame quantisation) + ** - by default time::Control is not threadsafe. But, as each change is basically + ** processed within its own call context (function invocation), parallelism is only + ** a concern with respect to the value finally visible within the target. + ** - the change notification is processed right away, after applying the change to the + ** target; in all cases, the effective change value is what will be propagated, \em not + ** the content of the target after applying the change + ** ** @todo WIP-WIP-WIP ** */ diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index ee9a04188..f2b8e2d4d 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" +#include "include/play-facade.h" #include //#include diff --git a/tests/40components.tests b/tests/40components.tests index 22defbc85..aff1800f8 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -688,6 +688,11 @@ return: 0 END +PLANNED "Life changing time specifications with feedback" TimeControl_test < + + 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 "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" +#include "lib/time/mutation.hpp" +#include "proc/asset/meta/time-grid.hpp" +#include "lib/util.hpp" + +#include +#include +#include + +using boost::lexical_cast; +using util::isnil; +using std::cout; +using std::endl; +using std::string; + + +namespace lib { +namespace time{ +namespace test{ + + using asset::meta::TimeGrid; + + namespace { + inline string + pop (Arg arg) + { + if (isnil (arg)) return ""; + string entry = arg[0]; + arg.erase (arg.begin()); + return entry; + } + } + + + + + /**************************************************************** + * @test use the time::Control to push a sequence of modifications + * to various time entities; in all cases, a suitable change + * should be imposed to the target and then a notification signal + * should be invoked. + * @todo cover the cases..... + * - change to a given value + * - change by an offset + * - change using a grid value + * - apply an (grid) increment + */ + class TimeControl_test : public Test + { + gavl_time_t + random_or_get (string arg) + { + if (isnil(arg)) + return gavl_time_t (1 + (rand() % 100000)) * GAVL_TIME_SCALE; + else + return lexical_cast (arg); + } + + struct TestValues + { + TimeVar var; + Duration dur; + TimeSpan span; + QuTime quant; + + TestValues (TimeValue o) + : var(o) + , dur(o) + , span(o, Offset(o)) + , quant(o, "test_grid") + { } + }; + + + virtual void + run (Arg arg) + { + TimeValue o (random_or_get (pop(arg))); + TimeValue c (random_or_get (pop(arg))); + CHECK (o != c, "unsuitable testdata"); + + // using a 25fps-grid, but with an time origin offset by 1/50sec + TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50))); + + QuTime qChange (c, "test_grid"); + FrameNr count(qChange); + + verifyBasics(); + runningMutations(); + } + + + void + verifyBasics() + { + } + + + void + runningMutations () + { + } + }; + + + /** Register this test class... */ + LAUNCHER (TimeControl_test, "unit common"); + + + +}}} // namespace lib::time::test From eb5e0ab9ff8951d56d8cd265accd16ea5d484396 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 27 May 2011 04:16:36 +0200 Subject: [PATCH 009/108] WIP: start building a Test for time::Control. Need Cartesian product of cases... --- src/lib/meta/generator-combinations.hpp | 106 ++++++++++++++++++++++++ src/lib/meta/typelist-util.hpp | 66 ++++++++++++--- tests/40components.tests | 2 +- tests/lib/meta/typelist-diagnostics.hpp | 8 +- tests/lib/meta/typelist-manip-test.cpp | 4 +- tests/lib/time/time-control-test.cpp | 21 ++++- wiki/renderengine.html | 4 +- 7 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 src/lib/meta/generator-combinations.hpp diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp new file mode 100644 index 000000000..a74eaea06 --- /dev/null +++ b/src/lib/meta/generator-combinations.hpp @@ -0,0 +1,106 @@ +/* + GENERATOR-COMBINATIONS.hpp - generate combinations and variations + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +/** @file generator-combinations.hpp + ** Metaprogramming facilities to generate combination cases. + ** Similar to the plain typelist-based generators, a custom supplied + ** template will be instantiated with combinations of the parameter types + ** and then mixed into the resulting type + ** + ** @see generator-combinations-test.cpp + ** @see generator.hpp + ** + */ + + +#ifndef LUMIERA_META_GENERATOR_COMBINATIONS_H +#define LUMIERA_META_GENERATOR_COMBINATIONS_H + +#include "lib/meta/typelist.hpp" +#include "lib/meta/typelist-util.hpp" +#include "lib/meta/generator.hpp" + + + +namespace lumiera { +namespace typelist{ + + + template + class CartesianProduct + : Distribute< typename TYPES_1::List + , typename TYPES_2::List + > + { }; + + + template + struct PickFirst; + + template + struct PickFirst > + { + typedef TY Type; + }; + + + template + struct PickSecond; + + template + struct PickSecond > + { + typedef typename PickFirst::Type Type; + }; + + + + template< template class _X_> + struct PickParametersFromSublist + { + template + struct CaseInstantiation + : _X_< PickFirst::Type + , PickSecond::Type + , BASE + > + { }; + }; + + + template + < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from + , template class _X_ ///< template with two arg types and a base type + , class BASE = NullType + > + class InstantiateChainedCombinations + : InstantiateChained< CartesianProduct::List + , PickParametersFromSublist<_X_>::template CaseInstantiation + , BASE + > + { }; + + + +}} // namespace lumiera::typelist +#endif diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index cf66a791a..55fdd4395 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -21,6 +21,34 @@ */ +/** @file typelist-util.hpp + ** Metaprogramming: Helpers for manipulating lists-of-types. + ** Sometimes, we use metaprogramming to generate a variation of concrete + ** implementations by combining some basic building blocks. Typically, there + ** is a number of similar, but not suitably related types involved. We want to + ** process those types using a common scheme, without being forced to squeeze + ** all those types into a artificial inheritance relationship. Now, generating + ** some kind of common factory or adapter, while mixing in pieces of code tailored + ** specifically to the individual types, allows still to build a common processing + ** in such situations. + ** + ** The facilities in this header provide the basics of simple functional list + ** processing (mostly with tail recursion). Usually, there is one template parameter + ** TYPES, which accepts a \em Type-list. The result of the processing step is then + ** accessible as an embedded typedef named \c List . Here, all of the 'processing' + ** to calculate this result is performed by the compiler, as a side-effect of + ** figuring out the resulting concrete type. At run time, in the generated + ** code, typically the resulting classes are empty, maybe just + ** exposing a specifically built-up function. + ** + ** @see generator.hpp + ** @see typelist-manip-test.cpp + ** @see TimeControl_test usage example + ** @see typelist.hpp + ** + */ + + #ifndef LUMIERA_META_TYPELIST_UTIL_H #define LUMIERA_META_TYPELIST_UTIL_H @@ -34,6 +62,7 @@ namespace typelist{ /** * Metafunction counting the number of Types in the collection + * @return an embedded constant \c value holding the result */ template struct count; @@ -80,6 +109,7 @@ namespace typelist{ > List; }; + /** conditional node: skip an element based on evaluating a predicate */ template struct CondNode { typedef TAIL Next; }; @@ -202,7 +232,7 @@ namespace typelist{ typedef T Head; ///< first element typedef Node First; ///< a list containing the first element typedef TYPES Tail; ///< remainder of the list starting with the second elm. - typedef typename SplitLast::List Prefix;///< all of the list, up to but extcluding the last element + typedef typename SplitLast::List Prefix;///< all of the list, up to but excluding the last element typedef typename SplitLast::Type End; ///< the last element typedef Node Last; ///< a list containing the last element }; @@ -245,6 +275,12 @@ namespace typelist{ + /** + * build a list-of lists, where each element of the first arg list + * gets in turn prepended to all elements of the second arg list. + * Can be used to build all possible combinations from two + * sources, i.e. the Cartesian product. + */ template struct Distribute { typedef typename PrefixAll::List List; }; @@ -261,24 +297,32 @@ namespace typelist{ - /** use a permutation generator - * for creating a list of all possible combinations + /** + * build all possible combinations, based on a enumeration of the basic cases. + * For each of the types in the argument list, an "enumeration generator" template is invoked, + * yielding a list of the possible base cases. These base cases are then combined with all the + * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered" + * means that the base cases of the n-th element will appear in the n-th position of the + * resulting lists, + * + * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a + * list-of-lists, featuring all possibilities to combine these distinct toggles. */ template< class X - , template class _PERMU_> - struct Combine { typedef typename Distribute< typename _PERMU_::List + , template class _ENUM_> + struct Combine { typedef typename Distribute< typename _ENUM_::List , Node >::List List; }; - template< template class _PERMU_> - struct Combine { typedef Node List; }; + template< template class _ENUM_> + struct Combine { typedef NodeNull List; }; template< class TY, class TYPES - , template class _PERMU_> - struct Combine,_PERMU_> { typedef typename Distribute< typename _PERMU_::List - , typename Combine::List + , template class _ENUM_> + struct Combine,_ENUM_> { typedef typename Distribute< typename _ENUM_::List + , typename Combine::List >::List List; }; - /** permutation generator for the Combine metafunction, + /** enumeration generator for the Combine metafunction, * yielding an "on" and "off" case */ template diff --git a/tests/40components.tests b/tests/40components.tests index aff1800f8..3405644a7 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -806,7 +806,7 @@ return: 0 END -TEST "typelist manipulation" TypeListManipl_test <-<2>-<3>- out: List2 :-<5>-<6>-<7>- out: Added2 :-<3>-<4>-<5>- diff --git a/tests/lib/meta/typelist-diagnostics.hpp b/tests/lib/meta/typelist-diagnostics.hpp index bd924b1d8..b26eb7ae7 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -25,13 +25,15 @@ ** a Printer template usable for debugging the structure of a typelist built ** upon some simple debugging-style types. Examples being a Num template, ** or the Flag type. A Printer type generated from this template provides - ** a static \c print() function returning a string visualising the structure - ** of the typelist provided as parameter to the Printer template. - ** + ** a static \c print() function. The string returned from this function + ** visualises the structure of the typelist provided as parameter + ** to the Printer template. + ** ** @see typelist-manip-test.cpp ** @see config-flags-test.cpp ** */ + #ifndef META_TYPELIST_DIAGNOSTICS_H #define META_TYPELIST_DIAGNOSTICS_H diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index ce51270a5..9bf43c429 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -90,7 +90,7 @@ namespace test { * using a "predicate template" (metafunction) * - building combinations and permutations */ - class TypeListManipl_test : public Test + class TypeListManip_test : public Test { virtual void run (Arg) @@ -352,7 +352,7 @@ namespace test { /** Register this test class... */ - LAUNCHER (TypeListManipl_test, "unit common"); + LAUNCHER (TypeListManip_test, "unit common"); diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index 2742831ce..b233ef114 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -27,6 +27,7 @@ #include "lib/time/timequant.hpp" #include "lib/time/mutation.hpp" #include "proc/asset/meta/time-grid.hpp" +#include "lib/meta/typelist.hpp" #include "lib/util.hpp" #include @@ -45,6 +46,8 @@ namespace time{ namespace test{ using asset::meta::TimeGrid; + using lumiera::typelist::Types; + using lumiera::typelist::InstantiateForEach; namespace { inline string @@ -112,7 +115,7 @@ namespace test{ FrameNr count(qChange); verifyBasics(); - runningMutations(); + verifyMatrix_of_MutationCases(); } @@ -121,10 +124,24 @@ namespace test{ { } + template + struct TestCase4Target + { + void + performTestCases() + { + + } + }; + void - runningMutations () + verifyMatrix_of_MutationCases () { + typedef Types KindsOfTarget; + typedef InstantiateForEach TestMatrix; + + TestMatrix().performTestCases(); } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1a36e067e..83ca6e30c 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6782,7 +6782,7 @@ Thus no server and no network connection is needed. Simply open the file in your * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
-
+
Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first relevant point to note is that for quantised time values, the effect of a change can be disproportional to the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the amount of the change itself might be related to a time grid.
 
 The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
@@ -6866,7 +6866,7 @@ The special focus of this problem seems to lead itself to a __visitor pattern__
 Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}} function, which is defined to reflect back into the (virtual) visitation functions of the Mutation Interface. Additionally, we provide some static convenience shortcuts right in the Mutation interface, performing the trivial mutations.
 
 !!!life value changes and monitoring
-Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
+Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This {{{time::Control}}} element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
 
From 786ecbe82944658cf2686ecdccd3d98416f33725 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 27 May 2011 21:27:55 +0200 Subject: [PATCH 010/108] WIP draft test helper for generating test cases (Cartesian product) --- src/lib/meta/generator-combinations.hpp | 45 +++--- src/lib/meta/typelist-util.hpp | 20 +++ src/lib/meta/util.hpp | 18 +++ tests/40components.tests | 5 + .../lib/meta/generator-combinations-test.cpp | 137 ++++++++++++++++++ tests/lib/meta/typelist-diagnostics.hpp | 4 +- tests/lib/meta/typelist-manip-test.cpp | 27 +++- 7 files changed, 226 insertions(+), 30 deletions(-) create mode 100644 tests/lib/meta/generator-combinations-test.cpp diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp index a74eaea06..dae553182 100644 --- a/src/lib/meta/generator-combinations.hpp +++ b/src/lib/meta/generator-combinations.hpp @@ -25,7 +25,7 @@ ** Metaprogramming facilities to generate combination cases. ** Similar to the plain typelist-based generators, a custom supplied ** template will be instantiated with combinations of the parameter types - ** and then mixed into the resulting type + ** and then mixed into the resulting type ** ** @see generator-combinations-test.cpp ** @see generator.hpp @@ -54,40 +54,35 @@ namespace typelist{ { }; - template - struct PickFirst; - - template - struct PickFirst > - { - typedef TY Type; - }; - - - template - struct PickSecond; - - template - struct PickSecond > - { - typedef typename PickFirst::Type Type; - }; - - template< template class _X_> struct PickParametersFromSublist { template - struct CaseInstantiation - : _X_< PickFirst::Type - , PickSecond::Type + struct SingleCaseInstantiation + : _X_< Pick::Type + , Pick::Type , BASE > { }; }; + /** + * Build a Case matrix. + * The given parameter template _X_ + * will be instantiated for each possible combination + * of the elements from both parameter type-lists. + * All these instantiations will be chained up + * into a linear inheritance chain rooted + * at the BASE type. + * @note the custom-supplied template _X_ needs to take a 3rd parameter, + * and inherit from this parameter, in order to form that chain. + * Typically you'll define some (static) functions within that + * template, which then forward the call to the given BASE + * (and of course, that BASE then needs to define this + * function as well). + */ template < class TYPES_1, class TYPES_2 ///< the two type collections to pick combinations from , template class _X_ ///< template with two arg types and a base type @@ -95,7 +90,7 @@ namespace typelist{ > class InstantiateChainedCombinations : InstantiateChained< CartesianProduct::List - , PickParametersFromSublist<_X_>::template CaseInstantiation + , PickParametersFromSublist<_X_>::template SingleCaseInstantiation , BASE > { }; diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index 55fdd4395..428c23f97 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -97,6 +97,26 @@ namespace typelist{ }; + /** pick the n-th element from a typelist */ + template + struct Pick + { + typedef NullType Type; + }; + template + struct Pick, 0> + { + typedef TY Type; + }; + template + struct Pick, i> + { + typedef typename Pick::Type Type; + }; + + + + /** apply a transformation (template) to each type in the list */ template class _TRANS_> struct Apply { typedef TY List; }; diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index e6bb82209..97e60f1e1 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -38,6 +38,24 @@ namespace lumiera { namespace typelist { + /** Compile-time Type equality: + * Simple Trait template to pick up types considered + * \em identical by the compiler. + * @warning identical, not sub-type! + */ + template + class is_sameType + { + static const bool value = false; + }; + + template + class is_sameType + { + static const bool value = true; + }; + + /** semi-automatic detection if an instantiation is possible. * Requires help by the template to be tested, which needs to define * a typedef member \c is_defined. The embedded metafunction Test can be used diff --git a/tests/40components.tests b/tests/40components.tests index 3405644a7..340203dbf 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -1104,6 +1104,11 @@ return: 0 END +TEST "Typelist combinations generator" GeneratorCombinations_test < + + 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 "lib/test/run.hpp" +#include "lib/meta/generator.hpp" +#include "lib/meta/generator-combinations.hpp" +#include "meta/typelist-diagnostics.hpp" + +#include +#include + +using ::test::Test; +using std::string; +using std::cout; +using std::endl; + + +namespace lumiera { +namespace typelist{ +namespace test { + + + namespace { // test cases and data.... + + typedef Types< Num<1> + , Num<3> + , Num<5> + > Types1; + typedef Types< Num<2> + , Num<4> + , Num<6> + > Types2; + + + using boost::str; + using boost::format; + format fmt ("-<%u%u>%s"); + + /** + * A Test-Template to be instantiated + * for all possible combinations + * of the {Types1} x {Types2} + */ + template + struct TestCase + { + static string + visitAll() + { + T1 param1; + T2 param2; + return str(fmt % param1 % param2 + % BASE::visitAll()); + } + }; + + struct TestCase + { + static string + visitAll() + { + return "-|"; + } + }; + typedef TestCase IterationEnd; + + } // (End) test data + + + + + + + /************************************************************************* + * @test check utilities for generating case combinations. + * - verify the Cartesian product is built properly + * - instantiate a two-parameter test template + * for all those cases, as given by the + * Cartesian product of two Type collections + */ + class GeneratorCombinations_test : public Test + { + virtual void + run (Arg) + { + checkCartesian(); + checkCaseInstantiation(); + } + + + void + checkCartesian () + { + typedef CartesianProduct Cartesian; + DISPLAY (Cartesian); + } + + + void + checkCaseInstantiation () + { + typedef InstantiateChainedCombinations< Types1,Types2 + , TestCase + , IterationEnd > CombnationCases; + + cout << "All-Test-Combinations-" << CombnationCases::visitAll() << endl; + } + + }; + + + /** Register this test class... */ + LAUNCHER (GeneratorCombinations_test, "unit common"); + + + +}}} // namespace lumiera::typelist::test diff --git a/tests/lib/meta/typelist-diagnostics.hpp b/tests/lib/meta/typelist-diagnostics.hpp index b26eb7ae7..10e64154a 100644 --- a/tests/lib/meta/typelist-diagnostics.hpp +++ b/tests/lib/meta/typelist-diagnostics.hpp @@ -200,10 +200,10 @@ namespace typelist{ #define DISPLAY(NAME) \ - cout << STRINGIFY(NAME) << "\t:" << showType() << "\n"; + cout << STRINGIFY(NAME) << "\t:" << showType() << endl; #define DUMPVAL(NAME) \ - cout << STRINGIFY(NAME) << "\t:" << showDump (NAME) << "\n"; + cout << STRINGIFY(NAME) << "\t:" << showDump (NAME) << endl; diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 9bf43c429..3e34eab37 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -43,12 +43,11 @@ #include "lib/meta/typelist-util.hpp" #include "meta/typelist-diagnostics.hpp" -#include #include using ::test::Test; -using std::string; using std::cout; +using std::endl; namespace lumiera { @@ -96,6 +95,7 @@ namespace test { run (Arg) { check_diagnostics (); + check_pick_elm (); check_apply (); check_filter (); check_append (); @@ -113,7 +113,7 @@ namespace test { { // Explanation: the DISPLAY macro results in the following definition.... typedef InstantiateChained Contents_List1; - cout << "List1" << "\t:" << Contents_List1::print() << "\n"; + cout << "List1" << "\t:" << Contents_List1::print() << endl; // That is: we instantiate the "Printer" template for each of the types in List1, // forming an inheritance chain. I.e. the defined Type "Contents_List1" inherits @@ -124,6 +124,27 @@ namespace test { } + void + check_pick_elm () + { + Pick::Type e0; + Pick::Type e1; + Pick::Type e2; + + Pick::Type E3; + Pick::Type Nil; + Pick::Type Irrelevant; + + CHECK (5 == e0); + CHECK (6 == e1); + CHECK (7 == e2); + + CHECK (is_sameType::value); + CHECK (is_sameType::value); + CHECK (is_sameType::value); + } + + void check_append () { From ae36b2d941656faf63487e962f6b1ecc33ed7dca Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 01:46:06 +0200 Subject: [PATCH 011/108] stubs and adjustments to get it through the compiler --- src/include/interfaceproxy.hpp | 72 +++-- src/include/play-facade.h | 18 +- src/lib/meta/generator-combinations.hpp | 10 +- src/lib/meta/util.hpp | 4 +- src/lib/time/control.hpp | 7 +- src/proc/engine/dispatcher.hpp | 6 +- src/proc/engine/engine-service.hpp | 28 +- .../engine/worker/dummy-image-generator.cpp | 282 +++++++++--------- .../engine/worker/dummy-image-generator.hpp | 86 +++--- src/proc/engine/worker/tick-service.hpp | 148 +++++---- src/proc/play/dummy-play-connection.hpp | 38 ++- src/proc/play/dummy-player-service.cpp | 5 +- src/proc/play/dummy-player-service.hpp | 9 +- src/proc/play/output-manager.cpp | 5 +- src/proc/play/play-process.hpp | 54 ++-- src/proc/play/play-service.cpp | 95 ++---- src/proc/play/play-service.hpp | 58 ++-- tests/lib/meta/config-flags-test.cpp | 1 + .../lib/meta/generator-combinations-test.cpp | 8 +- tests/lib/meta/typelist-manip-test.cpp | 12 +- tests/lib/meta/typeseq-manip-test.cpp | 1 + tests/lib/time/time-control-test.cpp | 37 ++- 22 files changed, 474 insertions(+), 510 deletions(-) diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp index a488ce65f..54084f8fd 100644 --- a/src/include/interfaceproxy.hpp +++ b/src/include/interfaceproxy.hpp @@ -82,45 +82,43 @@ namespace lumiera { - namespace facade { - - /** error-ID for accessing a (currently) closed facade */ - LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE); +namespace facade { + /** error-ID for accessing a (currently) closed facade */ + LUMIERA_ERROR_DECLARE(FACADE_LIFECYCLE); + + + /********************************************************************* + * + */ + template + class Accessor + { + protected: + static FA* implProxy_; + + + public: + FA& + operator() () + { + if (implProxy_) + return *implProxy_; + else + throw error::State("Facade interface currently closed."); + } + }; - /********************************************************************* - * - */ - template - class Accessor - { - protected: - static FA* implProxy_; - - - public: - FA& - operator() () - { - if (implProxy_) - return *implProxy_; - else - throw error::State("Facade interface currently closed."); - } - }; - - template - void openProxy (IHA const&); - - template - void closeProxy (); - - template - class Proxy; - - - } // namespace facade + template + void openProxy (IHA const&); -} // namespace lumiera + template + void closeProxy (); + + template + class Proxy; + + +}} // namespace lumiera::facade #endif diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 01db5f5f5..33429323b 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -32,14 +32,16 @@ //#include "include/interfaceproxy.hpp" #include "lib/handle.hpp" #include "lib/iter-source.hpp" +#include "lib/time/control.hpp" #include "lib/time/timevalue.hpp" +#include "include/interfaceproxy.hpp" #include "proc/mobject/model-port.hpp" #include "proc/mobject/output-designation.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/track.hpp" #include "proc/play/output-manager.hpp" -#include "asset/timeline.hpp" -#include "asset/viewer.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/viewer.hpp" @@ -51,6 +53,8 @@ namespace proc { namespace lumiera { + + namespace time = lib::time; /****************************************************************** @@ -71,7 +75,7 @@ namespace lumiera { public: /** get an implementation instance of this service */ - static lumiera::facade::Accessor facade; + static lumiera::facade::Accessor facade; /** @@ -96,9 +100,9 @@ namespace lumiera { void adjustSpeed(double); ///< playback speed control void go(lib::time::Time); ///< skip to the given point in time - void controlPlayhead (lib::time::TimeControl & ctrl); - void controlDuration (lib::time::TimeControl & ctrl); - void controlLooping (lib::time::TimeControl & ctrl); + void controlPlayhead (time::Control & ctrl); + void controlDuration (time::Control & ctrl); + void controlLooping (time::Control & ctrl); void useProxyMedia (bool); void setQuality (uint); @@ -131,7 +135,7 @@ namespace lumiera { Controller perform(Clip); protected: - virtual ~Player(); + virtual ~Play(); }; diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp index dae553182..28790ec01 100644 --- a/src/lib/meta/generator-combinations.hpp +++ b/src/lib/meta/generator-combinations.hpp @@ -47,7 +47,7 @@ namespace typelist{ template - class CartesianProduct + struct CartesianProduct : Distribute< typename TYPES_1::List , typename TYPES_2::List > @@ -60,8 +60,8 @@ namespace typelist{ { template struct SingleCaseInstantiation - : _X_< Pick::Type - , Pick::Type + : _X_< typename Pick::Type + , typename Pick::Type , BASE > { }; @@ -88,8 +88,8 @@ namespace typelist{ , template class _X_ ///< template with two arg types and a base type , class BASE = NullType > - class InstantiateChainedCombinations - : InstantiateChained< CartesianProduct::List + struct InstantiateChainedCombinations + : InstantiateChained< typename CartesianProduct::List , PickParametersFromSublist<_X_>::template SingleCaseInstantiation , BASE > diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 97e60f1e1..14c7cd6a8 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -44,13 +44,13 @@ namespace lumiera { * @warning identical, not sub-type! */ template - class is_sameType + struct is_sameType { static const bool value = false; }; template - class is_sameType + struct is_sameType { static const bool value = true; }; diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp index 29a16023a..e5f44629c 100644 --- a/src/lib/time/control.hpp +++ b/src/lib/time/control.hpp @@ -79,7 +79,6 @@ namespace time { //LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated - typedef function timeSignal; /** * Interface: controller-element for retrieving and @@ -91,10 +90,12 @@ namespace time { */ template class Control - : Mutation - , timeSignal + : public Mutation + , public function { public: + typedef function timeSignal; + /** install a callback functor to be invoked * to notify for any changes to the observed * time entity */ diff --git a/src/proc/engine/dispatcher.hpp b/src/proc/engine/dispatcher.hpp index 5b4509953..7565c70eb 100644 --- a/src/proc/engine/dispatcher.hpp +++ b/src/proc/engine/dispatcher.hpp @@ -32,9 +32,9 @@ namespace engine { -// using lib::time::TimeSpan; -// using lib::time::FSecs; -// using lib::time::Time; + using lib::time::TimeSpan; + using lib::time::FSecs; + using lib::time::Time; // // class ExitNode; diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 599f4fe40..7798307b0 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -37,7 +37,7 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" // -//#include +#include //#include //#include @@ -66,31 +66,23 @@ namespace play { : boost::noncopyable { - string error_; - Subsys::SigTerm notifyTermination_; +// string error_; +// Subsys::SigTerm notifyTermination_; /* === Interface Lifecycle === */ - typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) - , DummyPlayer - > ServiceInstanceHandle; +// typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) +// , DummyPlayer +// > ServiceInstanceHandle; - lib::SingletonRef implInstance_; - ServiceInstanceHandle serviceInstance_; +// lib::SingletonRef implInstance_; +// ServiceInstanceHandle serviceInstance_; public: - DummyPlayerService(Subsys::SigTerm terminationHandle); + EngineService(); /////TODO (Subsys::SigTerm terminationHandle); - ~DummyPlayerService() { notifyTermination_(&error_); } - - - - /** conceptually, this serves as implementation - * of the DummyPlayer#start() function. But because - * this function sits \em behind the interface, it - * just returns an impl pointer. */ - ProcessImpl* start (LumieraDisplaySlot viewerHandle); + ~EngineService() { } /////TODO notifyTermination_(&error_); } }; diff --git a/src/proc/engine/worker/dummy-image-generator.cpp b/src/proc/engine/worker/dummy-image-generator.cpp index 44cf97cd1..577da2f94 100644 --- a/src/proc/engine/worker/dummy-image-generator.cpp +++ b/src/proc/engine/worker/dummy-image-generator.cpp @@ -22,149 +22,147 @@ * *****************************************************/ -#include "proc/play/dummy-image-generator.hpp" +#include "proc/engine/worker/dummy-image-generator.hpp" namespace proc { - namespace play { - - - - namespace { // implementation details - - - typedef unsigned char byte; - - inline int - clamp (const int &val, const int &maxval, const int &minval) - { - if(val > maxval) return maxval; - if(val < minval) return minval; - return val; - } - - inline void - rgb_to_yuv (int r, int g, int b, byte &y, byte &u, byte &v) - { - // This code isn't great, but it does the job - y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); - v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); - u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); - } - - - void - rgb_buffer_to_yuy2 (unsigned char *in, unsigned char *out) - { - for (uint i = 0; i < 320*240*2; i+=4) - { - byte y0, u0, v0; - const byte r0 = *(in++); - const byte g0 = *(in++); - const byte b0 = *(in++); - rgb_to_yuv(r0, g0, b0, y0, u0, v0); - - byte y1, u1, v1; - const byte r1 = *(in++); - const byte g1 = *(in++); - const byte b1 = *(in++); - rgb_to_yuv(r1, g1, b1, y1, u1, v1); - - out[i] = y0; - out[i + 1] = u0; - out[i + 2] = y1; - out[i + 3] = v0; - } } - - - } // (End) implementation details - - - - - DummyImageGenerator::DummyImageGenerator(uint fps) - : current_(0) - , frame_(0) - , fps_(fps) - { } - - - LumieraDisplayFrame - DummyImageGenerator::next() - { - - ++frame_; - if(frame_ > 2 * fps_) - frame_ = 0; - - if(frame_ < 1 * fps_) - { - // create random snow... - for(int i = 0; i < 320*240*3; i+=3) - { - byte value ( rand() ); - buf_[i] = value; - buf_[i+1] = value; - buf_[i+2] = value; - } - } - else - { // create a colour strip pattern - typedef unsigned char Row[320 * 3]; - - unsigned char * row = buf_; - - // create a colour strip pattern in the first row... - for(int x = 0; x < 320; ++x) - { - byte &r = row[x*3]; - byte &g = row[x*3+1]; - byte &b = row[x*3+2]; - - if (x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; - else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; - else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; - else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; - else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; - else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; - else r = 0x00, g = 0x00, b = 0xC0; - } - - // fill remaining rows of the frame with the same pattern - for(int y = 1; y < 240; ++y) - memcpy(buf_ + y*sizeof(Row), row, sizeof(Row)); - - } - - // select output buffer to return - LumieraDisplayFrame outBuff; - - if (!current_) - { - outBuff = outFrame_A_; - current_= 1; - } - else - { - outBuff = outFrame_B_; - current_= 0; - } - - rgb_buffer_to_yuy2(buf_, outBuff); - return outBuff; - - } - - - LumieraDisplayFrame - DummyImageGenerator::current() - { - if (!current_) return outFrame_A_; - else return outFrame_B_; - } - - - - } // namespace play +namespace node { -} // namespace proc + + + namespace { // implementation details + + + typedef unsigned char byte; + + inline int + clamp (const int &val, const int &maxval, const int &minval) + { + if(val > maxval) return maxval; + if(val < minval) return minval; + return val; + } + + inline void + rgb_to_yuv (int r, int g, int b, byte &y, byte &u, byte &v) + { + // This code isn't great, but it does the job + y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); + v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); + u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); + } + + + void + rgb_buffer_to_yuy2 (unsigned char *in, unsigned char *out) + { + for (uint i = 0; i < 320*240*2; i+=4) + { + byte y0, u0, v0; + const byte r0 = *(in++); + const byte g0 = *(in++); + const byte b0 = *(in++); + rgb_to_yuv(r0, g0, b0, y0, u0, v0); + + byte y1, u1, v1; + const byte r1 = *(in++); + const byte g1 = *(in++); + const byte b1 = *(in++); + rgb_to_yuv(r1, g1, b1, y1, u1, v1); + + out[i] = y0; + out[i + 1] = u0; + out[i + 2] = y1; + out[i + 3] = v0; + } } + + + } // (End) implementation details + + + + + DummyImageGenerator::DummyImageGenerator(uint fps) + : current_(0) + , frame_(0) + , fps_(fps) + { } + + + LumieraDisplayFrame + DummyImageGenerator::next() + { + + ++frame_; + if(frame_ > 2 * fps_) + frame_ = 0; + + if(frame_ < 1 * fps_) + { + // create random snow... + for(int i = 0; i < 320*240*3; i+=3) + { + byte value ( rand() ); + buf_[i] = value; + buf_[i+1] = value; + buf_[i+2] = value; + } + } + else + { // create a colour strip pattern + typedef unsigned char Row[320 * 3]; + + unsigned char * row = buf_; + + // create a colour strip pattern in the first row... + for(int x = 0; x < 320; ++x) + { + byte &r = row[x*3]; + byte &g = row[x*3+1]; + byte &b = row[x*3+2]; + + if (x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; + else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; + else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; + else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; + else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; + else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; + else r = 0x00, g = 0x00, b = 0xC0; + } + + // fill remaining rows of the frame with the same pattern + for(int y = 1; y < 240; ++y) + memcpy(buf_ + y*sizeof(Row), row, sizeof(Row)); + + } + + // select output buffer to return + LumieraDisplayFrame outBuff; + + if (!current_) + { + outBuff = outFrame_A_; + current_= 1; + } + else + { + outBuff = outFrame_B_; + current_= 0; + } + + rgb_buffer_to_yuy2(buf_, outBuff); + return outBuff; + + } + + + LumieraDisplayFrame + DummyImageGenerator::current() + { + if (!current_) return outFrame_A_; + else return outFrame_B_; + } + + + +}} // namespace proc::node diff --git a/src/proc/engine/worker/dummy-image-generator.hpp b/src/proc/engine/worker/dummy-image-generator.hpp index c893962b8..e364423ef 100644 --- a/src/proc/engine/worker/dummy-image-generator.hpp +++ b/src/proc/engine/worker/dummy-image-generator.hpp @@ -35,8 +35,8 @@ */ -#ifndef PROC_PLAY_DUMMY_IMAGE_GENERATOR_H -#define PROC_PLAY_DUMMY_IMAGE_GENERATOR_H +#ifndef PROC_NODE_DUMMY_IMAGE_GENERATOR_H +#define PROC_NODE_DUMMY_IMAGE_GENERATOR_H #include "lib/error.hpp" @@ -44,47 +44,45 @@ namespace proc { - namespace play { +namespace node { + + + class DummyImageGenerator + { + + unsigned char buf_[320 * 240 * 3]; ///< working buffer for next frame + + unsigned char outFrame_A_[320 * 240 * 4]; ///< output frame 1 + unsigned char outFrame_B_[320 * 240 * 4]; ///< output frame 2 + + uint current_; + uint frame_; + uint fps_; + + + public: + DummyImageGenerator(uint fps); + + ~DummyImageGenerator() { } + + /** generate the next frame and + * occupy the alternate buffer. + * @return the buffer containing the new frame + */ + LumieraDisplayFrame next(); + + /** just re-return a pointer to the current frame + * without generating any new image data */ + LumieraDisplayFrame current(); + + + private: + + }; - - class DummyImageGenerator - { - - unsigned char buf_[320 * 240 * 3]; ///< working buffer for next frame - - unsigned char outFrame_A_[320 * 240 * 4]; ///< output frame 1 - unsigned char outFrame_B_[320 * 240 * 4]; ///< output frame 2 - - uint current_; - uint frame_; - uint fps_; - - - public: - DummyImageGenerator(uint fps); - - ~DummyImageGenerator() { } - - /** generate the next frame and - * occupy the alternate buffer. - * @return the buffer containing the new frame - */ - LumieraDisplayFrame next(); - - /** just re-return a pointer to the current frame - * without generating any new image data */ - LumieraDisplayFrame current(); - - - private: - - }; - - - - - } // namespace play - -} // namespace proc -#endif // PROC_PLAY_DUMMY_IMAGE_GENERATOR_H + + + +}} // namespace proc::node +#endif diff --git a/src/proc/engine/worker/tick-service.hpp b/src/proc/engine/worker/tick-service.hpp index a67116020..3f93dee56 100644 --- a/src/proc/engine/worker/tick-service.hpp +++ b/src/proc/engine/worker/tick-service.hpp @@ -46,82 +46,80 @@ namespace proc { - namespace play { +namespace node { - using std::tr1::function; - - - - /************************************************************ - * Tick generating service for a periodic callback, - * with adjustable frequency. Quick'n dirty implementation! - */ - class TickService - : backend::ThreadJoinable - { - typedef function Tick; - volatile uint timespan_; - - /** poll interval for new settings in wait state */ - static const uint POLL_TIMEOUT = 1000; - - public: - TickService (Tick callback) - : ThreadJoinable("Tick generator (dummy)" - , bind (&TickService::timerLoop, this, callback) - ) - { - INFO (proc, "TickService started."); - } - - ~TickService () - { - timespan_ = 0; - this->join(); - usleep (200000); // additional delay allowing GTK to dispatch the last output - - INFO (proc, "TickService shutdown."); - } - - - /** set the periodic timer to run with a given frequency, - * starting \em now. Well, not actually now, but at the next - * opportunity. It should be \em now, but this implementation - * is sloppy! setting fps==0 halts (pauses) the timer. - */ - void activate (uint fps) - { - REQUIRE ( 0==fps - ||( 1000000/fps < std::numeric_limits::max() - && 1000000/fps > POLL_TIMEOUT)); - if (fps) - timespan_ = 1000000/fps; // microseconds per tick - else - timespan_ = POLL_TIMEOUT; - } - - - private: - void timerLoop(Tick periodicFun) - { - timespan_ = POLL_TIMEOUT; - while (0 < timespan_) - { - if (timespan_ > POLL_TIMEOUT) - periodicFun(); - - usleep (timespan_); - } - TRACE (proc_dbg, "Tick Thread timer loop exiting..."); - } + using std::tr1::function; - }; - - - - - } // namespace play + + + /************************************************************ + * Tick generating service for a periodic callback, + * with adjustable frequency. Quick'n dirty implementation! + */ + class TickService + : backend::ThreadJoinable + { + typedef function Tick; + volatile uint timespan_; + + /** poll interval for new settings in wait state */ + static const uint POLL_TIMEOUT = 1000; + + public: + TickService (Tick callback) + : ThreadJoinable("Tick generator (dummy)" + , bind (&TickService::timerLoop, this, callback) + ) + { + INFO (proc, "TickService started."); + } + + ~TickService () + { + timespan_ = 0; + this->join(); + usleep (200000); // additional delay allowing GTK to dispatch the last output + + INFO (proc, "TickService shutdown."); + } + + + /** set the periodic timer to run with a given frequency, + * starting \em now. Well, not actually now, but at the next + * opportunity. It should be \em now, but this implementation + * is sloppy! setting fps==0 halts (pauses) the timer. + */ + void activate (uint fps) + { + REQUIRE ( 0==fps + ||( 1000000/fps < std::numeric_limits::max() + && 1000000/fps > POLL_TIMEOUT)); + if (fps) + timespan_ = 1000000/fps; // microseconds per tick + else + timespan_ = POLL_TIMEOUT; + } + + + private: + void timerLoop(Tick periodicFun) + { + timespan_ = POLL_TIMEOUT; + while (0 < timespan_) + { + if (timespan_ > POLL_TIMEOUT) + periodicFun(); + + usleep (timespan_); + } + TRACE (proc_dbg, "Tick Thread timer loop exiting..."); + } -} // namespace proc -#endif // PROC_PLAY_TICKSERVICE_H + }; + + + + +}} // namespace proc::node +#endif diff --git a/src/proc/play/dummy-play-connection.hpp b/src/proc/play/dummy-play-connection.hpp index 7186987df..0f002cb1b 100644 --- a/src/proc/play/dummy-play-connection.hpp +++ b/src/proc/play/dummy-play-connection.hpp @@ -37,33 +37,31 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" // -//#include +#include //#include //#include namespace proc { - namespace play { - +namespace play { + // using std::string; // using lumiera::Subsys; // using lumiera::Display; // using lumiera::DummyPlayer; - - - - /******************************************************************** - */ - class DummyPlayConnection - : boost::noncopyable - { - - }; - - - - - } // namespace play - -} // namespace proc + + + + /******************************************************************** + */ + class DummyPlayConnection + : boost::noncopyable + { + + }; + + + + +}} // namespace proc::play #endif diff --git a/src/proc/play/dummy-player-service.cpp b/src/proc/play/dummy-player-service.cpp index ea443f709..d4905f2d4 100644 --- a/src/proc/play/dummy-player-service.cpp +++ b/src/proc/play/dummy-player-service.cpp @@ -22,8 +22,8 @@ #include "proc/play/dummy-player-service.hpp" -#include "proc/play/dummy-image-generator.hpp" -#include "proc/play/tick-service.hpp" +#include "proc/engine/worker/dummy-image-generator.hpp" +#include "proc/engine/worker/tick-service.hpp" #include "lib/singleton.hpp" extern "C" { @@ -47,6 +47,7 @@ namespace proc { using std::tr1::bind; + namespace { // hidden local details of the service implementation.... /** details of how the DummyPlayer service can be started diff --git a/src/proc/play/dummy-player-service.hpp b/src/proc/play/dummy-player-service.hpp index 75e0f3d78..5cf8438ac 100644 --- a/src/proc/play/dummy-player-service.hpp +++ b/src/proc/play/dummy-player-service.hpp @@ -52,6 +52,11 @@ namespace proc { + namespace node { + class DummyImageGenerator; + class TickService; + } + namespace play { using std::string; @@ -59,9 +64,9 @@ namespace proc { using lumiera::Display; using lumiera::DummyPlayer; + using proc::node::DummyImageGenerator; + using proc::node::TickService; - class DummyImageGenerator; - class TickService; /******************************************************************** diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp index 39887ec80..6ffca994f 100644 --- a/src/proc/play/output-manager.cpp +++ b/src/proc/play/output-manager.cpp @@ -21,10 +21,11 @@ * *****************************************************/ -#include "play/output-manager.hpp" +#include "proc/play/output-manager.hpp" +namespace proc { namespace play { @@ -40,4 +41,4 @@ namespace play { -} // namespace play +}} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index f9d0ea85c..44a259c33 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -36,41 +36,39 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" // -//#include +#include //#include //#include namespace proc { - namespace play { - +namespace play { + // using std::string; // using lumiera::Subsys; // using lumiera::Display; // using lumiera::DummyPlayer; - - - - /****************************************************** - * Actual implementation of the DummyPlayer service. - * Creating an instance of this class automatically - * registers the interface lumieraorg_DummyPlayer with - * the Lumiera Interface/Plugin system and creates - * a forwarding proxy within the application core to - * route calls through this interface. - */ - class PlayerProcess - : boost::noncopyable - { - - public: - - }; - - - - - } // namespace play - -} // namespace proc + + + + /****************************************************** + * Actual implementation of the DummyPlayer service. + * Creating an instance of this class automatically + * registers the interface lumieraorg_DummyPlayer with + * the Lumiera Interface/Plugin system and creates + * a forwarding proxy within the application core to + * route calls through this interface. + */ + class PlayerProcess + : boost::noncopyable + { + + public: + + }; + + + + +}} // namespace proc::play #endif diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 1b8cc463e..7ee13c480 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -32,75 +32,32 @@ -namespace proc { - namespace play{ - - using std::string; - using lumiera::Subsys; - using std::auto_ptr; - using boost::scoped_ptr; - using std::tr1::bind; - - - namespace { // hidden local details of the service implementation.... - - /** details of how the DummyPlayer service can be started - * and used as independent "subsystem" within main() */ - class DummyPlayerSubsysDescriptor - : public Subsys - { - operator string () const { return "Dummy-Player"; } - - - bool - shouldStart (lumiera::Option&) - { - return false; // for now the DummyPlayerService only comes "up" as dependency, - } // but doesn't start as a subsystem on it's own. - - bool - start (lumiera::Option&, Subsys::SigTerm terminationHandle) - { - ASSERT (!thePlayer_); - - thePlayer_.reset (new DummyPlayerService (terminationHandle)); - return true; - } - - /** manages the actual (single) instance of the player service impl */ - scoped_ptr thePlayer_; - - - void - triggerShutdown () throw() - { - thePlayer_.reset(0); - // note: shutdown of the DummyPlayerService instance may block - // for a short period, until termination of all tick services - } - - bool - checkRunningState () throw() - { - return (thePlayer_); - } - }; - - lib::Singleton theDummyPlayerDescriptor; - - - - - - /* ================== define an lumieraorg_DummyPlayer instance ======================= */ - - - } // (End) hidden service impl details - - - - - /** */ +namespace proc { +namespace play { + +//using std::string; +//using lumiera::Subsys; +//using std::auto_ptr; +//using boost::scoped_ptr; +//using std::tr1::bind; + namespace { // hidden local details of the service implementation.... + + + + + + + /* ================== define an lumieraorg_DummyPlayer instance ======================= */ + + + } // (End) hidden service impl details + + + + + /** */ + + }} // namespace proc::play diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index f2b8e2d4d..03780f134 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -41,37 +41,35 @@ namespace proc { - namespace play { +namespace play { + + using std::string; +//using lumiera::Subsys; +//using lumiera::Display; +//using lumiera::DummyPlayer; + - using std::string; - using lumiera::Subsys; - using lumiera::Display; - using lumiera::DummyPlayer; - - // class DummyImageGenerator; // class TickService; - - - - /****************************************************** - * Interface: Player subsystem. - */ - class PlayService - : boost::noncopyable - { - - public: - PlayService(Subsys::SigTerm terminationHandle); - - ~PlayService() { notifyTermination_(&error_); } - - }; - - - - - } // namespace play - -} // namespace proc + + + + /****************************************************** + * Interface: Player subsystem. + */ + class PlayService + : boost::noncopyable + { + + public: + PlayService(); /////TODO Subsys::SigTerm terminationHandle); + + ~PlayService() { }/////TODO notifyTermination_(&error_); } + + }; + + + + +}} // namespace proc::play #endif diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp index 572bc3adf..57a5621d1 100644 --- a/tests/lib/meta/config-flags-test.cpp +++ b/tests/lib/meta/config-flags-test.cpp @@ -52,6 +52,7 @@ using ::test::Test; using std::string; using std::cout; +using std::endl; namespace lumiera { diff --git a/tests/lib/meta/generator-combinations-test.cpp b/tests/lib/meta/generator-combinations-test.cpp index be6b322fe..2a55998e2 100644 --- a/tests/lib/meta/generator-combinations-test.cpp +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -54,7 +54,7 @@ namespace test { using boost::str; using boost::format; - format fmt ("-<%u%u>%s"); + format formatted ("-<%u%u>%s"); /** * A Test-Template to be instantiated @@ -63,17 +63,19 @@ namespace test { */ template struct TestCase + : BASE { static string visitAll() { T1 param1; T2 param2; - return str(fmt % param1 % param2 - % BASE::visitAll()); + return str(formatted % param1 % param2 + % BASE::visitAll()); } }; + template<> struct TestCase { static string diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 3e34eab37..09ab07390 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -131,17 +131,17 @@ namespace test { Pick::Type e1; Pick::Type e2; - Pick::Type E3; - Pick::Type Nil; - Pick::Type Irrelevant; + typedef Pick::Type E3; + typedef Pick::Type Nil; + typedef Pick::Type Irrelevant; CHECK (5 == e0); CHECK (6 == e1); CHECK (7 == e2); - CHECK (is_sameType::value); - CHECK (is_sameType::value); - CHECK (is_sameType::value); + CHECK ((is_sameType::value)); + CHECK ((is_sameType::value)); + CHECK ((is_sameType::value)); } diff --git a/tests/lib/meta/typeseq-manip-test.cpp b/tests/lib/meta/typeseq-manip-test.cpp index 1221c610b..6efffd272 100644 --- a/tests/lib/meta/typeseq-manip-test.cpp +++ b/tests/lib/meta/typeseq-manip-test.cpp @@ -48,6 +48,7 @@ using ::test::Test; using std::string; using std::cout; +using std::endl; namespace lumiera { diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp index b233ef114..a89b96e48 100644 --- a/tests/lib/time/time-control-test.cpp +++ b/tests/lib/time/time-control-test.cpp @@ -27,7 +27,7 @@ #include "lib/time/timequant.hpp" #include "lib/time/mutation.hpp" #include "proc/asset/meta/time-grid.hpp" -#include "lib/meta/typelist.hpp" +#include "lib/meta/generator-combinations.hpp" #include "lib/util.hpp" #include @@ -47,7 +47,7 @@ namespace test{ using asset::meta::TimeGrid; using lumiera::typelist::Types; - using lumiera::typelist::InstantiateForEach; + using lumiera::typelist::InstantiateChainedCombinations; namespace { inline string @@ -58,6 +58,24 @@ namespace test{ arg.erase (arg.begin()); return entry; } + + + template + struct TestCase + : BASE + { + void + performTestCases() + { + + } + }; + + struct IterationEnd + { + void performTestCases() { } + }; + } @@ -124,22 +142,17 @@ namespace test{ { } - template - struct TestCase4Target - { - void - performTestCases() - { - - } - }; void verifyMatrix_of_MutationCases () { typedef Types KindsOfTarget; - typedef InstantiateForEach TestMatrix; + typedef Types KindsOfSource; + typedef InstantiateChainedCombinations< KindsOfTarget + , KindsOfSource + , TestCase + , IterationEnd > TestMatrix; TestMatrix().performTestCases(); } From b25d85e1ddcb0d776265f6c30ecab0452c1c0f99 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 01:46:26 +0200 Subject: [PATCH 012/108] ProcNode hierarchy cleanup --- src/proc/engine/mask.hpp | 4 +- src/proc/engine/pluginadapter.hpp | 4 +- src/proc/engine/projector.cpp | 34 ---------------- src/proc/engine/projector.hpp | 46 --------------------- src/proc/engine/trafo.cpp | 33 --------------- src/proc/engine/trafo.hpp | 67 ------------------------------- 6 files changed, 4 insertions(+), 184 deletions(-) delete mode 100644 src/proc/engine/projector.cpp delete mode 100644 src/proc/engine/projector.hpp delete mode 100644 src/proc/engine/trafo.cpp delete mode 100644 src/proc/engine/trafo.hpp diff --git a/src/proc/engine/mask.hpp b/src/proc/engine/mask.hpp index d2973550f..b805467a3 100644 --- a/src/proc/engine/mask.hpp +++ b/src/proc/engine/mask.hpp @@ -24,7 +24,7 @@ #ifndef ENGINE_MASK_H #define ENGINE_MASK_H -#include "proc/engine/trafo.hpp" +#include "proc/engine/procnode.hpp" @@ -32,7 +32,7 @@ namespace engine { - class Mask : public Trafo + class Mask : public ProcNode {}; diff --git a/src/proc/engine/pluginadapter.hpp b/src/proc/engine/pluginadapter.hpp index 25dabab66..19ed23f3b 100644 --- a/src/proc/engine/pluginadapter.hpp +++ b/src/proc/engine/pluginadapter.hpp @@ -24,7 +24,7 @@ #ifndef ENGINE_PLUGINADAPTER_H #define ENGINE_PLUGINADAPTER_H -#include "proc/engine/trafo.hpp" +#include "proc/engine/procnode.hpp" @@ -37,7 +37,7 @@ namespace engine * Effects processors are typically defined in a separate library and * will be loaded at runtime using Lumiera's plugin interface. */ - class PluginAdapter : public Trafo + class PluginAdapter : public ProcNode { ///////////// }; diff --git a/src/proc/engine/projector.cpp b/src/proc/engine/projector.cpp deleted file mode 100644 index 3b75702ed..000000000 --- a/src/proc/engine/projector.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - Projector - video ProcNode for scaling and translating image data - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler - - 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 "proc/engine/projector.hpp" - -namespace engine - { - - /** */ - - - - -} // namespace engine diff --git a/src/proc/engine/projector.hpp b/src/proc/engine/projector.hpp deleted file mode 100644 index b93552e4d..000000000 --- a/src/proc/engine/projector.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - PROJECTOR.hpp - video ProcNode for scaling and translating image data - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler - - 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. - -*/ - - -#ifndef ENGINE_PROJECTOR_H -#define ENGINE_PROJECTOR_H - -#include "proc/engine/trafo.hpp" - - - -namespace engine - { - - - /** - * Special video processing node used to scale and translate image data. - */ - class Projector : public Trafo - { - ////////////TODO adapt ctor - }; - - - -} // namespace engine -#endif diff --git a/src/proc/engine/trafo.cpp b/src/proc/engine/trafo.cpp deleted file mode 100644 index 636e59e4e..000000000 --- a/src/proc/engine/trafo.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - Trafo - transforming processing Node - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler - - 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 "proc/engine/trafo.hpp" - -namespace engine - { - - /** */ - - - -} // namespace engine diff --git a/src/proc/engine/trafo.hpp b/src/proc/engine/trafo.hpp deleted file mode 100644 index cb1933619..000000000 --- a/src/proc/engine/trafo.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - TRAFO.hpp - transforming processing Node - - Copyright (C) Lumiera.org - 2008, Hermann Vosseler - - 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. - -*/ - - -#ifndef ENGINE_TRAFO_H -#define ENGINE_TRAFO_H - -#include "proc/engine/procnode.hpp" - - - -namespace engine - { - - - /** - * abstraction of the most important kind of Processing node, - * which really works on the media data and transforms input - * into ouput. Subclasses include the (Video) Projector - * for scaling/translating, all sorts of effects (Plugins), - * as well as the low level codecs used to decode the raw - * media at the source end of the render pipeline(s) - */ - class Trafo : public ProcNode - { - protected: - Trafo (WiringDescriptor const& wd) - : ProcNode(wd) - { } - - friend class NodeFactory; - - - - /** do the actual calculations. - * @internal dispatch to implementation. - * Client code should use #render() - * @todo obviously we need a parameter!!! - */ - virtual void process() = 0; - }; - - typedef Trafo* PTrafo; ///< @todo handle ProcNode by pointer or by shared-ptr?? - - - -} // namespace engine -#endif From e497f0a41ecc70df3d9011365cdc785633c035df Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 01:57:26 +0200 Subject: [PATCH 013/108] test case generator (Cartesian product): unit test pass --- tests/40components.tests | 269 +++++++++--------- .../lib/meta/generator-combinations-test.cpp | 3 +- tests/lib/meta/typelist-manip-test.cpp | 4 +- 3 files changed, 144 insertions(+), 132 deletions(-) diff --git a/tests/40components.tests b/tests/40components.tests index 340203dbf..4541270c7 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -807,135 +807,135 @@ END TEST "typelist manipulation" TypeListManip_test <-<2>-<3>- -out: List2 :-<5>-<6>-<7>- -out: Added2 :-<3>-<4>-<5>- -out: FilterEven :-<2>-<6>- -out: Append1 :- -out: Append2 :-<11>-<22>- -out: Append3 :-<111>- -out: Append4 :-<222>- -out: Append5 :-<1>-<2>-<3>- -out: Append6 :-<5>-<6>-<7>- -out: Append7 :-<111>-<5>-<6>-<7>- -out: Append8 :-<1>-<2>-<3>-<222>- -out: Append9 :-<1>-<2>-<3>-<5>-<6>-<7>- -out: Overl01 :- -out: Overl02 :- -out: Overl03 :- -out: Overl04 :-<9>-<8>-<3>- -out: Overl05 :-<1>-<9>-<8>- -out: Overl06 :-<1>-<2>-<9>- -out: Overl07 :-<1>-<2>-<3>- -out: Overl08 :-<1>-<2>-<3>- -out: Overl09 :-<1>-<2>-<3>- -out: Overl10 :-<1>-<1>-<2>- -out: Overl11 :-<1>-<2>-<3>- -out: Overl12 :-<1>-<2>-<3>- -out: Overl13 :-<1>-<2>-<3>- -out: Front1 :- -out: Front2 :-<1>- -out: Front3 :-<1>-<2>-<3>- -out: Back1 :-<2>-<3>- -out: Back2 :-<3>- -out: Back3 :- -out: Front4 :-<1>- -out: Back4 :-<2>-<3>- -out: Prefix :-<1>-<2>- -out: ElmL :-<3>- -out: NPrefix :- -out: Types :-<3>- -out: NList :- -out: Types :- -out: LL :-<1>-<2>-<3>-<5>-<6>-<7>- -out: List :-<1>-<2>-<3>-<5>-<6>-<7>- -out: First :-<1>- -out: Tail :-<2>-<3>-<5>-<6>-<7>- -out: Prefix :-<1>-<2>-<3>-<5>-<6>- -out: Last :-<7>- -out: HeadEnd :-<1>-<7>- -out: Prefix1 : -out: \+---<11>-<22>-\+- -out: Prefix2 : -out: \+---<101>-<1>-\+ -out: \+---<101>-<2>-\+ -out: \+---<101>-<3>-\+- -out: Prefix3 : -out: \+---<1>-\+ -out: \+---<2>-\+ -out: \+---<3>-\+- -out: Prefix4 : -out: \+---<111>-<1>-<2>-<3>-\+ -out: \+---<111>-<0>-\+ -out: \+---<111>-<5>-<6>-<7>-\+- -out: Prefix5 : -out: \+---<1>-<2>-<3>-<5>-\+ -out: \+---<1>-<2>-<3>-<6>-\+ -out: \+---<1>-<2>-<3>-<7>-\+- -out: Prefix6 : -out: \+---<1>-<2>-<3>-<1>-<2>-<3>-\+ -out: \+---<1>-<2>-<3>-<0>-\+ -out: \+---<1>-<2>-<3>-<5>-<6>-<7>-\+- -out: Dist1 : -out: \+---<11>-<1>-\+ -out: \+---<11>-<2>-\+ -out: \+---<11>-<3>-\+- -out: Dist2 : -out: \+---<11>-<0>-\+ -out: \+---<22>-<0>-\+ -out: \+---<33>-<0>-\+- -out: Dist3 : -out: \+---<11>-<1>-\+ -out: \+---<11>-<2>-\+ -out: \+---<11>-<3>-\+ -out: \+---<22>-<1>-\+ -out: \+---<22>-<2>-\+ -out: \+---<22>-<3>-\+ -out: \+---<33>-<1>-\+ -out: \+---<33>-<2>-\+ -out: \+---<33>-<3>-\+- -out: Dist4 : -out: \+---<11>-<1>-<2>-<3>-\+ -out: \+---<11>-<5>-<6>-<7>-\+ -out: \+---<22>-<1>-<2>-<3>-\+ -out: \+---<22>-<5>-<6>-<7>-\+ -out: \+---<33>-<1>-<2>-<3>-\+ -out: \+---<33>-<5>-<6>-<7>-\+- -out: Down :-<11>-<10>-<9>-<8>-<7>-<6>-<5>-<4>-<3>-<2>-<1>-<0>- -out: Combi : -out: \+---<1>-<2>-<3>-<·>-\+ -out: \+---<1>-<2>-<2>-<·>-\+ -out: \+---<1>-<2>-<1>-<·>-\+ -out: \+---<1>-<2>-<0>-<·>-\+ -out: \+---<1>-<1>-<3>-<·>-\+ -out: \+---<1>-<1>-<2>-<·>-\+ -out: \+---<1>-<1>-<1>-<·>-\+ -out: \+---<1>-<1>-<0>-<·>-\+ -out: \+---<1>-<0>-<3>-<·>-\+ -out: \+---<1>-<0>-<2>-<·>-\+ -out: \+---<1>-<0>-<1>-<·>-\+ -out: \+---<1>-<0>-<0>-<·>-\+ -out: \+---<0>-<2>-<3>-<·>-\+ -out: \+---<0>-<2>-<2>-<·>-\+ -out: \+---<0>-<2>-<1>-<·>-\+ -out: \+---<0>-<2>-<0>-<·>-\+ -out: \+---<0>-<1>-<3>-<·>-\+ -out: \+---<0>-<1>-<2>-<·>-\+ -out: \+---<0>-<1>-<1>-<·>-\+ -out: \+---<0>-<1>-<0>-<·>-\+ -out: \+---<0>-<0>-<3>-<·>-\+ -out: \+---<0>-<0>-<2>-<·>-\+ -out: \+---<0>-<0>-<1>-<·>-\+ -out: \+---<0>-<0>-<0>-<·>-\+- -out: OnOff : -out: \+---<1>-<2>-<3>-<·>-\+ -out: \+---<1>-<2>-<·>-\+ -out: \+---<1>-<3>-<·>-\+ -out: \+---<1>-<·>-\+ -out: \+---<2>-<3>-<·>-\+ -out: \+---<2>-<·>-\+ -out: \+---<3>-<·>-\+ -out: \+---<·>-\+- +out-lit: List1 :-<1>-<2>-<3>- +out-lit: List2 :-<5>-<6>-<7>- +out-lit: Added2 :-<3>-<4>-<5>- +out-lit: FilterEven :-<2>-<6>- +out-lit: Append1 :- +out-lit: Append2 :-<11>-<22>- +out-lit: Append3 :-<111>- +out-lit: Append4 :-<222>- +out-lit: Append5 :-<1>-<2>-<3>- +out-lit: Append6 :-<5>-<6>-<7>- +out-lit: Append7 :-<111>-<5>-<6>-<7>- +out-lit: Append8 :-<1>-<2>-<3>-<222>- +out-lit: Append9 :-<1>-<2>-<3>-<5>-<6>-<7>- +out-lit: Overl01 :- +out-lit: Overl02 :- +out-lit: Overl03 :- +out-lit: Overl04 :-<9>-<8>-<3>- +out-lit: Overl05 :-<1>-<9>-<8>- +out-lit: Overl06 :-<1>-<2>-<9>- +out-lit: Overl07 :-<1>-<2>-<3>- +out-lit: Overl08 :-<1>-<2>-<3>- +out-lit: Overl09 :-<1>-<2>-<3>- +out-lit: Overl10 :-<1>-<1>-<2>- +out-lit: Overl11 :-<1>-<2>-<3>- +out-lit: Overl12 :-<1>-<2>-<3>- +out-lit: Overl13 :-<1>-<2>-<3>- +out-lit: Front1 :- +out-lit: Front2 :-<1>- +out-lit: Front3 :-<1>-<2>-<3>- +out-lit: Back1 :-<2>-<3>- +out-lit: Back2 :-<3>- +out-lit: Back3 :- +out-lit: Front4 :-<1>- +out-lit: Back4 :-<2>-<3>- +out-lit: Prefix :-<1>-<2>- +out-lit: ElmL :-<3>- +out-lit: NPrefix :- +out-lit: Types :-<3>- +out-lit: NList :- +out-lit: Types :- +out-lit: LL :-<1>-<2>-<3>-<5>-<6>-<7>- +out-lit: List :-<1>-<2>-<3>-<5>-<6>-<7>- +out-lit: First :-<1>- +out-lit: Tail :-<2>-<3>-<5>-<6>-<7>- +out-lit: Prefix :-<1>-<2>-<3>-<5>-<6>- +out-lit: Last :-<7>- +out-lit: HeadEnd :-<1>-<7>- +out-lit: Prefix1 : +out-lit: +---<11>-<22>-+- +out-lit: Prefix2 : +out-lit: +---<101>-<1>-+ +out-lit: +---<101>-<2>-+ +out-lit: +---<101>-<3>-+- +out-lit: Prefix3 : +out-lit: +---<1>-+ +out-lit: +---<2>-+ +out-lit: +---<3>-+- +out-lit: Prefix4 : +out-lit: +---<111>-<1>-<2>-<3>-+ +out-lit: +---<111>-<0>-+ +out-lit: +---<111>-<5>-<6>-<7>-+- +out-lit: Prefix5 : +out-lit: +---<1>-<2>-<3>-<5>-+ +out-lit: +---<1>-<2>-<3>-<6>-+ +out-lit: +---<1>-<2>-<3>-<7>-+- +out-lit: Prefix6 : +out-lit: +---<1>-<2>-<3>-<1>-<2>-<3>-+ +out-lit: +---<1>-<2>-<3>-<0>-+ +out-lit: +---<1>-<2>-<3>-<5>-<6>-<7>-+- +out-lit: Dist1 : +out-lit: +---<11>-<1>-+ +out-lit: +---<11>-<2>-+ +out-lit: +---<11>-<3>-+- +out-lit: Dist2 : +out-lit: +---<11>-<0>-+ +out-lit: +---<22>-<0>-+ +out-lit: +---<33>-<0>-+- +out-lit: Dist3 : +out-lit: +---<11>-<1>-+ +out-lit: +---<11>-<2>-+ +out-lit: +---<11>-<3>-+ +out-lit: +---<22>-<1>-+ +out-lit: +---<22>-<2>-+ +out-lit: +---<22>-<3>-+ +out-lit: +---<33>-<1>-+ +out-lit: +---<33>-<2>-+ +out-lit: +---<33>-<3>-+- +out-lit: Dist4 : +out-lit: +---<11>-<1>-<2>-<3>-+ +out-lit: +---<11>-<5>-<6>-<7>-+ +out-lit: +---<22>-<1>-<2>-<3>-+ +out-lit: +---<22>-<5>-<6>-<7>-+ +out-lit: +---<33>-<1>-<2>-<3>-+ +out-lit: +---<33>-<5>-<6>-<7>-+- +out-lit: Down :-<11>-<10>-<9>-<8>-<7>-<6>-<5>-<4>-<3>-<2>-<1>-<0>- +out-lit: Combi : +out-lit: +---<1>-<2>-<3>-<·>-+ +out-lit: +---<1>-<2>-<2>-<·>-+ +out-lit: +---<1>-<2>-<1>-<·>-+ +out-lit: +---<1>-<2>-<0>-<·>-+ +out-lit: +---<1>-<1>-<3>-<·>-+ +out-lit: +---<1>-<1>-<2>-<·>-+ +out-lit: +---<1>-<1>-<1>-<·>-+ +out-lit: +---<1>-<1>-<0>-<·>-+ +out-lit: +---<1>-<0>-<3>-<·>-+ +out-lit: +---<1>-<0>-<2>-<·>-+ +out-lit: +---<1>-<0>-<1>-<·>-+ +out-lit: +---<1>-<0>-<0>-<·>-+ +out-lit: +---<0>-<2>-<3>-<·>-+ +out-lit: +---<0>-<2>-<2>-<·>-+ +out-lit: +---<0>-<2>-<1>-<·>-+ +out-lit: +---<0>-<2>-<0>-<·>-+ +out-lit: +---<0>-<1>-<3>-<·>-+ +out-lit: +---<0>-<1>-<2>-<·>-+ +out-lit: +---<0>-<1>-<1>-<·>-+ +out-lit: +---<0>-<1>-<0>-<·>-+ +out-lit: +---<0>-<0>-<3>-<·>-+ +out-lit: +---<0>-<0>-<2>-<·>-+ +out-lit: +---<0>-<0>-<1>-<·>-+ +out-lit: +---<0>-<0>-<0>-<·>-+- +out-lit: OnOff : +out-lit: +---<1>-<2>-<3>-<·>-+ +out-lit: +---<1>-<2>-<·>-+ +out-lit: +---<1>-<3>-<·>-+ +out-lit: +---<1>-<·>-+ +out-lit: +---<2>-<3>-<·>-+ +out-lit: +---<2>-<·>-+ +out-lit: +---<3>-<·>-+ +out-lit: +---<·>-+- return: 0 END @@ -1105,6 +1105,17 @@ END TEST "Typelist combinations generator" GeneratorCombinations_test <-<2>-+ +out-lit: +---<1>-<4>-+ +out-lit: +---<1>-<6>-+ +out-lit: +---<3>-<2>-+ +out-lit: +---<3>-<4>-+ +out-lit: +---<3>-<6>-+ +out-lit: +---<5>-<2>-+ +out-lit: +---<5>-<4>-+ +out-lit: +---<5>-<6>-+- +out-lit: All-Test-Combinations--<12>-<14>-<16>-<32>-<34>-<36>-<52>-<54>-<56>-| return: 0 END diff --git a/tests/lib/meta/generator-combinations-test.cpp b/tests/lib/meta/generator-combinations-test.cpp index 2a55998e2..4c90b1f93 100644 --- a/tests/lib/meta/generator-combinations-test.cpp +++ b/tests/lib/meta/generator-combinations-test.cpp @@ -70,7 +70,8 @@ namespace test { { T1 param1; T2 param2; - return str(formatted % param1 % param2 + return str(formatted % uint(param1) + % uint(param2) % BASE::visitAll()); } }; diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp index 09ab07390..a14daef90 100644 --- a/tests/lib/meta/typelist-manip-test.cpp +++ b/tests/lib/meta/typelist-manip-test.cpp @@ -139,8 +139,8 @@ namespace test { CHECK (6 == e1); CHECK (7 == e2); - CHECK ((is_sameType::value)); - CHECK ((is_sameType::value)); + CHECK ((is_sameType ::value)); + CHECK ((is_sameType ::value)); CHECK ((is_sameType::value)); } From 5e8a9b50d131e4dd934788755ddfed143facb77b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 May 2011 02:22:57 +0200 Subject: [PATCH 014/108] define asset::Viewer --- doc/devel/uml/fig130309.png | Bin 37738 -> 38015 bytes doc/devel/uml/fig130437.png | Bin 32728 -> 33013 bytes doc/devel/uml/fig131205.png | Bin 12187 -> 13639 bytes src/proc/asset/viewer.cpp | 34 ++++++++++++------------- src/proc/asset/viewer.hpp | 35 ++++++++++++++++---------- uml/lumiera/128133 | 24 +++++++++++++++++- uml/lumiera/130309.diagram | 23 ++++++++++++----- uml/lumiera/131205.diagram | 48 +++++++++++++++++++++++++++--------- uml/lumiera/5.session | 12 ++++++--- wiki/renderengine.html | 5 ++-- 10 files changed, 126 insertions(+), 55 deletions(-) diff --git a/doc/devel/uml/fig130309.png b/doc/devel/uml/fig130309.png index d63c8432b93d39c2154ee10fb143e3e62549a70e..6afb6d27999d2bd1b9990ffdf973dae1a34c1587 100644 GIT binary patch literal 38015 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 literal 37738 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 literal 32728 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 diff --git a/doc/devel/uml/fig131205.png b/doc/devel/uml/fig131205.png index 17826d19baf8709bee8784508a6c4800dd7de253..5d46c324929c09b91f96a5ad2619e0b7ccc42a69 100644 GIT binary patch literal 13639 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 IY5e?u0gRfoPyhe` diff --git a/src/proc/asset/viewer.cpp b/src/proc/asset/viewer.cpp index b5c74ad30..bd1db8cbf 100644 --- a/src/proc/asset/viewer.cpp +++ b/src/proc/asset/viewer.cpp @@ -36,33 +36,31 @@ namespace asset { /** @todo anything significant to do here??? */ - Timeline::Timeline (const Asset::Ident& idi, RBinding const& sequenceBinding) + Viewer::Viewer (const Asset::Ident& idi) : Struct (idi) - , boundSequence_(sequenceBinding) { - REQUIRE (boundSequence_); + UNIMPLEMENTED ("anything regarding Viewer Assets"); } - PTimeline - Timeline::create (Asset::Ident const& idi, RBinding const& sequenceBinding) - { - REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); - - PTimeline newElement (AssetManager::instance().wrap (*new Timeline(idi, sequenceBinding))); - getRegistry().append (newElement); - - ENSURE (newElement); - ENSURE (getRegistry().isRegistered (*newElement)); - return newElement; - } +//PViewer +//Viewer::create (Asset::Ident const& idi) +//{ +// REQUIRE (getRegistry, "can't create a Timeline prior to session initialisation"); +// +// PTimeline newElement (AssetManager::instance().wrap (*new Viewer(idi))); +// getRegistry().append (newElement); +// +// ENSURE (newElement); +// ENSURE (getRegistry().isRegistered (*newElement)); +// return newElement; +//} void - Timeline::unlink () + Viewer::unlink () { - AutoRegistered::detach(); - boundSequence_.purge(); +// AutoRegistered::detach(); Struct::unlink(); } diff --git a/src/proc/asset/viewer.hpp b/src/proc/asset/viewer.hpp index 5e5890552..a70c322f2 100644 --- a/src/proc/asset/viewer.hpp +++ b/src/proc/asset/viewer.hpp @@ -22,11 +22,23 @@ /** @file viewer.hpp - ** structural element within the session. - ** \c "timeline(theTimelineName),bindSequence(theTimelineName,sequenceID)." + ** structural element corresponding to a viewer in the GUI. + ** This Asset marks an attachment point, allowing other (output producing) + ** elements to be connected to ("viewer attachment"). The typical standard case + ** is a Timeline: It features a set of global pipes (busses), which collect all + ** produced data. Yet without an explicit output connection, this data can't be + ** generated, because in Lumiera, actual output is always retrieved ("pulled") + ** starting from an output sink. Thus, in order to \em perform (play, render) + ** the timeline, an "view connection" needs to be established: this connection + ** is represented by an session::BindingMO, linking the Timeline to an + ** asset::Viewer. Consequently, the same output mapping and translation + ** mechanism used for Sequence-Timeline- (and VirtualClip-)Bindings + ** is employed in this situation to figure out the real output port. + ** + ** @todo WIP-WIP-WIP as of 5/11 ** ** @see Session - ** @see Sequence + ** @see Timeline ** @see StructFactory ** */ @@ -61,27 +73,24 @@ namespace session { namespace asset { -// using lumiera::P; - class Timeline; - typedef lumiera::P PTimeline; + using lumiera::P; + class Viewer; + typedef lumiera::P PViewer; /** * TODO type comment */ - class Timeline + class Viewer : public Struct - , public lib::AutoRegistered +// , public lib::AutoRegistered { - typedef mobject::session::RBinding RBinding; - RBinding boundSequence_; - - Timeline (Ident const&, RBinding const&); + Viewer (Ident const&); /////TODO ctor params???? public: /** create and register a new Timeline instance */ - static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); +// static PTimeline create (Asset::Ident const& idi, RBinding const& sequenceBinding); protected: virtual void unlink (); diff --git a/uml/lumiera/128133 b/uml/lumiera/128133 index b913b7406..377bc708e 100644 --- a/uml/lumiera/128133 +++ b/uml/lumiera/128133 @@ -1,6 +1,6 @@ format 58 "Asset" // ProcessingLayer::Asset - revision 22 + revision 23 modified_by 5 "hiv" // class settings //class diagram settings @@ -773,6 +773,28 @@ ${inlines} end end + class 174981 "Viewer" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 214277 // + relation 203141 ---|> + a public + cpp default "${type}" + classrelation_ref 214277 // + b parent class_ref 136965 // Struct + end + end + class 138117 "Pipe" visibility package cpp_decl "${comment}${template}class ${name}${inherit} diff --git a/uml/lumiera/130309.diagram b/uml/lumiera/130309.diagram index 3f9868b95..e0c065816 100644 --- a/uml/lumiera/130309.diagram +++ b/uml/lumiera/130309.diagram @@ -42,11 +42,11 @@ classcanvas 131461 class_ref 137605 // Preview end classcanvas 131973 class_ref 137733 // Effect draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default - xyz 412 416 2000 + xyz 412 419 2000 end classcanvas 132101 class_ref 137861 // Codec draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default - xyz 464 417 2000 + xyz 464 419 2000 end classcanvas 132613 class_ref 138117 // Pipe draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default @@ -96,6 +96,10 @@ classcanvas 140805 class_ref 160901 // Timeline draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default xyz 622 419 2000 end +classcanvas 141445 class_ref 174981 // Viewer + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 545 419 2000 +end relationcanvas 129157 relation_ref 138117 // geometry VHV from ref 128645 z 1999 to point 289 293 @@ -135,16 +139,16 @@ relationcanvas 130949 relation_ref 138629 // end relationcanvas 132229 relation_ref 139269 // geometry VHV - from ref 131973 z 1999 to point 432 390 - line 139269 z 1999 to point 448 390 + from ref 131973 z 1999 to point 432 391 + line 139269 z 1999 to point 448 391 line 139397 z 1999 to ref 128773 no_role_a no_role_b no_multiplicity_a no_multiplicity_b end relationcanvas 132357 relation_ref 139397 // geometry VHV - from ref 132101 z 1999 to point 484 390 - line 139525 z 1999 to point 448 390 + from ref 132101 z 1999 to point 484 391 + line 139525 z 1999 to point 448 391 line 139653 z 1999 to ref 128773 no_role_a no_role_b no_multiplicity_a no_multiplicity_b @@ -270,6 +274,13 @@ relationcanvas 141189 relation_ref 185349 // no_role_a no_role_b no_multiplicity_a no_multiplicity_b end +relationcanvas 141573 relation_ref 203141 // + geometry HV + from ref 141445 z 1999 to point 601 436 + line 141701 z 1999 to ref 128901 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end line 128261 -_-_ geometry HV from ref 128005 z 1999 to point 328 149 line 128389 z 1999 to ref 128133 diff --git a/uml/lumiera/131205.diagram b/uml/lumiera/131205.diagram index 51140cdfa..dc3096de2 100644 --- a/uml/lumiera/131205.diagram +++ b/uml/lumiera/131205.diagram @@ -24,10 +24,6 @@ classcanvas 128645 class_ref 139141 // DoAttach draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default xyz 337 431 2000 end -classcanvas 128773 class_ref 137989 // Track - draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default - xyz 328 168 2000 -end classcanvas 128901 class_ref 139269 // DoRecurse draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default xyz 409 431 2000 @@ -36,6 +32,18 @@ classcanvas 129029 class_ref 136965 // Struct draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default xyz 425 73 2005 end +classcanvas 132101 class_ref 152197 // Sequence + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 121 217 2000 +end +classcanvas 132229 class_ref 160901 // Timeline + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 87 168 2000 +end +classcanvas 132357 class_ref 174981 // Viewer + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 162 168 2000 +end relationcanvas 129157 relation_ref 139653 // geometry VHV from ref 128517 z 1999 to point 260 139 @@ -50,14 +58,6 @@ relationcanvas 129285 relation_ref 141189 // no_role_a no_role_b no_multiplicity_a no_multiplicity_b end -relationcanvas 129413 relation_ref 139525 // - geometry VHV - from ref 128773 z 1999 to point 348 139 - line 130181 z 1999 to point 445 139 - line 130309 z 1999 to ref 129029 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -end relationcanvas 129541 relation_ref 141701 // from ref 128389 z 1999 stereotype "<>" xyz 367 290 3000 to ref 128133 role_a_pos 364 301 3000 no_role_b @@ -91,5 +91,29 @@ relationcanvas 131589 relation_ref 146053 // role_a_pos 328 250 3000 no_role_b multiplicity_a_pos 379 273 3000 no_multiplicity_b end +relationcanvas 132485 relation_ref 185349 // + geometry VHV unfixed + from ref 132229 z 1999 to point 112 139 + line 133125 z 1999 to point 445 139 + line 133253 z 1999 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 132613 relation_ref 203141 // + geometry VHV unfixed + from ref 132357 z 1999 to point 183 139 + line 133381 z 1999 to point 445 139 + line 133509 z 1999 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 132741 relation_ref 185221 // + geometry VHV unfixed + from ref 132101 z 1999 to point 149 139 + line 132869 z 1999 to point 445 139 + line 132997 z 1999 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end preferred_whz 530 608 1 end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 09a1aa0c4..b61f08080 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -4,8 +4,12 @@ diagrams 631 352 100 4 0 0 objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 - active classdiagram_ref 142725 // Time flavours + classdiagram_ref 142725 // Time flavours 595 646 100 4 0 0 + classdiagram_ref 131205 // Struct-Asset Relations + 530 608 100 4 0 0 + active classdiagram_ref 130309 // Asset Kinds + 855 805 100 4 0 0 end show_stereotypes selected @@ -15,7 +19,9 @@ open package_ref 128005 // design class_ref 160389 // VirtualMedia class_ref 136837 // Proc + class_ref 152197 // Sequence class_ref 160901 // Timeline + class_ref 174981 // Viewer class_ref 139269 // DoRecurse class_ref 162821 // TypedID::Link classview_ref 128389 // Controller Workings @@ -31,9 +37,9 @@ open class_ref 152453 // PlacementRef classrelation_ref 178437 // class_ref 153733 // QueryFocusStack - expansionregion_ref 128133 // establish partitioning usecaseview_ref 128261 // config examples - classview_ref 128133 // Engine Workings + + package_ref 128389 // RenderEngine classdiagram_ref 142725 // Time flavours class_ref 134917 // Time class_ref 169221 // TimeGrid diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 83ca6e30c..2105f0406 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -5579,13 +5579,14 @@ Instead, we should try to just connect the various subsystems via Interfaces and * to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy
-
+
Structural Assets are intended mainly for internal use, but the user should be able to see and query them. They are not "loaded" or "created" directly, rather they //leap into existence // by creating or extending some other structures in the session, hence the name. Some of the structural Asset parametrisation can be modified to exert control on some aspects of the Proc Layer's (default) behaviour.
 * [[Processing Patterns|ProcPatt]] encode information how to set up some parts of the render network to be created automatically: for example, when building a clip, we use the processing pattern how to decode and pre-process the actual media data.
 * [[Tracks|Track]] are one of the dimensions used for organizing the session data. They serve as an Anchor to attach parametrisation of output pipe, overlay mode etc. By [[placing|Placement]] to a track, a media object inherits placement properties from this track.
 * [[Pipes|Pipe]] form &mdash; at least as visible to the user &mdash; the basic building block of the render network, because the latter appears to be a collection of interconnected processing pipelines. (this is the //outward view; // in fact the render network consists of [[nodes|ProcNode]] and is [[built|Builder]] from the Pipes, clips, effects...)[>img[Asset Classess|uml/fig131205.png]]
 * [[Sequence]] assets act as a façade to the fundamental compound building blocks within the model, a sequence being a collection of clips placed onto a tree of tracks. Sequences, as well as the top-level tracks enclosed will be created automatically on demand. Of course you may create them deliberately. Without binding it to a timeline or meta-clip, a sequence remains invisible.
-* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID 
+* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID
+* [[Viewer|ViewerAsset]] assets correspond to the available viewer elements in the GUI. By [[connecting to a viewer|ViewConnection]], session elements derive a concrete (physical) output and gain the ability to be [[played|PlayService]].
 
 
 

From 27533c3bb9392d6d25b51fadecc5e50ebe1c777a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 28 May 2011 18:14:21 +0200
Subject: [PATCH 015/108] WIP darft usage for the new time::Control (life
 change) element

---
 src/lib/scoped-holder.hpp            |  9 +++
 src/lib/time/control.hpp             |  9 ++-
 tests/lib/time/time-control-test.cpp | 86 +++++++++++++++++++++-------
 3 files changed, 79 insertions(+), 25 deletions(-)

diff --git a/src/lib/scoped-holder.hpp b/src/lib/scoped-holder.hpp
index f8eaf74d2..67252b053 100644
--- a/src/lib/scoped-holder.hpp
+++ b/src/lib/scoped-holder.hpp
@@ -173,6 +173,15 @@ namespace lib {
           return *obj;
         }
       
+      TY& 
+      create (TY const& o)    ///< place new content object using copy ctor
+        {
+          ASSERT (!created_);
+          TY * obj = new(content_) TY(o);
+          ++created_;
+          return *obj;
+        }
+      
       void
       clear ()
         {
diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index e5f44629c..87bd271f2 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -91,15 +91,18 @@ namespace time {
   template
   class Control
     : public Mutation
-    , public function
     {
     public:
-      typedef function timeSignal;
+      typedef function TimeSignal;
+      
+      void operator() (TI const&);
+      void operator() (Offset const&);
+      void operator() (uint);
       
       /** install a callback functor to be invoked
        *  to notify for any changes to the observed
        *  time entity */
-      void connectChangeNotification (timeSignal);
+      void connectChangeNotification (TimeSignal const&);
       
       /** disconnect from observed entity and
        *  cease any change notification */
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index a89b96e48..5b4784835 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -25,15 +25,17 @@
 #include "lib/test/test-helper.hpp"
 #include "lib/time/timevalue.hpp"
 #include "lib/time/timequant.hpp"
-#include "lib/time/mutation.hpp"
+#include "lib/time/control.hpp"
 #include "proc/asset/meta/time-grid.hpp"
 #include "lib/meta/generator-combinations.hpp"
+#include "lib/scoped-holder.hpp"
 #include "lib/util.hpp"
 
 #include 
 #include 
 #include 
 
+using lib::test::showType;
 using boost::lexical_cast;
 using util::isnil;
 using std::cout;
@@ -45,9 +47,13 @@ namespace lib {
 namespace time{
 namespace test{
   
+  namespace error = lumiera::error;
+  
+  using lib::ScopedHolder;
   using asset::meta::TimeGrid;
   using lumiera::typelist::Types;
   using lumiera::typelist::InstantiateChainedCombinations;
+  using error::LUMIERA_ERROR_UNCONNECTED;
   
   namespace {
     inline string
@@ -60,20 +66,52 @@ namespace test{
     }
     
     
+    /**
+     * Mock object to receive change notifications.
+     * A copy of the most recently received value
+     * is memorised within an embedded buffer,
+     * to be verified by the actual tests.
+     */
+    template
+    class TestListener
+      : boost::noncopyable
+      {
+        ScopedHolder received_;
+        
+      public:
+        void
+        operator() (TI const& changeValue)
+          {
+            received_.clear();
+            received_.create (changeValue);
+          }
+        
+        TI&
+        reveivedValue()
+          {
+            return *received_;
+          }
+      };
+    
+    
+    
     template
     struct TestCase
       : BASE
       {
         void
-        performTestCases()
+        performTestCases(TimeValue const& o, TimeValue const& c)
           {
-            
+            cout << "Test-Case. Target=" << showType() 
+                 <<" <--feed--- "        << showType() 
+                 < (arg);
         }
       
-      struct TestValues
-        {
-          TimeVar var;
-          Duration dur;
-          TimeSpan span;
-          QuTime quant;
-          
-          TestValues (TimeValue o)
-            : var(o)
-            , dur(o)
-            , span(o, Offset(o))
-            , quant(o, "test_grid")
-            { }
-        };
-      
       
       virtual void
       run (Arg arg) 
@@ -133,19 +156,38 @@ namespace test{
           FrameNr count(qChange);
           
           verifyBasics();
-          verifyMatrix_of_MutationCases();
+          verifyMatrix_of_MutationCases(o,c);
         } 
       
       
       void
       verifyBasics()
         {
+          TimeSpan target(Time(0,10), FSecs(5));
+          
+          Control
-
+
The term &raquo;Time&laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
 ;continuous time
 :without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
 :the time values are just numbers; the point of reference and the meaning is implicit.
 :within Lumiera, time is encoded as integral number of //micro ticks,// practically continuous
-;duration
+;time distance
 :a range of time, a ''distance'' on the time axis, measured with the same arbitrary precision as time points.
-:distances and durations can be determined by //subtracting// two time points, consequently they are //signed numbers.//
-:a duration always abstracts from the time //when// this duration or distance happens, the relation to any time scale remains implicit
+:distances can be determined by //subtracting// two time points, consequently they are //signed numbers.//
 ;offset
-:offsetting a time or a duration is an operation (not an entity): it means changing the denoted time point or duration.
-:the //target// of an offset operation is a time or duration, while it's //argument// is a distance (synonymous to duration).
-:Time values are //immutable,// like numbers. Only a ''time variable'' can be changed. Durations to the contrary can be mutable or  const.
+:a distance can be used to adjust (offset) a time or a duration: this means applying a relative change.
+:the //target// of an offset operation is a time or duration, while it's //argument// is a distance (synonymous to offset).
+;duration
+:the length of a time range yields a ''time metric''.
+:the duration can be defined as the //absolute value//&nbsp; of the offset between start and endpoint of the time range.
+:a duration always abstracts from the time //when// this duration or distance happens, the relation to any time scale remains implicit
 ;time span
 :contrary to a mere duration, a ''time interval'' or time span is actually //anchored// at a specific point in time.
 :it can be seen as a //special kind of duration,// which explicitly states the information //when// this time span takes place.
+;changing time
+:Time values are //immutable,// like numbers. Only a ''time variable'' can be changed.
+:yet some of the special time entities can recieve [[mutation messages|TimeMutation]], allowing e.g. for adjustments to a time interval selection from the GUI
 
 ;internal time
 :While the basic continuous time values don't imply any provision regarding the time scale and origin to be used, actually, within the implementation of the application, the meaning of time values is uniform and free of contradictions. Thus effectively there is an ''implementation time scale'' -- but its scope of validity is //strictly limited to the implementation level of a single application instance.// It is never exposed and never persisted. It might not be reproducible over multiple instantiations of the application. The implementation reserves the right to recalibrate this internal scale. Later, when Lumiera gains the capability to run within a network of render nodes, these instance connections will include a negotiation about the internal time scale, which remains completely opaque to the outer world. This explains, why {{{lumiera::Time}}} instances lack the ability to show their time value beyond debugging purposes. This is to avoid confusion and to stress their opaque nature.

From b54cd2c722042198b6cfa44da7f5dd5e97353b8a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 8 Jun 2011 03:35:48 +0200
Subject: [PATCH 023/108] cont. defining time::Control special cases

the refactoring seems to work out OK, was able
to cover all the cases defined thus far....
---
 src/lib/time/control.hpp             | 182 ++++++++++++++++++++-------
 tests/lib/time/time-control-test.cpp |  11 +-
 2 files changed, 147 insertions(+), 46 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 69ae4ec02..01f5295cc 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -162,12 +162,53 @@ namespace time {
       };
     
     
+    
+    namespace { // metaprogramming helpers to pick the suitable Instantiation...
+      
+      template
+      struct isDurationZ
+        {
+          static const bool value = is_sameType::value;
+        };
+      template
+      struct isTimeSpanZ
+        {
+          static const bool value = is_sameType::value;
+        };
+      template
+      struct isQuTime
+        {
+          static const bool value = is_sameType::value;
+        };
+      
+      template
+      struct isSpecialCase
+        {
+          static const bool value = isDurationZ::value;
+        };
+      
+      template
+      inline bool
+      isDuration()
+        {
+          return is_sameType::value;
+        }
+      
+      template
+      inline bool
+      isTimeSpan()
+        {
+          return is_sameType::value;
+        }
+    }
+    
+    
     template
     struct Builder
       {
         
         static TI
-        buildChangedValue (TAR& target)
+        buildChangedValue (TAR const& target)
           {
             return TI(target);
           }
@@ -176,12 +217,21 @@ namespace time {
     struct Builder
       {
         static TimeSpan
-        buildChangedValue (TAR& target)
+        buildChangedValue (TAR const& target)
           {
             return TimeSpan (target, Duration::NIL);  /////////////TODO how to feed the "new value" duration????
           }
       };
     template<>
+    struct Builder
+      {
+        static TimeSpan
+        buildChangedValue (Duration const& targetDuration)
+          {
+            return TimeSpan (Time::ZERO, targetDuration);
+          }
+      };
+    template<>
     struct Builder
       {
         static TimeSpan
@@ -219,41 +269,112 @@ namespace time {
       , Builder
       {
         
+        template
         static TI
-        imposeValueChange (TAR& target, TI const& newVal)
+        processValueChange (TAR& target, SRC const& change)
           {
-            imposeChange (target,newVal);
+            imposeChange (target,change);
             return buildChangedValue(target);
           }
         
         static TI
-        imposeOffset (TAR& target, Offset const& off)
+        useLengthAsChange (TAR& target, TimeSpan const& change)
           {
-            imposeChange (target,off);
-            return buildChangedValue(target);
+            return processValueChange(target, change.duration());
           }
         
         static TI
-        imposeNudge (TAR& target, int off_by_steps)
+        mutateLength (TimeSpan& target, Duration const& change)
           {
-            imposeChange (target,off_by_steps);
+            Mutator::imposeChange (target.duration(), change);
+            return Builder::buildChangedValue(target);
+          }
+        
+        static TimeSpan
+        mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
+          {
+            Mutator::imposeChange (target.duration(), change.duration());
+            Mutator::imposeChange (target,change.start());
+            return Builder::buildChangedValue(target);
+          }
+        
+        static TI
+        dontChange (TAR& target)
+          {
+            // note: not touching the target
             return buildChangedValue(target);
           }
       };
     
+    
+    
     template
-    struct Policy;
-    
-    template
-    struct Policy
+    struct Policy
       {
-        static function
+        static function
         buildChangeHandler (TAR& target)
           {
-            return bind (Adap::imposeValueChange, ref(target), _1 );
+            return bind (Adap::template processValueChange, ref(target), _1 );
           }
       };
     
+    
+    // special treatment of Durations as target...
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (Duration& target)
+          {
+            return bind (Adap::dontChange, ref(target) );
+          }
+      };
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (Duration& target)
+          {
+            return bind (Adap::template processValueChange, ref(target), _1 );
+          }
+      };
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (Duration& target)
+          {
+            return bind (Adap::useLengthAsChange, ref(target), _1 );
+          }
+      };
+    
+    // special treatment for TimeSpan values...
+    
+    template
+    struct Policy
+      {
+        static function
+        buildChangeHandler (TimeSpan& target)
+          {
+            return bind (Adap::mutateLength, ref(target), _1 );
+          }
+      };
+    
+    template<>
+    struct Policy
+      {
+        static function
+        buildChangeHandler (TimeSpan& target)
+          {
+            return bind (Adap::mutateTimeSpan, ref(target), _1 );
+          }
+      };
+    
+    
+/*    
     template
     struct Policy
       {
@@ -273,7 +394,7 @@ namespace time {
             return bind (Adap::imposeNudge, ref(target), _1 );
           }
       };
-    
+*/    
       
     template
     TI
@@ -371,33 +492,6 @@ namespace time {
     }
 #endif //(End)quantisation special case    
     
-    
-    namespace { // metaprogramming helpers to pick the suitable Instantiation...
-      
-      template
-      struct isDuration
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isTimeSpan
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isQuTime
-        {
-          static const bool value = is_sameType::value;
-        };
-      
-      template
-      struct isSpecialCase
-        {
-          static const bool value = isDuration::value;
-        };
-      
-    }
-    
     template
     template
     struct Mutator::MutationPolicy<                  typename disable_if< isSpecialCase, 
@@ -437,7 +531,7 @@ namespace time {
 
     template
     template
-    struct Mutator::MutationPolicy<                  typename enable_if< isDuration, 
+    struct Mutator::MutationPolicy<                  typename enable_if< isDurationZ, 
                                        TI>::type, TAR>
       {
         static function
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index 1b5cb2dd7..e309fabaa 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -260,10 +260,17 @@ namespace test{
       CHECK (target == otherDuration);
     }
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span)
+    verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span_as_change)
     {
       CHECK (target != org);
-      CHECK (target == span.duration());
+      CHECK (target == span_as_change.duration());
+    }
+    void
+    verify_wasChanged (TimeSpan const& target, TimeValue const& org, Duration const& changedDur)
+    {
+      CHECK (target == org, "Logic error: Duration was used as start point of the target TimeSpan");
+      CHECK (target.duration() != Time(FSecs(3,2)), "length of the timespan should have been changed");
+      CHECK (target.duration() == changedDur);
     }
     
     

From 92dbedc289585ddbba8e519a8c8960d285a7ba46 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 10 Jun 2011 18:19:02 +0200
Subject: [PATCH 024/108] intensify the unit-test...

---
 tests/lib/time/time-control-test.cpp | 104 ++++++++++++++++++++-------
 1 file changed, 78 insertions(+), 26 deletions(-)

diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index e309fabaa..6787e995c 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -81,7 +81,15 @@ namespace test{
         ScopedHolder received_;
         
       public:
-        TestListener() { received_.create (Time::ZERO); }
+        TestListener()
+          { 
+            received_.create (Time::ZERO); 
+          }
+        
+        TestListener(TI const& initialValue)
+          { 
+            received_.create (initialValue);
+          }
         
         void
         operator() (TI const& changeValue)  const
@@ -90,8 +98,8 @@ namespace test{
             received_.create (changeValue);
           }
         
-        TI&
-        reveivedValue()
+        TI const&
+        receivedValue()  const
           {
             return *received_;
           }
@@ -160,13 +168,13 @@ namespace test{
           controller (Time(FSecs(21,2)));
           CHECK (Time(500,10) == target);
           
-          CHECK (follower.reveivedValue() == Time::ZERO);
+          CHECK (follower.receivedValue() == Time::ZERO);
           controller.connectChangeNotification (follower);
-          CHECK (follower.reveivedValue() == Time(500,10));
+          CHECK (follower.receivedValue() == Time(500,10));
           
           controller (Offset(-Time(500,1)));
           CHECK (Time(0,9) == target);
-          CHECK (Time(0,9) == follower.reveivedValue());
+          CHECK (Time(0,9) == follower.receivedValue());
         }
       
       
@@ -241,7 +249,7 @@ namespace test{
     
     template
     void
-    verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change)
+    ____verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change)
     {
       CHECK (target != org);
       CHECK (target == change);
@@ -249,24 +257,24 @@ namespace test{
     
     template
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, SRC const&)
+    ____verify_wasChanged (Duration const& target, TimeValue const& org, SRC const&)
     {
       CHECK (target == org, "Logic error: Duration was changed by time value");
     }
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, Duration const& otherDuration)
+    ____verify_wasChanged (Duration const& target, TimeValue const& org, Duration const& otherDuration)
     {
       CHECK (target != org);
       CHECK (target == otherDuration);
     }
     void
-    verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span_as_change)
+    ____verify_wasChanged (Duration const& target, TimeValue const& org, TimeSpan const& span_as_change)
     {
       CHECK (target != org);
       CHECK (target == span_as_change.duration());
     }
     void
-    verify_wasChanged (TimeSpan const& target, TimeValue const& org, Duration const& changedDur)
+    ____verify_wasChanged (TimeSpan const& target, TimeValue const& org, Duration const& changedDur)
     {
       CHECK (target == org, "Logic error: Duration was used as start point of the target TimeSpan");
       CHECK (target.duration() != Time(FSecs(3,2)), "length of the timespan should have been changed");
@@ -277,23 +285,54 @@ namespace test{
     
     template
     void
-    verify_wasOffset (TAR const&, TimeValue const&, Offset const&)//(TAR const& target, TimeValue const& o, Offset const& offset)
+    ____verify_wasOffset (TAR const& target, TAR const& refState, Offset const& offset)
     {
-      
+      CHECK (target != refState);
+      CHECK (target == Time(refState)+offset);
     }
     
     template
     void
-    verify_zeroAlligned (TAR const&, TAR const&)//(TAR const& target, TAR const& oldState)
+    ____verify_wasOffsetBack (TAR const& target, TAR const& refState)
     {
-      
+      CHECK (target == refState);
     }
     
+    
     template
     void
-    verify_nudged (TAR const&, TAR const&, int64_t)//(TAR const& target, TAR const& oldState, int64_t offsetSteps)
+    ____verify_nudged (TAR const& target, TAR const& refState, int64_t offsetSteps)
     {
-      
+      CHECK (target != refState);
+      CHECK (target == Time(refState)+Time(FSecs(offsetSteps)));
+    }
+    template<>
+    void
+    ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
+    {
+      CHECK (target != refState);
+      PQuant quantiser(target);
+      CHECK (target == Time(quantiser->materialise(refState))
+                     + offsetSteps * FrameRate::NTSC.duration());
+    }
+    
+    
+    template
+    void
+    ____verify_notification (TAR const& target, TestListener const& follower)
+    {
+      CHECK (target == follower.receivedValue());
+    }
+    void
+    ____verify_notification (TimeSpan const& targetTimeSpan, TestListener const& follower)
+    {
+      CHECK (follower.receivedValue() == targetTimeSpan.duration());
+    }
+    void
+    ____verify_notification (Duration const& targetDuration, TestListener const& follower)
+    {
+      CHECK (Time::ZERO     == follower.receivedValue());
+      CHECK (targetDuration == follower.receivedValue().duration());
     }
     
     
@@ -313,34 +352,47 @@ namespace test{
             Control controller;
             
             TAR target = TestTarget::build(org);
+            SRC change = TestChange::prepareChangeValue(c);
+            TestListener follower(change);
+            
+            controller.connectChangeNotification(follower);
             target.accept (controller);
             
-            SRC change = TestChange::prepareChangeValue(c);
             controller (change);
-            verify_wasChanged (target, org, change);
+            ____verify_wasChanged (target, org, change);
+            ____verify_notification(target,follower);
+            
+            TAR refState(target);
             
             Offset offset(c);
             controller (offset);
-            verify_wasOffset (target, org, offset);
+            ____verify_wasOffset (target, refState, offset);
+            controller (-offset);
+            ____verify_wasOffsetBack (target, refState);
+            ____verify_notification(target,follower);
             
-            TAR oldState(target);
             controller (0);
-            verify_zeroAlligned(target, oldState);
+            ____verify_nudged (target, refState, 0);
+            ____verify_notification(target,follower);
             
             controller (+1);
-            verify_nudged (target, oldState, +1);
+            ____verify_nudged (target, refState, +1);
+            ____verify_notification(target,follower);
             
             controller (-2);
-            verify_nudged (target, oldState, -1);
+            ____verify_nudged (target, refState, -1);
+            ____verify_notification(target,follower);
             
             int maxInt = std::numeric_limits::max();
             int minInt = std::numeric_limits::min();
             
             controller (maxInt);
-            verify_nudged (target, oldState, -1LL + maxInt);
+            ____verify_nudged (target, refState, -1LL + maxInt);
+            ____verify_notification(target,follower);
             
             controller (minInt);
-            verify_nudged (target, oldState, -1LL + maxInt+minInt);
+            ____verify_nudged (target, refState, -1LL + maxInt+minInt);
+            ____verify_notification(target,follower);
             
             
             // tail recursion: further test combinations....

From 15a3694cca9fe09b3a52cf487a9dc05d90c8801c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 11 Jun 2011 20:20:20 +0200
Subject: [PATCH 025/108] more complete unit test pass

---
 src/lib/time/control.hpp             | 35 ++++++++++++++++++----------
 src/lib/time/lumitime.cpp            | 13 ++++++++---
 src/lib/time/timevalue.hpp           | 13 +++++++----
 tests/lib/time/time-control-test.cpp | 13 ++++-------
 tests/lib/time/time-value-test.cpp   |  9 ++++++-
 5 files changed, 55 insertions(+), 28 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 01f5295cc..48af1813c 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -321,8 +321,19 @@ namespace time {
     
     // special treatment of Durations as target...
     
+    namespace {
+      template
+      struct canMutateDuration
+        {
+          static const bool value = is_sameType::value
+                               ||   is_sameType::value
+                               ||   is_sameType::value;
+        };
+    }
+    
     template
-    struct Policy
+    struct Policy, 
+                         Duration>::type>
       {
         static function
         buildChangeHandler (Duration& target)
@@ -331,15 +342,15 @@ namespace time {
           }
       };
     
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Adap::template processValueChange, ref(target), _1 );
-          }
-      };
+//  template
+//  struct Policy
+//    {
+//      static function
+//      buildChangeHandler (Duration& target)
+//        {
+//          return bind (Adap::template processValueChange, ref(target), _1 );
+//        }
+//    };
     
     template
     struct Policy
@@ -573,9 +584,9 @@ namespace time {
     void
     Mutator::bind_to (TAR& target)  const
     {
-      setVal_ = Policy    ::buildChangeHandler (target);
+      setVal_ = Policy::buildChangeHandler (target);
       offset_ = Policy::buildChangeHandler (target);
-      nudge_  = Policy   ::buildChangeHandler (target);
+      nudge_  = Policy::buildChangeHandler (target);
     }
     
     template
diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp
index 978e8190f..f80dc9c6e 100644
--- a/src/lib/time/lumitime.cpp
+++ b/src/lib/time/lumitime.cpp
@@ -119,9 +119,16 @@ namespace time {
   }
   
   
-  /** duration of the given number of frames */
-  Duration::Duration (ulong count, FrameRate const& fps)
-    : TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL))
+  /** offset by the given number of frames. */
+  Offset::Offset (int64_t count, FrameRate const& fps)
+    : TimeValue (count?  (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
+                      : _raw(Duration::NIL))
+    { }
+  
+  /** duration of the given number of frames.
+   * @note always positive; count used absolute */
+  Duration::Duration (int64_t count, FrameRate const& fps)
+    : TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL))
     { }
   
   
diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp
index 5535beed1..0c8fb2e15 100644
--- a/src/lib/time/timevalue.hpp
+++ b/src/lib/time/timevalue.hpp
@@ -42,6 +42,11 @@ namespace time {
   
   namespace error = lumiera::error;
   
+  // forwards...
+  class FrameRate;
+  class TimeSpan;
+  class Mutation;
+  
   
   /**
    * basic constant internal time value.
@@ -193,8 +198,11 @@ namespace time {
         : TimeValue(TimeVar(target) -= origin)
         { }
       
+      Offset (int64_t count, FrameRate const& fps);
+      
       static const Offset ZERO;
       
+      
       TimeValue
       abs()  const
         {
@@ -243,7 +251,6 @@ namespace time {
    * @warning do not mix up gavl_time_t and FSecs */
   typedef boost::rational FSecs;
   
-  class FrameRate;
   
   /**
    * Lumiera's internal time value datatype.
@@ -306,8 +313,6 @@ namespace time {
   
   
   
-  class TimeSpan;
-  class Mutation;
   
   /**
    * Duration is the internal Lumiera time metric.
@@ -338,7 +343,7 @@ namespace time {
         { }
       
       Duration (TimeSpan const& interval);
-      Duration (ulong count, FrameRate const& fps);
+      Duration (int64_t count, FrameRate const& fps);
       
       static const Duration NIL;
       
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index 6787e995c..db1fa6d83 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -140,14 +140,11 @@ namespace test{
           CHECK (c!=Time::ZERO && o != c, "unsuitable testdata");
           
           // 25fps-grid, but with an time origin offset by 1/50sec
-          TimeGrid::build("test_grid", FrameRate::PAL, Time(FSecs(1,50)));
+          TimeGrid::build("test_grid_PAL", FrameRate::PAL, Time(FSecs(1,50)));
           
           // disjoint NTSC-framerate grid for grid aligned changes
           TimeGrid::build("test_grid_NTSC", FrameRate::NTSC);
           
-          QuTime qChange (c, "test_grid");
-          FrameNr count(qChange);
-          
           verifyBasics();
           verifyMatrix_of_MutationCases(o,c);
         } 
@@ -210,7 +207,7 @@ namespace test{
         static QuTime
         build (TimeValue const& org)
           {
-            return QuTime (org, "test_grid");
+            return QuTime (org, "test_grid_PAL");
           }
       };
    
@@ -303,17 +300,17 @@ namespace test{
     void
     ____verify_nudged (TAR const& target, TAR const& refState, int64_t offsetSteps)
     {
-      CHECK (target != refState);
+      CHECK (target != refState  || !offsetSteps);
       CHECK (target == Time(refState)+Time(FSecs(offsetSteps)));
     }
     template<>
     void
     ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
     {
-      CHECK (target != refState);
+      CHECK (target != refState  || !offsetSteps);
       PQuant quantiser(target);
       CHECK (target == Time(quantiser->materialise(refState))
-                     + offsetSteps * FrameRate::NTSC.duration());
+                     + Offset(offsetSteps, FrameRate::PAL));
     }
     
     
diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp
index e2316f240..27656bd44 100644
--- a/tests/lib/time/time-value-test.cpp
+++ b/tests/lib/time/time-value-test.cpp
@@ -233,7 +233,14 @@ namespace test{
           CHECK (9 == off9);
           // simple linear combinations
           CHECK (7 == -2*off9 + off5*5);
-        }
+          
+          // build offset by number of frames
+          Offset byFrames(-125, FrameRate::PAL);
+          CHECK (Time(FSecs(-5)) == byFrames);
+          
+          CHECK (Offset(-5, FrameRate(5,4)) == -Offset(5, FrameRate(5,4)));
+          CHECK (Offset(3, FrameRate(3)) == Offset(12345, FrameRate(24690,2)));
+        }                                // precise rational number calculations
       
       
       void

From a1427bb0b9790b7aa42049a110004f3d6191792c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 11 Jun 2011 21:10:47 +0200
Subject: [PATCH 026/108] next challenge: expected behaviour for quantised
 source/target values

---
 tests/lib/time/time-control-test.cpp | 77 ++++++++++++++++++++++++----
 1 file changed, 66 insertions(+), 11 deletions(-)

diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index db1fa6d83..a57c758f2 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -28,6 +28,7 @@
 #include "lib/time/control.hpp"
 #include "proc/asset/meta/time-grid.hpp"
 #include "lib/meta/generator-combinations.hpp"
+#include "lib/meta/util.hpp"
 #include "lib/scoped-holder.hpp"
 #include "lib/util.hpp"
 
@@ -181,6 +182,36 @@ namespace test{
   
   namespace { // Implementation: Matrix of individual test combinations
     
+    using lumiera::typelist::is_sameType;
+    
+    template
+    inline bool
+    isDuration()
+      {
+        return is_sameType::value;
+      }
+    
+    template
+    inline bool
+    isQuTime()
+      {
+        return is_sameType::value;
+      }
+    
+    template
+    inline TimeValue
+    materialise (T const&)
+      {
+        NOTREACHED ("only grid aligned values can be materialised");
+      }
+    inline TimeValue
+    materialise (QuTime const& alignedTime)
+      {
+        PQuant grid(alignedTime);
+        return grid->materialise (alignedTime);
+      }
+    
+    
     template
     struct TestTarget
       {
@@ -248,16 +279,23 @@ namespace test{
     void
     ____verify_wasChanged (TAR const& target, TimeValue const& org, SRC const& change)
     {
-      CHECK (target != org);
-      CHECK (target == change);
+      if (isDuration())
+        {
+          CHECK (target == org, "Logic error: Duration was changed by time value");
+        }
+      else
+      if (isQuTime())
+        {
+          CHECK (target != org);
+          CHECK (target == materialise(change));
+        }
+      else
+        {
+          CHECK (target != org);
+          CHECK (target == change);
+        }
     }
     
-    template
-    void
-    ____verify_wasChanged (Duration const& target, TimeValue const& org, SRC const&)
-    {
-      CHECK (target == org, "Logic error: Duration was changed by time value");
-    }
     void
     ____verify_wasChanged (Duration const& target, TimeValue const& org, Duration const& otherDuration)
     {
@@ -308,8 +346,8 @@ namespace test{
     ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
     {
       CHECK (target != refState  || !offsetSteps);
-      PQuant quantiser(target);
-      CHECK (target == Time(quantiser->materialise(refState))
+      PQuant grid(target);
+      CHECK (target == Time (grid->materialise(refState))
                      + Offset(offsetSteps, FrameRate::PAL));
     }
     
@@ -318,7 +356,19 @@ namespace test{
     void
     ____verify_notification (TAR const& target, TestListener const& follower)
     {
-      CHECK (target == follower.receivedValue());
+      if (isDuration())
+        {
+          CHECK (Duration::NIL == follower.receivedValue());
+        }
+      else
+      if (isQuTime())
+        {
+          CHECK (materialise (target) == follower.receivedValue());
+        }
+      else
+        {
+          CHECK (target == follower.receivedValue());
+        }
     }
     void
     ____verify_notification (TimeSpan const& targetTimeSpan, TestListener const& follower)
@@ -326,6 +376,11 @@ namespace test{
       CHECK (follower.receivedValue() == targetTimeSpan.duration());
     }
     void
+    ____verify_notification (Duration const& target, TestListener const& follower)
+    {
+      CHECK (target == follower.receivedValue());
+    }
+    void
     ____verify_notification (Duration const& targetDuration, TestListener const& follower)
     {
       CHECK (Time::ZERO     == follower.receivedValue());

From fec2d25b52d596c12bfdcd884b85369ccf91ffa8 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Jun 2011 01:11:15 +0200
Subject: [PATCH 027/108] additional quantisation and duration canges covered
 and passing test

---
 src/lib/time/control.hpp             | 51 ++++++++++++++++++++++++----
 tests/lib/time/time-control-test.cpp | 15 +++++---
 2 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 48af1813c..60809e53d 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -200,13 +200,27 @@ namespace time {
         {
           return is_sameType::value;
         }
+      
+      template
+      inline T const&
+      maybeMaterialise (T const& non_grid_aligned_TimeValue)
+      {
+        return non_grid_aligned_TimeValue;
+      }
+#ifdef LIB_TIME_TIMEQUQNT_H
+      inline QuTime
+      maybeMaterialise (QuTime const& alignedTime)
+      {
+        PQuant const& grid(alignedTime);
+        return QuTime(grid->materialise(alignedTime), grid);
+      }
+#endif
     }
     
     
     template
     struct Builder
       {
-        
         static TI
         buildChangedValue (TAR const& target)
           {
@@ -235,7 +249,7 @@ namespace time {
     struct Builder
       {
         static TimeSpan
-        buildChangedValue (TimeSpan& target)
+        buildChangedValue (TimeSpan const& target)
           {
             return target;
           }
@@ -245,7 +259,7 @@ namespace time {
     struct Builder
       {
         static QuTime
-        buildChangedValue (TAR& target)
+        buildChangedValue (TAR const& target)
           {
             return QuTime (target
                           ,getDefaultGridFallback()                                                     //////////////////TICKET #810
@@ -256,7 +270,7 @@ namespace time {
     struct Builder
       {
         static QuTime
-        buildChangedValue (QuTime& target)
+        buildChangedValue (QuTime const& target)
           {
             return target;
           }
@@ -273,8 +287,8 @@ namespace time {
         static TI
         processValueChange (TAR& target, SRC const& change)
           {
-            imposeChange (target,change);
-            return buildChangedValue(target);
+            imposeChange (target, maybeMaterialise(change));
+            return buildChangedValue (maybeMaterialise(target));
           }
         
         static TI
@@ -329,6 +343,14 @@ namespace time {
                                ||   is_sameType::value
                                ||   is_sameType::value;
         };
+      
+      template
+      struct canReceiveDuration
+        {
+          static const bool value = is_sameType::value
+                               ||   is_sameType::value;
+        };
+      
     }
     
     template
@@ -342,6 +364,23 @@ namespace time {
           }
       };
     
+    template
+    struct Policy, 
+                     Duration>::type, TAR>
+      {
+        static function
+        buildChangeHandler (TAR&)
+          {
+            return bind ( ignore_change_and_return_Zero );
+          }
+        
+        static Duration
+        ignore_change_and_return_Zero()
+          {
+            return Duration::NIL;
+          }
+      };
+    
 //  template
 //  struct Policy
 //    {
diff --git a/tests/lib/time/time-control-test.cpp b/tests/lib/time/time-control-test.cpp
index a57c758f2..ab12a2591 100644
--- a/tests/lib/time/time-control-test.cpp
+++ b/tests/lib/time/time-control-test.cpp
@@ -200,9 +200,9 @@ namespace test{
     
     template
     inline TimeValue
-    materialise (T const&)
+    materialise (T const& someTime)
       {
-        NOTREACHED ("only grid aligned values can be materialised");
+        return someTime;
       }
     inline TimeValue
     materialise (QuTime const& alignedTime)
@@ -284,6 +284,11 @@ namespace test{
           CHECK (target == org, "Logic error: Duration was changed by time value");
         }
       else
+      if (isDuration())
+        {
+          CHECK (target == org, "Logic error: Duration used to change time value");
+        }
+      else
       if (isQuTime())
         {
           CHECK (target != org);
@@ -346,8 +351,7 @@ namespace test{
     ____verify_nudged (QuTime const& target, QuTime const& refState, int64_t offsetSteps)
     {
       CHECK (target != refState  || !offsetSteps);
-      PQuant grid(target);
-      CHECK (target == Time (grid->materialise(refState))
+      CHECK (target == Time (materialise(refState))
                      + Offset(offsetSteps, FrameRate::PAL));
     }
     
@@ -358,7 +362,8 @@ namespace test{
     {
       if (isDuration())
         {
-          CHECK (Duration::NIL == follower.receivedValue());
+          CHECK (materialise(target) == follower.receivedValue()
+                 ||    Duration::NIL == follower.receivedValue() );
         }
       else
       if (isQuTime())

From f4d0d23e4800a3fd1932ce372871aecaf321fc0f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Jun 2011 02:05:24 +0200
Subject: [PATCH 028/108] clean up leftovers from the first implementation
 attempts

---
 src/lib/time/control.hpp | 404 ++++++++-------------------------------
 1 file changed, 79 insertions(+), 325 deletions(-)

diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index 60809e53d..ba9fba5ce 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -62,27 +62,15 @@
 #include "lib/meta/util.hpp"
 #include "lib/time/mutation.hpp"
 #include "lib/time/timevalue.hpp"
-//#include "lib/symbol.hpp"
 
-//#include 
-//#include 
-//#include 
 #include 
 #include 
 #include 
-//#include 
 
 
 namespace lib {
 namespace time {
   
-//using lib::Symbol;
-//using std::string;
-//using lib::Literal;
-  
-  
-//LUMIERA_ERROR_DECLARE (INVALID_MUTATION); ///< Changing a time value in this way was not designated
-  
   namespace mutation {
     
     using boost::enable_if;
@@ -94,17 +82,23 @@ namespace time {
     using std::tr1::ref;
     
     
-    template
-    struct Mutabor;
-    
     
     /**
      * Implementation building block: impose changes to a Time element.
-     * The Mutator supports attaching a target time entity (through
-     * the Mutation interface), which then will be subject to any
-     * received value changes, offsets and grid nudging.
-     * 
-     * @todo WIP-WIP-WIP
+     * The purpose of the Mutator is to attach a target time entity,
+     * which then will be subject to any received value changes,
+     * offsets and grid nudging. The actual attachment is to be
+     * performed in a subclass, by using the Mutation interface.
+     * When attaching to a target, the Mutator will be outfitted 
+     * with a set of suitable functors, incorporating the specific
+     * behaviour for the concrete combination of input changes
+     * ("source values") and target object type. This works by
+     * binding to the appropriate implementation functionality,
+     * guided by a templated policy class. After installing
+     * these functors, these decisions remains opaque and
+     * encapsulated within the functor objects, so the
+     * mutator object doesn't need to carry this 
+     * type information on the interface
      */
     template
     class Mutator
@@ -114,18 +108,6 @@ namespace time {
         typedef function Ofsetter;
         typedef function           Nudger;
         
-        static TI imposeValueChange(TimeValue& target, TI const&);
-        static TI imposeOffset (TimeValue& target, Offset const&);
-        static TI imposeNudge (TimeValue& target, int);
-        
-        static TI changeDuration (Duration& target, TI const&);
-//      static TI nudgeDuration (Duration& target, int);
-        
-        template
-        struct MutationPolicy;
-        
-        friend class Mutabor;
-
       protected:
         mutable ValueSetter setVal_;
         mutable Ofsetter    offset_;
@@ -140,66 +122,84 @@ namespace time {
                                 ,error::LUMIERA_ERROR_UNCONNECTED);
           }
         
-      public:
-        // using default construction and copy
         
         template
         void bind_to (TAR& target)  const;
         
         void unbind();
         
+        // using default construction and copy
       };
     
+    
+    
+    /**
+     * Implementation building block: propagate changes to listeners.
+     * The Propagator manages a set of callback signals, allowing to
+     * propagate notifications for changed Time values.
+     * 
+     * There are no specific requirements on the acceptable listeners,
+     * besides exposing a function-call operator to feed the changed
+     * time value to. Both Mutator and Propagator employ one primary
+     * template parameter, which is the type of the time values
+     * to be fed in and propagated. 
+     */
     template
-    struct Mutabor
+    class Propagator
       {
+        typedef function ChangeSignal;
+        typedef std::vector ListenerList;
         
-        static void
-        imposeChange (TimeValue& target, TI const& newVal)
+        ListenerList listeners_;
+        
+      public:
+        /** install notification receiver */
+        template
+        void
+        attach (SIG const& toNotify)
           {
-            Mutator::imposeChange (target,newVal);
+            ChangeSignal newListener (ref(toNotify));
+            listeners_.push_back (newListener);
+          }
+        
+        /** disconnect any observers */
+        void
+        disconnnect()
+          {
+            listeners_.clear();
+          }
+        
+        /** publish a change */
+        TI
+        operator() (TI const& changedVal)  const
+          {
+            typedef typename ListenerList::const_iterator Iter;
+            Iter p = listeners_.begin();
+            Iter e = listeners_.end();
+            
+            for ( ; p!=e; ++p )
+              (*p) (changedVal);
+            return changedVal;
           }
       };
     
     
     
-    namespace { // metaprogramming helpers to pick the suitable Instantiation...
-      
-      template
-      struct isDurationZ
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isTimeSpanZ
-        {
-          static const bool value = is_sameType::value;
-        };
-      template
-      struct isQuTime
-        {
-          static const bool value = is_sameType::value;
-        };
-      
-      template
-      struct isSpecialCase
-        {
-          static const bool value = isDurationZ::value;
-        };
+    namespace { // metaprogramming helpers to pick suitable implementation branch...
       
       template
       inline bool
       isDuration()
-        {
-          return is_sameType::value;
-        }
+      {
+        return is_sameType::value;
+      }
       
       template
       inline bool
       isTimeSpan()
-        {
-          return is_sameType::value;
-        }
+      {
+        return is_sameType::value;
+      }
       
       template
       inline T const&
@@ -207,6 +207,7 @@ namespace time {
       {
         return non_grid_aligned_TimeValue;
       }
+      
 #ifdef LIB_TIME_TIMEQUQNT_H
       inline QuTime
       maybeMaterialise (QuTime const& alignedTime)
@@ -233,7 +234,7 @@ namespace time {
         static TimeSpan
         buildChangedValue (TAR const& target)
           {
-            return TimeSpan (target, Duration::NIL);  /////////////TODO how to feed the "new value" duration????
+            return TimeSpan (target, Duration::NIL);
           }
       };
     template<>
@@ -278,7 +279,7 @@ namespace time {
 #endif
     
     template
-    struct Adap
+    struct Link
       : Mutator
       , Builder
       {
@@ -328,7 +329,7 @@ namespace time {
         static function
         buildChangeHandler (TAR& target)
           {
-            return bind (Adap::template processValueChange, ref(target), _1 );
+            return bind (Link::template processValueChange, ref(target), _1 );
           }
       };
     
@@ -350,9 +351,9 @@ namespace time {
           static const bool value = is_sameType::value
                                ||   is_sameType::value;
         };
-      
     }
     
+    
     template
     struct Policy, 
                          Duration>::type>
@@ -360,7 +361,7 @@ namespace time {
         static function
         buildChangeHandler (Duration& target)
           {
-            return bind (Adap::dontChange, ref(target) );
+            return bind (Link::dontChange, ref(target) );
           }
       };
     
@@ -381,23 +382,13 @@ namespace time {
           }
       };
     
-//  template
-//  struct Policy
-//    {
-//      static function
-//      buildChangeHandler (Duration& target)
-//        {
-//          return bind (Adap::template processValueChange, ref(target), _1 );
-//        }
-//    };
-    
     template
     struct Policy
       {
         static function
         buildChangeHandler (Duration& target)
           {
-            return bind (Adap::useLengthAsChange, ref(target), _1 );
+            return bind (Link::useLengthAsChange, ref(target), _1 );
           }
       };
     
@@ -409,7 +400,7 @@ namespace time {
         static function
         buildChangeHandler (TimeSpan& target)
           {
-            return bind (Adap::mutateLength, ref(target), _1 );
+            return bind (Link::mutateLength, ref(target), _1 );
           }
       };
     
@@ -419,201 +410,11 @@ namespace time {
         static function
         buildChangeHandler (TimeSpan& target)
           {
-            return bind (Adap::mutateTimeSpan, ref(target), _1 );
+            return bind (Link::mutateTimeSpan, ref(target), _1 );
           }
       };
     
     
-/*    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Adap::imposeOffset, ref(target), _1 );
-          }
-      };
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Adap::imposeNudge, ref(target), _1 );
-          }
-      };
-*/    
-      
-    template
-    TI
-    Mutator::imposeValueChange (TimeValue& target, TI const& newVal)
-    {
-      return TI (Mutation::imposeChange (target,newVal));
-    }
-    template
-    TI
-    Mutator::imposeOffset (TimeValue& target, Offset const& off)
-    {
-      return TI (Mutation::imposeChange (target, TimeVar(target)+off));
-    }
-    template
-    TI
-    Mutator::imposeNudge (TimeValue& target, int off_by_steps)
-    {
-      return TI (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))));     //////////////////TICKET #810
-    }
-    
-    // special cases...
-    template
-    TI
-    Mutator::changeDuration (Duration&, TI const&)
-    {
-      return TI (Time::ZERO);
-    }
-    template<>
-    Duration
-    Mutator::changeDuration (Duration& target, Duration const& changedDur)
-    {
-      return Duration (Mutation::imposeChange (target,changedDur));
-    }
-    template<>
-    TimeSpan
-    Mutator::changeDuration (Duration& target, TimeSpan const& timeSpan_to_impose)
-    {
-      return TimeSpan (timeSpan_to_impose.start()
-                      ,Duration(Mutation::imposeChange (target,timeSpan_to_impose.duration()))
-                      );
-    }
-    
-    template<>
-    TimeSpan
-    Mutator::imposeValueChange (TimeValue& target, TimeSpan const& newVal)
-    {
-      Mutation::imposeChange (target,newVal);
-      return newVal;
-    }
-    template<>
-    TimeSpan
-    Mutator::imposeOffset (TimeValue& target, Offset const& off)
-    {
-      return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+off), Duration::NIL);
-    }
-    template<>
-    TimeSpan
-    Mutator::imposeNudge (TimeValue& target, int off_by_steps)
-    {
-      return TimeSpan (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps))), Duration::NIL);
-    }
-    
-
-#ifdef LIB_TIME_TIMEQUQNT_H
-    template<>
-    QuTime
-    Mutator::changeDuration (Duration&, QuTime const& irrelevantChange)
-    {
-      return QuTime (Time::ZERO, PQuant(irrelevantChange));
-    }
-    template<>
-    QuTime
-    Mutator::imposeValueChange (TimeValue& target, QuTime const& grid_aligned_Value_to_set)
-    {
-      PQuant quantiser (grid_aligned_Value_to_set);
-      TimeValue appliedChange = quantiser->materialise(grid_aligned_Value_to_set);
-      Mutation::imposeChange (target, appliedChange);
-      return QuTime (target, quantiser);
-    }
-    template<>
-    QuTime
-    Mutator::imposeOffset (TimeValue& target, Offset const& off)
-    {
-      return QuTime (Mutation::imposeChange (target, TimeVar(target)+off)
-                    ,getDefaultGridFallback()                                                     //////////////////TICKET #810
-                    );
-    }
-    template<>
-    QuTime
-    Mutator::imposeNudge (TimeValue& target, int off_by_steps)
-    {
-      return QuTime (Mutation::imposeChange (target, TimeVar(target)+Time(FSecs(off_by_steps)))
-                    ,getDefaultGridFallback()                                                     //////////////////TICKET #810
-                    );
-    }
-#endif //(End)quantisation special case    
-    
-    template
-    template
-    struct Mutator::MutationPolicy<                  typename disable_if< isSpecialCase, 
-                                       TI>::type, TAR>
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Mutator::imposeValueChange, ref(target), _1 );
-          }
-      };
-    
-    template
-    template
-    struct Mutator::MutationPolicy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Mutator::imposeOffset, ref(target), _1 );
-          }
-      };
-    
-    template
-    template
-    struct Mutator::MutationPolicy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Mutator::imposeNudge, ref(target), _1 );
-          }
-      };
-    
-    
-    //special cases for changing Durations....
-
-    template
-    template
-    struct Mutator::MutationPolicy<                  typename enable_if< isDurationZ, 
-                                       TI>::type, TAR>
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Mutator::changeDuration, ref(target), _1 );
-          }
-      };
-    
-    //    
-//    template
-//    template
-//    struct Mutator::MutationPolicy
-//      {
-//        static function
-//        buildChangeHandler (Duration& target)
-//          {
-//            return bind (Mutator::changeDuration, ref(target), _1 );
-//          }
-//      };
-    
-//    template
-//    template<>
-//    struct Mutator::MutationPolicy
-//      {
-//        static function
-//        buildChangeHandler (Duration& target)
-//          {
-//            return bind (Mutator::nudgeDuration, ref(target), _1 );
-//          }
-//      };
-    
     
     
     
@@ -637,59 +438,12 @@ namespace time {
       nudge_  = Nudger();
     }
     
-    
-    
-    /**
-     * Implementation building block: propagate changes to listeners.
-     * The Propagator manages a set of callback signals, allowing to
-     * propagate notifications for changed Time values.
-     * 
-     * @todo WIP-WIP-WIP
-     */
-    template
-    class Propagator
-      {
-        typedef function ChangeSignal;
-        typedef std::vector ListenerList;
-        
-        ListenerList listeners_;
-        
-      public:
-        /** install notification receiver */
-        template
-        void
-        attach (SIG const& toNotify)
-          {
-            ChangeSignal newListener (ref(toNotify));
-            listeners_.push_back (newListener);
-          }
-        
-        /** disconnect any observers */
-        void
-        disconnnect()
-          {
-            listeners_.clear();
-          }
-        
-        /** publish a change */
-        TI
-        operator() (TI const& changedVal)  const
-          {
-            typedef typename ListenerList::const_iterator Iter;
-            Iter p = listeners_.begin();
-            Iter e = listeners_.end();
-            
-            for ( ; p!=e; ++p )
-              (*p) (changedVal);
-            return changedVal;
-          }
-      };
   }
   
   
   /**
-   * Frontend/Interface: controller-element for retrieving and
-   * changing running time values
+   * Frontend/Interface: controller-element to retrieve
+   * and change running time values
    * 
    * @see time::Mutation
    * @see time::TimeSpan#accept(Mutation const&)
@@ -725,7 +479,7 @@ namespace time {
   
   
   
-  /* === implementation === */
+  /* === forward to implementation === */
   
   template
   void

From 2099ecbcac6d09a58ed19753db6a7b29cd929b23 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 12 Jun 2011 19:03:12 +0200
Subject: [PATCH 029/108] finish and comment the new time::Control facility

---
 src/lib/time/control-impl.hpp        | 203 ++++++++++++
 src/lib/time/control-policy.hpp      | 400 +++++++++++++++++++++++
 src/lib/time/control.hpp             | 467 +++++----------------------
 tests/40components.tests             |  17 +-
 tests/lib/time/time-control-test.cpp |  70 ++--
 5 files changed, 747 insertions(+), 410 deletions(-)
 create mode 100644 src/lib/time/control-impl.hpp
 create mode 100644 src/lib/time/control-policy.hpp

diff --git a/src/lib/time/control-impl.hpp b/src/lib/time/control-impl.hpp
new file mode 100644
index 000000000..1d2ab6d94
--- /dev/null
+++ b/src/lib/time/control-impl.hpp
@@ -0,0 +1,203 @@
+/*
+  CONTROL-IMPL.hpp  -  time::control implementation building blocks  
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+/** @file control-impl.hpp
+ ** Implementation building blocks for time modification and propagation.
+ ** The time::Control element allows to impose modifications to a connected
+ ** time value entity and at the same time publish the changes to registered
+ ** listeners. Due to the various flavours of actual time value entities, this
+ ** is a complex undertaking, which is implemented here based on policies and
+ ** template metaprogramming. This header/include defines two building blocks:
+ ** - the actual Mutator to apply the changes to the target entity
+ ** - a Propagator to register listeners and forward the changes.
+ ** 
+ ** \par implementation technique
+ ** 
+ ** The Mutator uses functor objects to encapsulate the actual modification
+ ** operations. When attaching to a target time entity to be manipulated, these
+ ** functor objects will be configured by binding them to the appropriate
+ ** implementation function. And picking this actual implementation is done
+ ** through a time::mutation::Policy element, using the concrete time entity
+ ** types as template parameter. Thus, the actual implementation to be used
+ ** is determined by the compiler, through the template specialisations
+ ** contained in control-policy.hpp
+ ** 
+ ** @note the header control-policy.hpp with the template specialisations
+ **       is included way down, after the class definitions. This is done
+ **       so for sake of readability
+ ** 
+ ** @see TimeControl_test
+ **
+ */
+
+#ifndef LIB_TIME_CONTROL_IMPL_H
+#define LIB_TIME_CONTROL_IMPL_H
+
+#include "lib/error.hpp"
+#include "lib/time/mutation.hpp"
+#include "lib/time/timevalue.hpp"
+
+#include 
+
+
+namespace lib {
+namespace time {
+namespace mutation {
+  
+  
+  
+  
+  /**
+   * Implementation building block: impose changes to a Time element.
+   * The purpose of the Mutator is to attach a target time entity,
+   * which then will be subject to any received value changes,
+   * offsets and grid nudging. The actual attachment is to be
+   * performed in a subclass, by using the Mutation interface.
+   * When attaching to a target, the Mutator will be outfitted 
+   * with a set of suitable functors, incorporating the specific
+   * behaviour for the concrete combination of input changes
+   * ("source values") and target object type. This works by
+   * binding to the appropriate implementation functionality,
+   * guided by a templated policy class. After installing
+   * these functors, these decisions remains opaque and
+   * encapsulated within the functor objects, so the
+   * mutator object doesn't need to carry this 
+   * type information on the interface
+   */
+  template
+  class Mutator
+    : public Mutation
+    {
+      typedef function     ValueSetter;
+      typedef function Ofsetter;
+      typedef function           Nudger;
+      
+    protected:
+      mutable ValueSetter setVal_;
+      mutable Ofsetter    offset_;
+      mutable Nudger      nudge_;
+      
+      void
+      ensure_isArmed()  const
+        {
+          if (!setVal_)
+            throw error::State("feeding time/value change "
+                               "while not (yet) connected to any target to change"
+                              ,error::LUMIERA_ERROR_UNCONNECTED);
+        }
+      
+      
+      template
+      void bind_to (TAR& target)  const;
+      
+      void unbind();
+      
+      // using default construction and copy
+    };
+  
+  
+  
+  /**
+   * Implementation building block: propagate changes to listeners.
+   * The Propagator manages a set of callback signals, allowing to
+   * propagate notifications for changed Time values.
+   * 
+   * There are no specific requirements on the acceptable listeners,
+   * besides exposing a function-call operator to feed the changed
+   * time value to. Both Mutator and Propagator employ one primary
+   * template parameter, which is the type of the time values
+   * to be fed in and propagated. 
+   */
+  template
+  class Propagator
+    {
+      typedef function ChangeSignal;
+      typedef std::vector ListenerList;
+      
+      ListenerList listeners_;
+      
+    public:
+      /** install notification receiver */
+      template
+      void
+      attach (SIG const& toNotify)
+        {
+          ChangeSignal newListener (ref(toNotify));
+          listeners_.push_back (newListener);
+        }
+      
+      /** disconnect any observers */
+      void
+      disconnnect()
+        {
+          listeners_.clear();
+        }
+      
+      /** publish a change */
+      TI
+      operator() (TI const& changedVal)  const
+        {
+          typedef typename ListenerList::const_iterator Iter;
+          Iter p = listeners_.begin();
+          Iter e = listeners_.end();
+          
+          for ( ; p!=e; ++p )
+            (*p) (changedVal);
+          return changedVal;
+        }
+      
+      // using default construction and copy
+    };
+  
+}}} // lib::time::mutation
+
+
+/* ===== Definition of actual operations ===== */
+#include "lib/time/control-policy.hpp"
+
+
+
+
+template
+template
+void
+lib::time::mutation::Mutator::bind_to (TAR& target)  const
+{
+  using lib::time::mutation::Policy;
+  
+  setVal_ = Policy::buildChangeHandler (target);
+  offset_ = Policy::buildChangeHandler (target);
+  nudge_  = Policy::buildChangeHandler (target);
+}
+
+template
+void
+lib::time::mutation::Mutator::unbind()
+{
+  setVal_ = ValueSetter();
+  offset_ = Ofsetter();
+  nudge_  = Nudger();
+}
+
+
+#endif
diff --git a/src/lib/time/control-policy.hpp b/src/lib/time/control-policy.hpp
new file mode 100644
index 000000000..a941c3c96
--- /dev/null
+++ b/src/lib/time/control-policy.hpp
@@ -0,0 +1,400 @@
+/*
+  CONTROL-POLICY.hpp  -  detail definition of actual time changing functionality  
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+/** @file control-policy.hpp
+ ** Definition of special cases when imposing a change onto concrete time values.
+ ** The time::Control element allows to impose modifications to a connected
+ ** time value entity and at the same time publish the changes to registered
+ ** listeners. Due to the various flavours of actual time value entities, this
+ ** is a complex undertaking, which is implemented here based on policies and
+ ** template metaprogramming.
+ ** 
+ ** The header control-impl.hpp defines the building blocks for time::Control
+ ** and then includes this header here to get the concrete template specialisations
+ ** for time::mutation::Policy. This policy class is templated by time entity types
+ ** - for \c TI, the \em nominal value type used on the time::Control interface
+ ** - for \c SRC, the actual type of values to impose as \em change
+ ** - for \c TAR, the target time value's type, receiving those changes.
+ ** 
+ ** \par mutating a time value entity
+ ** 
+ ** Actually imposing a change to the attached time value entity involves several
+ ** steps. Each of these steps might be adapted specifically, in accordance to
+ ** the concrete time value types involved.
+ ** - TimeValue, Time
+ ** - Offset
+ ** - Duration
+ ** - TimeSpan
+ ** - QuTime (grid aligned time value)
+ ** - QuTimeSpan (planned as of 6/2011)
+ ** 
+ ** Moreover, the combination of types needs to be taken into account. For example,
+ ** it doesn't make sense to apply a Duration value as change to a TimeValue, which
+ ** has no duration (temporal extension). While a TimeSpan might receive a Duration
+ ** change, but behaves differently when imposing a Time to manipulate the starting
+ ** point of the time interval given by the TimeSpan.
+ ** 
+ ** Incoming changes might be of any of the aforementioned types, and in addition,
+ ** we might receive \em nudging, which means to increment or decrement the target
+ ** time value in discrete steps. After maybe adapting these incoming change values,
+ ** they may be actually \em imposed to the target. In all cases, this is delegated
+ ** to the time::Mutation base class, which is declared fried to TimeValue and thus
+ ** has the exceptional ability to manipulate time values, which otherwise are defined
+ ** to be immutable. Additionally, these protected functions in the time::Mutation
+ ** baseclass also know how to handle \em nudge values, either by using the native
+ ** (embedded) time grid of a quantised time value, or by falling back to a standard
+ ** nudging grid, defined in the session context (TODO as of 6/2011).                     //////////////////////TICKET #810
+ ** 
+ ** After (maybe) imposing a change to the target, the change \em notification value
+ ** needs to be built. This is the time value entity to be forwarded to registered
+ ** listeners. This notification value has to be given as the type \c TI, in accordance
+ ** to the \c time::Control frontend definition used in the concrete usage situation.
+ ** As this type \c TI might be different to the actual target type, and again different
+ ** to the type of the change handed in, in some cases this involves a second conversion
+ ** step, to represent the current state of the target \c TAR in terms of the interface
+ ** type \c TI.
+ ** 
+ ** \par changing quantised (grid aligned) time entities
+ ** 
+ ** The time::Control element includes the capability to handle grid aligned time values,
+ ** both as target and as change/notification value. This ability is compiled in conditionally,
+ ** as including mutation.hpp causes several additional includes, which isn't desirable when
+ ** it comes just to changing plain time values. Thus, to get these additional specialisations,
+ ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
+ ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
+ ** 
+ ** As a special convention, any \em quantised (grid aligned) types involved in these
+ ** time changes will be \em materialised, whenever a type conversion happens. Generally
+ ** speaking, a quantised time value contains an (opaque) raw time value, plus a reference
+ ** to a time grid definition to apply. In this context \em materialising means actually
+ ** to apply this time grid to yield a grid aligned value. Thus, when using a quantised
+ ** value to impose as change (or to receive a change), its grid aligning nature
+ ** becomes effective, by applying the \em current definition of the grid to
+ ** create a fixed (materialised) time value, aligned to that current grid.
+ ** 
+ ** @todo 6/2011 include all the special cases for QuTimeSpan                               ////////////////////TICKET #760
+ ** 
+ ** @see TimeControl_test
+ **
+ */
+
+#ifndef LIB_TIME_CONTROL_POLICY_H
+#define LIB_TIME_CONTROL_POLICY_H
+
+#include "lib/meta/util.hpp"
+#include "lib/time/mutation.hpp"
+#include "lib/time/timevalue.hpp"
+
+#include 
+#include 
+
+
+namespace lib {
+namespace time {
+  
+namespace mutation {
+  
+  using boost::disable_if;
+  using lumiera::typelist::is_sameType;
+  using std::tr1::placeholders::_1;
+  using std::tr1::function;
+  using std::tr1::bind;
+  using std::tr1::ref;
+  
+  
+  
+  namespace { // metaprogramming helpers to pick a suitable implementation branch...
+    
+    template
+    inline bool
+    isDuration()
+    {
+      return is_sameType::value;
+    }
+    
+    template
+    inline bool
+    isTimeSpan()
+    {
+      return is_sameType::value;
+    }
+    
+    template
+    inline T const&
+    maybeMaterialise (T const& non_grid_aligned_TimeValue)
+    {
+      return non_grid_aligned_TimeValue;
+    }
+    
+#ifdef LIB_TIME_TIMEQUQNT_H
+    inline QuTime
+    maybeMaterialise (QuTime const& alignedTime)
+    {
+      PQuant const& grid(alignedTime);
+      return QuTime(grid->materialise(alignedTime), grid);
+    }
+#endif //--quantised-time-support
+  }
+  
+  
+  /** 
+   * Implementation policy: how to build a new
+   * notification value of type \c TI, given a 
+   * target time value entity of type \c TAR
+   */
+  template
+  struct Builder
+    {
+      static TI
+      buildChangedValue (TAR const& target)
+        {
+          return TI(target);
+        }
+    };
+  template
+  struct Builder
+    {
+      static TimeSpan
+      buildChangedValue (TAR const& target)
+        {
+          return TimeSpan (target, Duration::NIL);
+        }
+    };
+  template<>
+  struct Builder
+    {
+      static TimeSpan
+      buildChangedValue (Duration const& targetDuration)
+        {
+          return TimeSpan (Time::ZERO, targetDuration);
+        }
+    };
+  template<>
+  struct Builder
+    {
+      static TimeSpan
+      buildChangedValue (TimeSpan const& target)
+        {
+          return target;
+        }
+    };
+#ifdef LIB_TIME_TIMEQUQNT_H
+  template
+  struct Builder
+    {
+      static QuTime
+      buildChangedValue (TAR const& target)
+        {
+          return QuTime (target
+                        ,getDefaultGridFallback()                                                     //////////////////TICKET #810
+                        );
+        }
+    };
+  template<>
+  struct Builder
+    {
+      static QuTime
+      buildChangedValue (QuTime const& target)
+        {
+          return target;
+        }
+    };
+#endif //--quantised-time-support
+  
+  
+  
+  
+  /**  
+   * Policy to tie the various detail policies together
+   * for providing actual value change operations.
+   * The standard case uses the (inherited) time::Mutation
+   * base implementation to impose a new value onto the
+   * target entity and then uses the Builder policy to
+   * create a notification value reflecting this change.
+   */
+  template
+  struct Link
+    : Mutator
+    , Builder
+    {
+      
+      template
+      static TI
+      processValueChange (TAR& target, SRC const& change)  ///< standard case: plain value change
+        {
+          imposeChange (target, maybeMaterialise(change));
+          return buildChangedValue (maybeMaterialise(target));
+        }
+      
+      static TI
+      useLengthAsChange (TAR& target, TimeSpan const& change)
+        {
+          return processValueChange(target, change.duration());
+        }
+      
+      static TI
+      mutateLength (TimeSpan& target, Duration const& change)
+        {
+          Mutator::imposeChange (target.duration(), change);
+          return Builder::buildChangedValue(target);
+        }
+      
+      static TimeSpan
+      mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
+        {
+          Mutator::imposeChange (target.duration(), change.duration());
+          Mutator::imposeChange (target,change.start());
+          return Builder::buildChangedValue(target);
+        }
+      
+      static TI
+      dontChange (TAR& target)  ///< @note: not touching the target
+        {
+          return buildChangedValue(target);
+        }
+    };
+  
+  
+  
+  /** 
+   * Policy how to impose changes onto a connected target time value entity
+   * This policy will be parametrised with the concrete time entity types
+   * involved in the usage situation of time::Control. The purpose of the
+   * policy is to \em bind a functor object to the concrete implementation
+   * of the value change applicable for this combination of types.
+   * This functor will then be stored within time::Control and
+   * invoked for each actual value change.
+   * @param TI the nominal (interface) type of the change, propagated to listeners
+   * @param SRC the actual type of the change to be imposed
+   * @param TAR the actual type of the target entity to receive the changes
+   * @note typically either SRC is identical to TI, or it is an
+   *       time::Offset, or an int for \em nudging the target 
+   */
+  template
+  struct Policy
+    {
+      static function
+      buildChangeHandler (TAR& target)
+        {
+          return bind (Link::template processValueChange, ref(target), _1 );
+        }
+    };
+  
+  
+  // special treatment of Durations as target------------------------------------
+  
+  namespace {
+    template
+    struct canMutateDuration
+      {
+        static const bool value = is_sameType::value
+                             ||   is_sameType::value
+                             ||   is_sameType::value;
+      };
+    
+    template
+    struct canReceiveDuration
+      {
+        static const bool value = is_sameType::value
+                             ||   is_sameType::value;
+      };
+  }
+  
+  
+  /** 
+   * special case: a Duration target value can't be changed by plain time values.
+   * This specialisation is \em not used (\c disable_if ) when the given change (SRC)
+   * is applicable to a Duration in a sensible way. We either define explicit
+   * specialisations (for TimeSpan) or fall back to the default in these cases. 
+   */
+  template
+  struct Policy, 
+                       Duration>::type>
+    {
+      static function
+      buildChangeHandler (Duration& target)
+        {
+          return bind (Link::dontChange, ref(target) );
+        }
+    };
+  
+  /**
+   * special case: a Duration change value can't be imposed to a plain time value.
+   * In these cases, we even propagate a Duration::ZERO to the listeners.
+   * As above, there are exceptions to this behaviour, where a Duration change
+   * can sensibly be applied. 
+   */
+  template
+  struct Policy, 
+                   Duration>::type, TAR>
+    {
+      static function
+      buildChangeHandler (TAR&)
+        {
+          return bind ( ignore_change_and_return_Zero );
+        }
+      
+      static Duration
+      ignore_change_and_return_Zero()
+        {
+          return Duration::NIL;
+        }
+    };
+  
+  template
+  struct Policy
+    {
+      static function
+      buildChangeHandler (Duration& target)
+        {
+          return bind (Link::useLengthAsChange, ref(target), _1 );
+        }
+    };
+  
+  
+  // special treatment for TimeSpan values---------------------------------------
+  
+  template
+  struct Policy
+    {
+      static function
+      buildChangeHandler (TimeSpan& target)
+        {
+          return bind (Link::mutateLength, ref(target), _1 );
+        }
+    };
+  
+  template<>
+  struct Policy
+    {
+      static function
+      buildChangeHandler (TimeSpan& target)
+        {
+          return bind (Link::mutateTimeSpan, ref(target), _1 );
+        }
+    };
+  
+  
+  
+}}} // namespace lib::time::mutation
+#endif
diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index ba9fba5ce..cbac65d33 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -40,6 +40,46 @@
  ** of time-like entities -- be it the running time display in a GUI widget, a ruler marker
  ** which can be dragged, a modifiable selection or the animated playhead cursor.
  ** 
+ ** \par usage scenarios
+ ** The time::Control element provides mediating functionality, but doesn't assume or provide
+ ** anything special regarding the usage pattern or the lifecycle, beyond the ability to
+ ** attach listeners, attach to a (different) target and to detach from all connections.
+ ** Especially, no assumptions are made about which side is the server or the client
+ ** and who owns the time::Control element.
+ ** 
+ ** Thus an interface might accept a time::Control element \em reference (e.g. the
+ ** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
+ ** Control element and might attach listeners, while the implementation (server side)
+ ** will attach the Control to mutate an time value entity otherwise not disclosed
+ ** (e.g. the playhead position of the playback process). Of course, in this case
+ ** the client is responsible for keeping the Control element and all listeners
+ ** alive, and to invoke Control#disconnect prior to destroying the element.
+ ** 
+ ** Of course, the reversed usage situation would be possible as well: an interface
+ ** exposing a time::Control, thus allowing to attach target and listeners, while the
+ ** actual changes will originate somewhere within the service implementation.
+ ** 
+ ** Another usage pattern would be to expose a time::Control \c const&, allowing only to
+ ** impose changes, but not to change the target or listener attachments. To the contrary,
+ ** when exposing only a time::Mutation \c const& through an interface allows only to
+ ** attach new target elements, but not to change listeners or feed any value changes.
+ ** 
+ ** Using time::Control as an implementation building block and just exposing the
+ ** change (function) operators or the listener attachment through an forwarding sub
+ ** interface is another option.
+ ** 
+ ** @note time::Control is default constructible and freely copyable.
+ ** 
+ ** 
+ ** \par changing quantised (grid aligned) time entities
+ ** 
+ ** The time::Control element includes the functionality to handle grid aligned time values,
+ ** both as target and as change/notification value. This ability is compiled in conditionally,
+ ** as including mutation.hpp causes several additional includes, which isn't desirable when
+ ** it comes just to changing plain time values. Thus, to get these additional specialisations,
+ ** the LIB_TIME_TIMEQUQNT_H header guard needs to be defined, which happens automatically
+ ** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
+ ** 
  ** \par implementation notes
  ** - the validity of a given combination of change and target is checked immediately,
  **   when connecting to the target. Depending on the situation, the actual changes later
@@ -48,406 +88,42 @@
  **   processed within its own call context (function invocation), parallelism is only
  **   a concern with respect to the value finally visible within the target.
  ** - the change notification is processed right away, after applying the change to the
- **   target; in all cases, the effective change value is what will be propagated, \em not
- **   the content of the target after applying the change
+ **   target; of course there is a race between applying the value and building the
+ **   response value passed on as notification. In all cases, the effective change
+ **   notification value is built from the state of the target after applying
+ **   the change, which might or might not reflect the change value passed in.
  ** 
- ** @todo WIP-WIP-WIP
+ ** @todo include support for QuTimeSpan values                               ////////////////////TICKET #760
  **
  */
 
 #ifndef LIB_TIME_CONTROL_H
 #define LIB_TIME_CONTROL_H
 
-#include "lib/error.hpp"
-#include "lib/meta/util.hpp"
 #include "lib/time/mutation.hpp"
 #include "lib/time/timevalue.hpp"
+#include "lib/time/control-impl.hpp"
 
-#include 
-#include 
-#include 
 
 
 namespace lib {
 namespace time {
   
-  namespace mutation {
-    
-    using boost::enable_if;
-    using boost::disable_if;
-    using lumiera::typelist::is_sameType;
-    using std::tr1::placeholders::_1;
-    using std::tr1::function;
-    using std::tr1::bind;
-    using std::tr1::ref;
-    
-    
-    
-    /**
-     * Implementation building block: impose changes to a Time element.
-     * The purpose of the Mutator is to attach a target time entity,
-     * which then will be subject to any received value changes,
-     * offsets and grid nudging. The actual attachment is to be
-     * performed in a subclass, by using the Mutation interface.
-     * When attaching to a target, the Mutator will be outfitted 
-     * with a set of suitable functors, incorporating the specific
-     * behaviour for the concrete combination of input changes
-     * ("source values") and target object type. This works by
-     * binding to the appropriate implementation functionality,
-     * guided by a templated policy class. After installing
-     * these functors, these decisions remains opaque and
-     * encapsulated within the functor objects, so the
-     * mutator object doesn't need to carry this 
-     * type information on the interface
-     */
-    template
-    class Mutator
-      : public Mutation
-      {
-        typedef function     ValueSetter;
-        typedef function Ofsetter;
-        typedef function           Nudger;
-        
-      protected:
-        mutable ValueSetter setVal_;
-        mutable Ofsetter    offset_;
-        mutable Nudger      nudge_;
-        
-        void
-        ensure_isArmed()
-          {
-            if (!setVal_)
-              throw error::State("feeding time/value change "
-                                 "while not (yet) connected to any target to change"
-                                ,error::LUMIERA_ERROR_UNCONNECTED);
-          }
-        
-        
-        template
-        void bind_to (TAR& target)  const;
-        
-        void unbind();
-        
-        // using default construction and copy
-      };
-    
-    
-    
-    /**
-     * Implementation building block: propagate changes to listeners.
-     * The Propagator manages a set of callback signals, allowing to
-     * propagate notifications for changed Time values.
-     * 
-     * There are no specific requirements on the acceptable listeners,
-     * besides exposing a function-call operator to feed the changed
-     * time value to. Both Mutator and Propagator employ one primary
-     * template parameter, which is the type of the time values
-     * to be fed in and propagated. 
-     */
-    template
-    class Propagator
-      {
-        typedef function ChangeSignal;
-        typedef std::vector ListenerList;
-        
-        ListenerList listeners_;
-        
-      public:
-        /** install notification receiver */
-        template
-        void
-        attach (SIG const& toNotify)
-          {
-            ChangeSignal newListener (ref(toNotify));
-            listeners_.push_back (newListener);
-          }
-        
-        /** disconnect any observers */
-        void
-        disconnnect()
-          {
-            listeners_.clear();
-          }
-        
-        /** publish a change */
-        TI
-        operator() (TI const& changedVal)  const
-          {
-            typedef typename ListenerList::const_iterator Iter;
-            Iter p = listeners_.begin();
-            Iter e = listeners_.end();
-            
-            for ( ; p!=e; ++p )
-              (*p) (changedVal);
-            return changedVal;
-          }
-      };
-    
-    
-    
-    namespace { // metaprogramming helpers to pick suitable implementation branch...
-      
-      template
-      inline bool
-      isDuration()
-      {
-        return is_sameType::value;
-      }
-      
-      template
-      inline bool
-      isTimeSpan()
-      {
-        return is_sameType::value;
-      }
-      
-      template
-      inline T const&
-      maybeMaterialise (T const& non_grid_aligned_TimeValue)
-      {
-        return non_grid_aligned_TimeValue;
-      }
-      
-#ifdef LIB_TIME_TIMEQUQNT_H
-      inline QuTime
-      maybeMaterialise (QuTime const& alignedTime)
-      {
-        PQuant const& grid(alignedTime);
-        return QuTime(grid->materialise(alignedTime), grid);
-      }
-#endif
-    }
-    
-    
-    template
-    struct Builder
-      {
-        static TI
-        buildChangedValue (TAR const& target)
-          {
-            return TI(target);
-          }
-      };
-    template
-    struct Builder
-      {
-        static TimeSpan
-        buildChangedValue (TAR const& target)
-          {
-            return TimeSpan (target, Duration::NIL);
-          }
-      };
-    template<>
-    struct Builder
-      {
-        static TimeSpan
-        buildChangedValue (Duration const& targetDuration)
-          {
-            return TimeSpan (Time::ZERO, targetDuration);
-          }
-      };
-    template<>
-    struct Builder
-      {
-        static TimeSpan
-        buildChangedValue (TimeSpan const& target)
-          {
-            return target;
-          }
-      };
-#ifdef LIB_TIME_TIMEQUQNT_H
-    template
-    struct Builder
-      {
-        static QuTime
-        buildChangedValue (TAR const& target)
-          {
-            return QuTime (target
-                          ,getDefaultGridFallback()                                                     //////////////////TICKET #810
-                          );
-          }
-      };
-    template<>
-    struct Builder
-      {
-        static QuTime
-        buildChangedValue (QuTime const& target)
-          {
-            return target;
-          }
-      };
-#endif
-    
-    template
-    struct Link
-      : Mutator
-      , Builder
-      {
-        
-        template
-        static TI
-        processValueChange (TAR& target, SRC const& change)
-          {
-            imposeChange (target, maybeMaterialise(change));
-            return buildChangedValue (maybeMaterialise(target));
-          }
-        
-        static TI
-        useLengthAsChange (TAR& target, TimeSpan const& change)
-          {
-            return processValueChange(target, change.duration());
-          }
-        
-        static TI
-        mutateLength (TimeSpan& target, Duration const& change)
-          {
-            Mutator::imposeChange (target.duration(), change);
-            return Builder::buildChangedValue(target);
-          }
-        
-        static TimeSpan
-        mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
-          {
-            Mutator::imposeChange (target.duration(), change.duration());
-            Mutator::imposeChange (target,change.start());
-            return Builder::buildChangedValue(target);
-          }
-        
-        static TI
-        dontChange (TAR& target)
-          {
-            // note: not touching the target
-            return buildChangedValue(target);
-          }
-      };
-    
-    
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TAR& target)
-          {
-            return bind (Link::template processValueChange, ref(target), _1 );
-          }
-      };
-    
-    
-    // special treatment of Durations as target...
-    
-    namespace {
-      template
-      struct canMutateDuration
-        {
-          static const bool value = is_sameType::value
-                               ||   is_sameType::value
-                               ||   is_sameType::value;
-        };
-      
-      template
-      struct canReceiveDuration
-        {
-          static const bool value = is_sameType::value
-                               ||   is_sameType::value;
-        };
-    }
-    
-    
-    template
-    struct Policy, 
-                         Duration>::type>
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Link::dontChange, ref(target) );
-          }
-      };
-    
-    template
-    struct Policy, 
-                     Duration>::type, TAR>
-      {
-        static function
-        buildChangeHandler (TAR&)
-          {
-            return bind ( ignore_change_and_return_Zero );
-          }
-        
-        static Duration
-        ignore_change_and_return_Zero()
-          {
-            return Duration::NIL;
-          }
-      };
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (Duration& target)
-          {
-            return bind (Link::useLengthAsChange, ref(target), _1 );
-          }
-      };
-    
-    // special treatment for TimeSpan values...
-    
-    template
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TimeSpan& target)
-          {
-            return bind (Link::mutateLength, ref(target), _1 );
-          }
-      };
-    
-    template<>
-    struct Policy
-      {
-        static function
-        buildChangeHandler (TimeSpan& target)
-          {
-            return bind (Link::mutateTimeSpan, ref(target), _1 );
-          }
-      };
-    
-    
-    
-    
-    
-    
-    template
-    template
-    void
-    Mutator::bind_to (TAR& target)  const
-    {
-      setVal_ = Policy::buildChangeHandler (target);
-      offset_ = Policy::buildChangeHandler (target);
-      nudge_  = Policy::buildChangeHandler (target);
-    }
-    
-    template
-    void
-    Mutator::unbind()
-    {
-      setVal_ = ValueSetter();
-      offset_ = Ofsetter();
-      nudge_  = Nudger();
-    }
-    
-  }
   
   
   /**
    * Frontend/Interface: controller-element to retrieve
-   * and change running time values
+   * and change running time values. time::Control is
+   * a mediator element, which can be attached to some
+   * time value entities as \em mutation, and at the
+   * same time allows to register listeners. When
+   * configured this way, \em changes may be fed
+   * to the function operator(s). These changes
+   * will be imposed to the connected target
+   * and the result propagated to the listeners.
    * 
    * @see time::Mutation
    * @see time::TimeSpan#accept(Mutation const&)
-   * @todo WIP-WIP-WIP
    */
   template
   class Control
@@ -460,9 +136,9 @@ namespace time {
       virtual void change (QuTime&)    const;
         
     public:
-      void operator() (TI const&);
-      void operator() (Offset const&);
-      void operator() (int);
+      void operator() (TI const&)      const;
+      void operator() (Offset const&)  const;
+      void operator() (int)            const;
       
       
       /** install a callback functor to be invoked as notification
@@ -481,27 +157,47 @@ namespace time {
   
   /* === forward to implementation === */
   
+  /** impose a new value to the connected target.
+   *  If applicable, the target will afterwards reflect
+   *  that change, and listeners will be notified, passing
+   *  the target's new state.
+   * @throw error::State if not connected to a target
+   * @note the actual change in the target also depends
+   *       on the concrete target type and the type of
+   *       the change. By default, the time value is
+   *       changed; this may include grid alignment.
+   */
   template
   void
-  Control::operator () (TI const& newValue)
+  Control::operator () (TI const& newValue)  const
   {
     this->ensure_isArmed();
     notifyListeners_(
         this->setVal_(newValue));
   }
   
+  /** impose an offset to the connected target.
+   *  If applicable, the target will be adjusted by the
+   *  time offset, and listeners will be notified.
+   * @throw error::State if not connected to a target
+   */
   template
   void
-  Control::operator () (Offset const& adjustment)
+  Control::operator () (Offset const& adjustment)  const
   {
     this->ensure_isArmed();
     notifyListeners_(
         this->offset_(adjustment));
   }
   
+  /** nudge the connected target by the given offset steps,
+   *  using either the target's own grid (when quantised),
+   *  or a 'natural' nudge grid
+   * @throw error::State if not connected to a target
+   */
   template
   void
-  Control::operator () (int offset_by_steps)
+  Control::operator () (int offset_by_steps)  const
   {
     this->ensure_isArmed();
     notifyListeners_(
@@ -530,6 +226,9 @@ namespace time {
     notifyListeners_.attach (toNotify);
   }
   
+  
+  /* ==== Implementation of the Mutation interface ==== */
+  
   template
   void
   Control::change (Duration& targetDuration)  const
diff --git a/tests/40components.tests b/tests/40components.tests
index 4541270c7..999817faa 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -688,7 +688,22 @@ return: 0
 END
 
 
-PLANNED "Life changing time specifications with feedback" TimeControl_test  <
@@ -57,7 +58,10 @@ namespace test{
   using lumiera::typelist::InstantiateChainedCombinations;
   using error::LUMIERA_ERROR_UNCONNECTED;
   
-  namespace {
+  
+  
+  namespace { // Test setup and helpers....
+    
     inline string
     pop (Arg arg)
     {
@@ -105,21 +109,25 @@ namespace test{
             return *received_;
           }
       };
-  }
+      
+  }//(End)Test helpers
   
   
   
   
-  /****************************************************************
-   * @test use the time::Control to push a sequence of modifications
-   *       to various time entities; in all cases, a suitable change
-   *       should be imposed to the target and then a notification signal
+  /***********************************************************************
+   * @test use the time::Control to push a sequence of modifications to
+   *       various time entities; in all cases, a suitable change should
+   *       be imposed to the target and then a notification signal
    *       should be invoked.
-   * @todo cover the cases.....
-   *       - change to a given value
-   *       - change by an offset
-   *       - change using a grid value
-   *       - apply an (grid) increment
+   *       
+   *       After covering a simple basic case, this test uses
+   *       template metaprogramming techniques to build a matrix of all
+   *       possible type combinations and then performs a standard test
+   *       sequence for each of these type combinations. Within this
+   *       test sequence, verification functions are invoked, which
+   *       are defined with specialisations to adapt for the various
+   *       types to be covered.
    */
   class TimeControl_test : public Test
     {
@@ -176,6 +184,11 @@ namespace test{
         }
       
       
+      /** @test cover all possible combinations of input change values
+       *        and target time value entities to be handled by time::Control.
+       *        Each of these cases executes a standard test sequence, which is
+       *        defined in TestCase#performTestSequence
+       */
       void verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c);
     };
   
@@ -241,7 +254,7 @@ namespace test{
             return QuTime (org, "test_grid_PAL");
           }
       };
-   
+    
     
     template
     struct TestChange
@@ -395,17 +408,21 @@ namespace test{
     
     
     
-    template
+    template< class TAR     ///< type of the target time value entity to receive changes
+            , class SRC     ///< type of the time value to be imposed as change
+            , class BASE
+            >
     struct TestCase
       : BASE
       {
         void
-        performTestCases(TimeValue const& org, TimeValue const& c)
+        performTestSequence(TimeValue const& org, TimeValue const& c)
           {
             cout << "Test-Case. Target=" << showType() 
-                 <<" <--feed--- "        << showType() 
-                 <() 
+                 << endl;
             
+            // test subject
             Control controller;
             
             TAR target = TestTarget::build(org);
@@ -453,32 +470,35 @@ namespace test{
             
             
             // tail recursion: further test combinations....
-            BASE::performTestCases(org,c);
+            BASE::performTestSequence(org,c);
           }
       };
     
     struct IterationEnd
       {
-        void performTestCases(TimeValue const&, TimeValue const&) { }
+        void performTestSequence(TimeValue const&, TimeValue const&) { }
       };
     
   }//(End)Implementation Test-case matrix
   
   
   void
-  TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c)
+  TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& origVal, TimeValue const& change)
   {
-    typedef Types KindsOfTarget;
-    typedef Types KindsOfSource;
+    typedef Types                KindsOfTarget;  // time entities to receive value changes
+    typedef Types KindsOfSource;  // time entities to be used as change values
     typedef InstantiateChainedCombinations< KindsOfTarget
                                           , KindsOfSource
-                                          , TestCase
+                                          , TestCase                       // template to be instantiated for each type
                                           , IterationEnd > TestMatrix;
     
-    TestMatrix().performTestCases(o,c);
+    TestMatrix().performTestSequence(origVal, change);
   }
   
   
+  
+  
+  
   /** Register this test class... */
   LAUNCHER (TimeControl_test, "unit common");
   

From 971dea6f6aa5c85333fa70e2e7e4f42b840932c2 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 13 Jun 2011 20:07:30 +0200
Subject: [PATCH 030/108] PlayService: basic definition and link to facade

---
 src/common/interface-facade-link.hpp   | 123 +++++++++++++++++++++++++
 src/common/interfaceproxy.cpp          |   2 +-
 src/include/interfaceproxy.hpp         |  40 ++++++--
 src/include/play-facade.h              |  11 ++-
 src/lib/error.hpp                      |   1 +
 src/lib/exception.cpp                  |   1 +
 src/lib/handle.hpp                     |   2 +-
 src/proc/facade.hpp                    |   7 +-
 src/proc/play/dummy-player-service.hpp |   2 +-
 src/proc/play/play-service.cpp         |  40 +++++++-
 src/proc/play/play-service.hpp         |  11 ++-
 wiki/renderengine.html                 |  27 ++++--
 12 files changed, 238 insertions(+), 29 deletions(-)
 create mode 100644 src/common/interface-facade-link.hpp

diff --git a/src/common/interface-facade-link.hpp b/src/common/interface-facade-link.hpp
new file mode 100644
index 000000000..27c0826c9
--- /dev/null
+++ b/src/common/interface-facade-link.hpp
@@ -0,0 +1,123 @@
+/*
+  INTERFACE-FACADE-LINK  -  a switchable link from interface to service implementation
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+/** @file interface-facade-link.hpp
+ ** Opening, accessing and closing the service access through a facade interface.
+ ** Client code is assumed to access an application level service through an facade
+ ** interface, while the actual implementation object remains an opaque internal detail.
+ ** Moreover, services may come up and shut down, so the implementation might change
+ ** during the Lifecycle. The facility defined here in this header provides a basic
+ ** implementation for this access mechanism, but without any adaptation, binding
+ ** or plugin access layer. It works only under the assumption that both the 
+ ** interface and the actual service implementation coexist in the same
+ ** executable and are written in C++, so any invocation of an 
+ ** interface method boils down to a language-level call.
+ ** 
+ ** Usually, client code doesn't need to include this header. Clients are assumed
+ ** to use the facade interface of the service in question. This facade interface
+ ** contains a static member of type \c lumiera::facade::Accessor (where I is
+ ** the type of the facade interface). The Accessor baseclass is defined in
+ ** interfaceproxy.hpp and typically included through the facade header.
+ ** 
+ ** @note there is a way more elaborate implementation of the same mechanism
+ **       for use with the Lumiera Interface/Plugin system.
+ **
+ ** @see interfaceproxy.hpp description of the more general use case
+ ** @see PlayService example for the simple use case
+ */
+
+
+#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
+#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H
+
+
+#include "lib/error.hpp"
+#include "lib/test/test-helper.hpp"
+#include "include/interfaceproxy.hpp"
+#include "lib/symbol.hpp"
+
+#include 
+
+
+
+namespace lumiera {
+namespace facade {
+  
+  using lib::Literal;
+  
+  
+  /************************************************************************
+   * simple access-frontend to the implementation of a service (C++ only).
+   * Usually, an instance of Accessor is placed as static member right into
+   * the facade interface used to access the service. This implementation
+   * of the access mechanism handles the simple case that both the facade
+   * and the service implementation are written in C++ and calls happen
+   * within the main executable as direct language calls, without an
+   * binding layer and without involving the Interface/Plugin system.
+   * 
+   * Typically, the InterfaceFacadeLink becomes a member of the service
+   * implementation class and is directly tied into the constructor of
+   * the latter. Being a subclass of lumiera::facade::Accessor, it is
+   * allowed to "open" the facade access just by setting the static
+   * protected pointer Accessor::implProxy_
+   */
+  template
+  class InterfaceFacadeLink
+    : protected Accessor
+    , boost::noncopyable
+    {
+      Literal displayName_;
+      
+      void
+      __checkLifecycle ()
+        {
+          if (Accessor::implProxy_)
+            throw error::State("Attempt to open an already opened Facade interface."
+                              , error::LUMIERA_ERROR_LIFECYCLE);
+        }
+      
+    public:
+      InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0)
+        : displayName_(lib::test::showType(interfaceName_for_Log))
+        {
+          __checkLifecycle();
+          Accessor::implProxy_ = &serviceImpl;
+          INFO (interface, "interface %s opened", displayName_.c());
+        }
+      
+     ~InterfaceFacadeLink()
+        {
+          INFO (interface, "closing interface %s...", displayName_.c());
+          Accessor::implProxy_ = 0;
+        }
+    };
+  
+  
+  
+  /** storage for the static access pointer */
+  template
+  FA* Accessor::implProxy_;
+  
+  
+}} // namespace lumiera::facade
+
+#endif
diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp
index 611bdd97c..814f991b4 100644
--- a/src/common/interfaceproxy.cpp
+++ b/src/common/interfaceproxy.cpp
@@ -32,7 +32,7 @@ namespace lumiera{
 namespace facade {
   
   
-  LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible");
+  LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade is closed; service currently not accessible");
   
   
   /**
diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp
index 54084f8fd..0469da8f9 100644
--- a/src/include/interfaceproxy.hpp
+++ b/src/include/interfaceproxy.hpp
@@ -48,17 +48,18 @@
  ** Any sort of dependency management is outside the scope of the InstanceHandle (for the core
  ** services, it is handled by the dependency of subsystems, while the plugin loader cares
  ** for dependency issues regarding loadable modules, thereby building on the deployment
- ** descriptors.
+ ** descriptors.)
  ** 
  ** For the Layer separation interfaces, the process of loading and opening is abstracted as
  ** an InstanceHandle object. When creating such an InstanceHandle using the appropriate
  ** template and ctor parameters, in addition to the registration with the Interface/Plugin 
- ** system, the corresponding facade::Proxy factory is addressed and "opened" by creating
- ** the right proxy object instance. Similarly, when the InstanceHandle object goes out
- ** of scope, prior to detaching from the Interface/Proxy system, the corresponding 
- ** lumiera::facade::Accessor factory is "closed", which additionally means destroying
- ** the proxy object instance and switching any further access to throwing and exception. 
- **
+ ** system, the corresponding facade::Proxy factory is addressed and the interface instance
+ ** is "opened" by creating the appropriate proxy object instance. Similarly, when the
+ ** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy
+ ** system, the corresponding lumiera::facade::Accessor frontend is "closed", which
+ ** additionally means destroying the proxy object instance and switching any
+ ** further access to throwing and exception. 
+ ** 
  ** While client code just includes the interface header (including interfaceproxy.hpp
  ** in turn), there needs to be an actual implementation of each proxy object located in
  ** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into 
@@ -89,7 +90,29 @@ namespace facade {
 
 
   /*********************************************************************
+   * access-frontend to the implementation of a service.
+   * Usually, an instance of Accessor is placed as static member
+   * right into the facade interface used to access the service.
+   * This allows clients to get the current actual implementation
+   * of that service, just by invoking the function operator on
+   * that member, e.g. \c lumiera::Play::facade()
    * 
+   * The reason for this rather indirect access technique is Lifecycle:
+   * Service implementations may come up and go down; moreover, a service
+   * might be implemented through a plugin component and thus the actual
+   * invocation needs to be passed through a binding layer. In this case,
+   * clients rather access a proxy object, which then passes on any call
+   * through that binding layer to the actual implementation located
+   * "somewhere".
+   * 
+   * @note the pointer to the actual implementation is a static variable.
+   *       This has two consequences. For one, we're dealing with kind of
+   *       singleton service here. And, secondly, the implementation or
+   *       proxy accessor can inherit from Accessor where FA is the
+   *       facade interface. Being a subclass, allows the implementation
+   *       to set that pointer when the service comes up, and to clear
+   *       it when the service goes down and the access needs to
+   *       be closed.
    */
   template
   class Accessor
@@ -105,7 +128,8 @@ namespace facade {
           if (implProxy_)
             return *implProxy_;
           else
-            throw error::State("Facade interface currently closed.");
+            throw error::State("Facade interface currently closed."
+                              , LUMIERA_ERROR_FACADE_LIFECYCLE);
         }
     };
   
diff --git a/src/include/play-facade.h b/src/include/play-facade.h
index bc8fa26e9..84865444c 100644
--- a/src/include/play-facade.h
+++ b/src/include/play-facade.h
@@ -115,15 +115,16 @@ namespace lumiera {
           };
         
         
-        typedef lib::IterSource ModelPorts;
-        typedef lib::IterSource Pipes;
+        typedef lib::IterSource&    ModelPorts;
+        typedef lib::IterSource& Pipes;
         typedef proc::play::POutputManager Output;
         typedef mobject::session::PClipMO Clip;
-        typedef mobject::PTrack Track;
+        typedef mobject::PTrack  Track;
         typedef asset::PTimeline Timeline;
         typedef asset::PViewer Viewer;
-          
-        /** create a new playback process outputting to the given viewer/display */
+        
+        /** core operation: create a new playback process
+         *  outputting to the given viewer/display  */
         virtual Controller connect(ModelPorts, Output)      =0;
         
         
diff --git a/src/lib/error.hpp b/src/lib/error.hpp
index e96942ee6..5e2427e0a 100644
--- a/src/lib/error.hpp
+++ b/src/lib/error.hpp
@@ -122,6 +122,7 @@ namespace lumiera {
     LUMIERA_ERROR_DECLARE (ASSERTION);    ///< assertion failure
     
     /* generic error situations */
+    LUMIERA_ERROR_DECLARE (LIFECYCLE);    ///< Lifecycle assumptions violated
     LUMIERA_ERROR_DECLARE (WRONG_TYPE);   ///< runtime type mismatch
     LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached
     LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value
diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp
index 82e228687..8c524e5d4 100644
--- a/src/lib/exception.cpp
+++ b/src/lib/exception.cpp
@@ -79,6 +79,7 @@ namespace lumiera {
     LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure");
     
     /* some further generic error situations */
+    LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated");
     LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch");
     LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached");
     LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value");
diff --git a/src/lib/handle.hpp b/src/lib/handle.hpp
index a1a01a919..685a6a4c6 100644
--- a/src/lib/handle.hpp
+++ b/src/lib/handle.hpp
@@ -60,7 +60,7 @@ namespace lib {
    * Generic opaque reference counting handle, for accessing a service
    * and managing its lifecycle. Usually such a handle is created by
    * an service interface and \link #activate activated \endlink by
-   * setting up the link to some internal implementation object.
+   * setting up the link to a suitable hidden implementation object.
    * This setup can only be done by a friend or derived class,       //////////////////////////TODO: that was the intention. Why didn't this work out as expected?
    * while client code is free to copy and store handle objects.
    * Finally, any handle can be closed, thereby decrementing
diff --git a/src/proc/facade.hpp b/src/proc/facade.hpp
index e32227729..18ef46454 100644
--- a/src/proc/facade.hpp
+++ b/src/proc/facade.hpp
@@ -57,14 +57,15 @@ namespace proc {
       
       
       /** provide a descriptor for lumiera::AppState,
-       *  wired accordingly to allow main to load and
-       *  save an existing session. */
+       *  wired accordingly to allow main to bring up
+       *  a editing session, possibly by loading an
+       *  existing session from storage. */
       static lumiera::Subsys& getSessionDescriptor();
       
       
       /** provide a descriptor for lumiera::AppState,
        *  wired accordingly to allow main to bring up
-       *  the render, playback coordination and 
+       *  the render / playback coordination and 
        *  output management subsystem. */
       static lumiera::Subsys& getPlayOutDescriptor();
       
diff --git a/src/proc/play/dummy-player-service.hpp b/src/proc/play/dummy-player-service.hpp
index 5cf8438ac..d5f942b3e 100644
--- a/src/proc/play/dummy-player-service.hpp
+++ b/src/proc/play/dummy-player-service.hpp
@@ -72,7 +72,7 @@ namespace proc {
     /********************************************************************
      * Actual implementation of a single (dummy) playback process.
      * The DummyPlayerService (see below) maintains a collection of such
-     * actively running playback processes, while the client code gets 
+     * actively running playback processes, while the client code gets
      * DummyPlayer::Process handles to track any ongoing use. Users of
      * the plain C interface get a direct bare pointer to the respective
      * ProcessImpl instance and have to manage the lifecycle manually.
diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp
index 7ee13c480..52d0ac789 100644
--- a/src/proc/play/play-service.cpp
+++ b/src/proc/play/play-service.cpp
@@ -32,6 +32,16 @@
 
 
 
+namespace lumiera {
+  
+  
+  Play::~Play() { } // emit VTables here...
+  
+  
+  
+}//(End) namespace lumiera
+
+
 namespace proc {
 namespace play {
 
@@ -55,9 +65,37 @@ namespace play {
   } // (End) hidden service impl details
   
   
+  using lumiera::Play;
   
   
-  /** */
+  /** bring up the global render- and playback service.
+   *  This service allows to create individual PlayProcess instances
+   *  to \em perform a timeline or similar model object, creating
+   *  rendered data for output. Client code is assumed to access
+   *  this service through the lumiera::Play facade. 
+   */
+  PlayService::PlayService()   /////TODO Subsys::SigTerm terminationHandle);
+    : facadeAccess_(*this, "Player")
+    { }
+  
+  
+  
+  /**
+   * @note this is the core operation of the play and render service
+   * 
+   * Invoking this function investigates the given exit nodes of the
+   * render nodes network and retrieves actual output destinations
+   * through the given OutputManager. The goal is to configure an 
+   * PlayProcess, based on the renderengine and the collection of
+   * OutputSlot instances retrieved for each of the given exit nodes.
+   * Running this PlayProcess will activate the render engine to deliver
+   * calculated media data to the outputs. 
+   */
+  Play::Controller
+  PlayService::connect(ModelPorts dataGenerators, Output outputDestinations)
+  {
+    UNIMPLEMENTED ("build a PlayProcess");
+  }
 
 
 }} // namespace proc::play
diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp
index 03780f134..29db10187 100644
--- a/src/proc/play/play-service.hpp
+++ b/src/proc/play/play-service.hpp
@@ -34,6 +34,7 @@
 
 #include "lib/error.hpp"
 #include "include/play-facade.h"
+#include "common/interface-facade-link.hpp"
 
 #include 
 //#include 
@@ -44,6 +45,7 @@ namespace proc {
 namespace play {
 
   using std::string;
+  using lumiera::facade::InterfaceFacadeLink;
 //using lumiera::Subsys;
 //using lumiera::Display;
 //using lumiera::DummyPlayer;
@@ -58,8 +60,15 @@ namespace play {
    * Interface: Player subsystem.
    */
   class PlayService
-    : boost::noncopyable
+    : public lumiera::Play
+    , boost::noncopyable
     {
+      InterfaceFacadeLink facadeAccess_;
+      
+      
+      /** Implementation: build a PlayProcess */
+      virtual Controller connect(ModelPorts, Output);
+      
       
     public:
       PlayService();   /////TODO Subsys::SigTerm terminationHandle);
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 1c1558952..d5ee8e508 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1632,7 +1632,7 @@ The main tool used to implement this separation is the [[Builder Pattern|http://
 Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
 
-
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
 
 !Starting point
@@ -1658,7 +1658,7 @@ Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger
 * then, of course, the PlayService interface needs to be created
 * as a temporary soltion, a ''~DummyPlayConnection'' will be created hard-wired, within the ~DummyPlayer service. This dumy container is a test setup, explicitly creating a OutputManager implementation, a GeneratorMO, a GeneratorNode (thus omitting the not-yet-implemented Builder) and the corresponding ModelPort. This way, the ~DummyPlayer service can be changed to use the real PlayService for creating the dummy generated output data. It will then pass back the resulting PlayController to the existing GUI setup.
 
-The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach tries to circumvent the problems with occasional crashes on shutdown, due to dispatching frame output signals in the event thread while already in shutdown.
+The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach we hope to circumvent problems with occasional crashes on closing the application, due to dispatching frame output signals in the event thread while shutdown is already in progress.
 
 
@@ -3219,14 +3219,15 @@ While actually data frames are //pulled,// on a conceptual level data is assumed As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
 * thus, any designation is a [[Pipe]]-ID.
 * consequently, it is not obviously clear if such an designation is the final exit point
-* please note the [[Engine interface proposal|http://lumiera.org/Lumiera/DesignProcess/EngineInterfaceOverview.html]]
+* please note the [[Engine interface proposal|http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html]]
 * this introduces the notion of a ModelPort: //a point in the (high level) model where output can be produced//
+* thus obviously we need an OutputManager element to track the association of OutputDesignation to OutputSlot
 
 Do we get a single [[Fixture]] &mdash; guess yes
 
@@ -3242,10 +3243,20 @@ We should note that in both cases this mapping operation is controlled and drive
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
-Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
+Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
 &rarr; see also the PlayerDummy
+
+!the global output manager
+While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
 
+
+
The term &raquo;''Output Manager''&laquo; might deonte two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global OutputManager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+&rarr; see [[output management overview|OutputManagement]]
+&rarr; see OutputSlot
+&rarr; see ViewerPlayConnection
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
@@ -7285,14 +7296,14 @@ These Viewer (or Monitor) elements play an enabling role for any output generati
 
 When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
-
+
for showing output, three entities are involved
 * the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
-* by connecting to a viewer component, an actual output target is established
+* by connecting to a viewer component (&rarr; ViewConnection), an actual output target is established
 * the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
 
 !the viewer connection
-A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
+A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputSlot registered with the OutputManager &mdash; again an output mapping step.
 
From 1b0cb56dccd88789084c0e1995edd303ecf46d02 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 14 Jun 2011 05:41:02 +0200 Subject: [PATCH 031/108] implement PlayOut subsystem and draft OutputDirector --- src/backend/thread-wrapper.hpp | 2 +- src/common/guifacade.cpp | 2 +- src/common/subsys.hpp | 11 +- src/common/subsystem-runner.hpp | 2 +- src/lib/result.hpp | 9 +- src/proc/control/handling-pattern.hpp | 1 + src/proc/facade.cpp | 17 ++- .../mobject/session/lifecycle-advisor.hpp | 4 +- .../mobject/session/sess-manager-impl.cpp | 14 +- .../mobject/session/sess-manager-impl.hpp | 2 +- src/proc/play/output-director.cpp | 135 ++++++++++++++++++ src/proc/play/output-director.hpp | 100 +++++++++++++ src/proc/play/output-manager.cpp | 44 ------ 13 files changed, 270 insertions(+), 73 deletions(-) create mode 100644 src/proc/play/output-director.cpp create mode 100644 src/proc/play/output-director.hpp delete mode 100644 src/proc/play/output-manager.cpp diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 60413b037..699bfd11d 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -200,7 +200,7 @@ namespace backend { /** Synchronisation barrier. In the function executing in this thread - * needs to be a corresponding Thread::sync() call. Blocking until + * needs to be a corresponding Thread::syncPoint() call. Blocking until * both the caller and the thread have reached the barrier. */ void diff --git a/src/common/guifacade.cpp b/src/common/guifacade.cpp index aff7febb8..5e1c4599d 100644 --- a/src/common/guifacade.cpp +++ b/src/common/guifacade.cpp @@ -156,7 +156,7 @@ namespace gui { if (facade) { WARN (guifacade, "GUI subsystem terminates, but GuiFacade isn't properly closed. " - "Closing it forcedly; this indicates broken startup logic and should be fixed."); + "Closing it forcedly; this indicates broken startup logic and should be fixed."); try { facade.reset (0); } catch(...) { WARN_IF (lumiera_error_peek(), guifacade, "Ignoring error: %s", lumiera_error()); } lumiera_error(); // clear any remaining error state... diff --git a/src/common/subsys.hpp b/src/common/subsys.hpp index 363b17e51..26b080861 100644 --- a/src/common/subsys.hpp +++ b/src/common/subsys.hpp @@ -104,9 +104,12 @@ namespace lumiera { /** initiate termination of this subsystem. - * may be called repeatedly any time... - * @warning must not block nor throw. */ - virtual void triggerShutdown () throw() =0; + * This trigger may be called repeatedly any time... + * When the subsystem actually has terminated, + * the SigTerm passed to #start must be invoked. + * @note called within a locked context (barrier) + * @warning must not block nor throw. */ + virtual void triggerShutdown () throw() =0; const std::vector @@ -114,7 +117,7 @@ namespace lumiera { private: - /** weather this subsystem is actually operational. + /** whether this subsystem is actually operational. * When returning \c false here, the application may * terminate at any point without further notice * Note further, that a subsystem must not be in diff --git a/src/common/subsystem-runner.hpp b/src/common/subsystem-runner.hpp index 89a90245d..6cbd3495a 100644 --- a/src/common/subsystem-runner.hpp +++ b/src/common/subsystem-runner.hpp @@ -173,7 +173,7 @@ namespace lumiera { if (!and_all (susy->getPrerequisites(), isRunning() )) { susy->triggerShutdown(); - throw error::Logic("Unable to start all prerequisites of Subsystem "+string(*susy)); + throw error::State("Unable to start all prerequisites of Subsystem "+string(*susy)); } } void diff --git a/src/lib/result.hpp b/src/lib/result.hpp index ae4f7b187..daf153a49 100644 --- a/src/lib/result.hpp +++ b/src/lib/result.hpp @@ -58,11 +58,10 @@ namespace lib { /** - * Result value and status of some operation. - * It can be created for passing a result produced - * by the operation, or the failure to do so. The value - * can be retrieved by implicit or explicit conversion. - * @throws on any attempt to access the value in case of failure + * Optional Result value or status of some operation. + * It can be created for passing a result produced by the operation, or the + * failure to do so. The value can be retrieved by implicit or explicit conversion. + * @throw error::State on any attempt to access the value in case of failure * @warning this class has a lot of implicit conversions; * care should be taken when defining functions * to take Result instances as parameter.... diff --git a/src/proc/control/handling-pattern.hpp b/src/proc/control/handling-pattern.hpp index f414e25a1..6cc947211 100644 --- a/src/proc/control/handling-pattern.hpp +++ b/src/proc/control/handling-pattern.hpp @@ -67,6 +67,7 @@ namespace control { * It is returned when invoking a HandlingPattern * and can be used to check for success and/or re-throw * any Exception encountered during the command execution. + * @todo couldn't that be replaced by a lib::Result instance?? */ class ExecResult : public lib::BoolCheckable diff --git a/src/proc/facade.cpp b/src/proc/facade.cpp index 6ca6a84b6..de6d562ee 100644 --- a/src/proc/facade.cpp +++ b/src/proc/facade.cpp @@ -23,6 +23,7 @@ #include "proc/facade.hpp" #include "lib/singleton.hpp" +#include "proc/play/output-director.hpp" #include @@ -77,7 +78,7 @@ namespace proc { bool shouldStart (lumiera::Option&) { - TODO ("determine, if an existing Session schould be loaded"); + TODO ("determine, if an existing Session should be loaded"); return false; } @@ -127,22 +128,24 @@ namespace proc { bool start (lumiera::Option&, Subsys::SigTerm termination) { - UNIMPLEMENTED ("bring up and configure the output connections and the player"); - return false; + this->completedSignal_ = termination; + return play::OutputDirector::instance().connectUp(); } + SigTerm completedSignal_; + + void triggerShutdown () throw() { - UNIMPLEMENTED ("initiate playback/render halt and disconnect output"); + play::OutputDirector::instance().triggerDisconnect (completedSignal_); } + bool checkRunningState () throw() { - //Lock guard (*this); - TODO ("implement detecting running state"); - return false; + return play::OutputDirector::instance().isOperational(); } }; diff --git a/src/proc/mobject/session/lifecycle-advisor.hpp b/src/proc/mobject/session/lifecycle-advisor.hpp index a3c286af8..0e8364fae 100644 --- a/src/proc/mobject/session/lifecycle-advisor.hpp +++ b/src/proc/mobject/session/lifecycle-advisor.hpp @@ -31,7 +31,7 @@ ** here; any implementation is delegated to the relevant session facilities. ** ** The idea of a LifecycleAdvisor is inspired by GUI frameworks, especially - ** Spring RichClient. Typically, such frameworks provides a means for flexible + ** Spring RichClient. Typically, such frameworks provide a means for flexible ** configuration of the application lifecycle. Configurability isn't the primary ** goal here, as there is only one Lumiera application and the session lifecycle ** can be considered fixed, with the exception of some extension points, which are @@ -64,7 +64,7 @@ namespace session { /** - * Skeleton operations conducting the session lifecycle sequences. + * Skeleton of operations conducting the session lifecycle sequences. * Any details of the operations are delegated to the current session * and associated services. * @warning this object is assumed to be used as a single instance diff --git a/src/proc/mobject/session/sess-manager-impl.cpp b/src/proc/mobject/session/sess-manager-impl.cpp index 972a711be..9ce3c61ec 100644 --- a/src/proc/mobject/session/sess-manager-impl.cpp +++ b/src/proc/mobject/session/sess-manager-impl.cpp @@ -67,7 +67,7 @@ namespace session { SessionImplAPI* SessManagerImpl::operator-> () throw() { - if (!pImpl_) + if (!pSess_) try { // create empty default configured session this->reset(); @@ -80,7 +80,7 @@ namespace session { } - return pImpl_.get(); + return pSess_.get(); } @@ -201,8 +201,8 @@ namespace session { * \link #operator-> access \endlink to the session object. */ SessManagerImpl::SessManagerImpl () throw() - : pImpl_ (0) - , lifecycle_(new SessionLifecycleDetails(pImpl_)) + : pSess_ (0) + , lifecycle_(new SessionLifecycleDetails(pSess_)) { Session::initFlag = true; //////////////////////////////////////// TICKET #518 instead of this hack, implement basic-init of the session manager for real } @@ -219,7 +219,7 @@ namespace session { bool SessManagerImpl::isUp () { - return bool(pImpl_); ///////////////////// TICKET #702 possible race, because this gets true way before the interface is up + return bool(pSess_); ///////////////////// TICKET #702 possible race, because this gets true way before the interface is up } /** @note no transactional behaviour. may succeed partially. @@ -229,7 +229,7 @@ namespace session { SessManagerImpl::clear () { Lock sync(this); - pImpl_->clear(); + pSess_->clear(); } @@ -242,7 +242,7 @@ namespace session { { Lock sync(this); lifecycle_->shutDown(); - pImpl_.reset(); + pSess_.reset(); } diff --git a/src/proc/mobject/session/sess-manager-impl.hpp b/src/proc/mobject/session/sess-manager-impl.hpp index 14af631c6..403f835c8 100644 --- a/src/proc/mobject/session/sess-manager-impl.hpp +++ b/src/proc/mobject/session/sess-manager-impl.hpp @@ -42,7 +42,7 @@ namespace session { : public SessManager , public lib::Sync<> { - scoped_ptr pImpl_; + scoped_ptr pSess_; scoped_ptr lifecycle_; SessManagerImpl() throw(); diff --git a/src/proc/play/output-director.cpp b/src/proc/play/output-director.cpp new file mode 100644 index 000000000..0bcbebc26 --- /dev/null +++ b/src/proc/play/output-director.cpp @@ -0,0 +1,135 @@ +/* + OutputDirector - handling all the real external output connections + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "proc/play/play-service.hpp" +#include "proc/play/output-manager.hpp" +#include "proc/play/output-director.hpp" +#include "backend/thread-wrapper.hpp" + +using backend::Thread; + + +namespace proc { +namespace play { + + + + namespace { // hidden local details of the service implementation.... + + } // (End) hidden service impl details + + + + /** storage for the single application wide OutputDirector instance */ + lib::Singleton OutputDirector::instance; + + + /** bring up the framework for handling input/output connections. + * Creating this object happens on first access and shouldn't be + * confused with actually booting up / shutting down this subsystem. + * Rather, the purpose of the OutputDirector is actively to conduct + * the Lifecycle of booting, connecting, operating, disconnecting. + */ + OutputDirector::OutputDirector() + { } + + OutputDirector::~OutputDirector() { } + + + + /** connect and bring up the external input/output connections, + * handlers and interface services and the render/playback service. + * @return true if the output subsystem can be considered operational + * + * @todo WIP-WIP-WIP 6/2011 + */ + bool + OutputDirector::connectUp() + { + Lock sync(this); + + player_.reset (new PlayService); + return this->isOperational(); + } + + + bool + OutputDirector::isOperational() const + { + return bool(player_); ////////////TODO more to check here.... + } + + + + /** initiate shutdown of all ongoing render/playback processes + * and closing of all external input/output interfaces. + * Works as an asynchronous operation; the given callback signal + * will be invoked when the shutdown is complete. + * @note starting a new thread, which might fail. + * When this happens, the raised exception will cause + * immediate unconditional termination of the application. + */ + void + OutputDirector::triggerDisconnect (SigTerm completedSignal) throw() + { + Thread ("Output shutdown supervisor", bind (&OutputDirector::bringDown, this, completedSignal)); + } + + + /** @internal actually bring down any calculation processes + * and finally disconnect any external input/output interfaces. + * This shutdown and cleanup operation is executed in a separate + * "Output shutdown supervisor" thread and has the liability to + * bring down the relevant facilities within a certain timespan. + * When done, the last operation within this thread will be to + * invoke the callback signal given as parameter. + * @note locks the OutputDirector + */ + void + OutputDirector::bringDown (SigTerm completedSignal) + { + Lock sync(this); + string problemLog; + try + { + TODO ("actually bring down the output generation"); + player_.reset(0); + + completedSignal(0); + } + + catch (lumiera::Error& problem) + { + problemLog = problem.what(); + lumiera_error(); // reset error state + completedSignal (&problemLog); + } + catch (...) + { + problemLog = "Unknown error while disconnecting output. " + "Lumiera error flag is = "+string(lumiera_error()); + completedSignal (&problemLog); + } + } + +}} // namespace proc::play diff --git a/src/proc/play/output-director.hpp b/src/proc/play/output-director.hpp new file mode 100644 index 000000000..2783b097c --- /dev/null +++ b/src/proc/play/output-director.hpp @@ -0,0 +1,100 @@ +/* + OUTPUT-DIRECTOR.hpp - handling all the real external output connections + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + +/** @file output-director.hpp + ** A global service to coordinate and handle all external output activities. + ** The OutputDirector is an application internal singleton service for + ** coordinating and controlling all actual input/output- and rendering + ** capabilities, exposing distinct lifecycle functions to connect, bring + ** up and shut down what can be considered the "Player/Output" subsystem. + ** + ** @see output-manager-test.cpp ////TODO + */ + + +#ifndef PROC_PLAY_OUTPUT_DIRECTOR_H +#define PROC_PLAY_OUTPUT_DIRECTOR_H + + +#include "lib/error.hpp" +#include "lib/singleton.hpp" +#include "proc/play/output-manager.hpp" +#include "common/subsys.hpp" +#include "lib/sync.hpp" + +#include +//#include +//#include +//#include +#include + + +namespace proc { +namespace play { + +//using std::string; +//using std::vector; +//using std::tr1::shared_ptr; + using boost::scoped_ptr; + + + class PlayService; + + +//typedef lib::ScopedPtrVect DisplayerTab; + + + + /****************************************************** + * Management of external Output connections. + * + * @todo write Type comment + */ + class OutputDirector + : boost::noncopyable + , public lib::Sync<> + { + typedef lumiera::Subsys::SigTerm SigTerm; + + scoped_ptr player_; + + public: + static lib::Singleton instance; + + bool connectUp() ; + void triggerDisconnect(SigTerm) throw(); + + bool isOperational() const; + + private: + OutputDirector() ; + ~OutputDirector() ; + friend class lib::singleton::StaticCreate; + + + void bringDown (SigTerm completedSignal); + }; + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-manager.cpp b/src/proc/play/output-manager.cpp deleted file mode 100644 index 6ffca994f..000000000 --- a/src/proc/play/output-manager.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - OutputManager - handling all the real external output connections - - Copyright (C) Lumiera.org - 2011, Hermann Vosseler - - 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 "proc/play/output-manager.hpp" - - - -namespace proc { -namespace play { - - - - namespace { // hidden local details of the service implementation.... - - } // (End) hidden service impl details - - - - - OutputSlot::~OutputSlot() { } - - - -}} // namespace proc::play From a37947641497b06248332e12d314af7f25a58d90 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 15 Jun 2011 03:51:06 +0200 Subject: [PATCH 032/108] create outline of PlayService, add stubs for Play::Controller --- src/proc/play/play-controller.cpp | 219 ++++++++++++++++++++++++++++++ src/proc/play/play-controller.hpp | 47 +++---- src/proc/play/play-process.hpp | 22 ++- src/proc/play/play-service.cpp | 79 ++++++++++- src/proc/play/play-service.hpp | 21 ++- wiki/renderengine.html | 4 +- 6 files changed, 356 insertions(+), 36 deletions(-) create mode 100644 src/proc/play/play-controller.cpp diff --git a/src/proc/play/play-controller.cpp b/src/proc/play/play-controller.cpp new file mode 100644 index 000000000..f8a3de185 --- /dev/null +++ b/src/proc/play/play-controller.cpp @@ -0,0 +1,219 @@ +/* + PlayProcess - frontend to control an play process + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +* *****************************************************/ + + +/** @file play-controller.cpp + ** Implementation of the control- / management frontend(s) exposed + ** to clients for talking to an ongoing PlayProcess. This includes + ** the implementation of lumiera::Play::Controller, exposed directly + ** from the Player facade interface. Besides that primary control + ** interface, this translation unit also contains the implementation + ** for several extended play controllers targeted at special usage + ** situations + ** + ** @see lumiera::DummyPlayer + ** @see gui::PlaybackController usage example + */ + + +#include "proc/play/play-controller.hpp" +#include "proc/play/play-process.hpp" +#include "lib/time/timevalue.hpp" + +//#include +//#include +//#include +//#include + +namespace lumiera { + + using lib::time::Time; + using lib::time::Duration; + using lib::time::TimeSpan; + + + + /* ==== Play::Controller implementation ==== */ + + /** toggle the play or pause state + * of the corresponding PlayProcess */ + void + Play::Controller::play(bool) + { + UNIMPLEMENTED ("toggle play/pause state"); + } + + + /** switch into scrubbing playback + * + */ + void + Play::Controller::scrub(bool) + { + UNIMPLEMENTED ("scrubbing playback"); + } + + + void + Play::Controller::adjustSpeed(double) ///< playback speed control + { + UNIMPLEMENTED ("adjust actual playback speed"); + } + + + /** + * + */ + void + Play::Controller::go(Time) ///< skip to the given point in time + { + UNIMPLEMENTED ("jump to given time"); + } + + + /** + * + */ + void + Play::Controller::controlPlayhead (time::Control
-
-
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
+
+
Within Lumiera, &raquo;Player&laquo; is the name for a [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
 Yet it adresses some central concerns:
 

From ce6a917b598d59c3dadfb19b97bad7ef1690700c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 17 Jun 2011 04:07:07 +0200
Subject: [PATCH 033/108] first draft spec for the OutputSlot

---
 src/proc/mobject/output-designation.hpp |  6 ++-
 wiki/renderengine.html                  | 57 ++++++++++++++++++-------
 2 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp
index fd571e56a..e5d32813d 100644
--- a/src/proc/mobject/output-designation.hpp
+++ b/src/proc/mobject/output-designation.hpp
@@ -34,6 +34,8 @@ extern "C" {
 
 namespace mobject {
   
+  namespace mp = lumiera::typelist;
+  
   class MObject;
   
   template
@@ -99,8 +101,8 @@ namespace mobject {
       
     private:
       enum {
-        SPEC_SIZ = lumiera::typelist::maxSize<
-                       lumiera::typelist::Types::List>::value 
+        SPEC_SIZ = mp::maxSize<
+                       mp::Types< PID, lumiera_uid, uint>::List>::value 
       };
       typedef lib::OpaqueHolder SpecBuff;
       
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 3f9043064..f86c6087b 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3159,7 +3159,7 @@ The operation point is provided by the current BuilderMould and used by the [[pr
 This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
 
-
+
An ever recurring problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 Wiring the flexible interconnections between the [[pipes|Pipe]] should take into account both the StreamType and the specific usage context ([[scope|PlacementScope]]) -- and the challenge is to avoid hard-linking of connections and tangling with the specifics of the target to be addressed and connected. This page, started __6/2010__ by collecting observations to work out the relations, arrives at defining a //key abstraction// of output management.
 
@@ -3183,21 +3183,21 @@ Wiring the flexible interconnections between the [[pipes|Pipe]] should take into
 * //indirect// means to derive the destination transitively (looking at the destination's output designation and so on)
 * //relative// in this context means that we refer to "the N^^th^^ of this kind" (e.g. the second video out)
 * we need to attach some metadata with an output; especially we need an associated StreamType
-* output designation is structured into several //levels://
+* the referral to an output designation can be observed on and is structured into several //levels://
 ** within the body of the model, mostly we address output destinations relatively
 ** at some point, we'll address a //subgroup// within the global pipes, which acts like a summation sink
-** there might be //master pipes//
-** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referred explicitly
+** there might be //master pipe(s),// collecting the output of various subgroups
+** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referenced explicitly
 !!!relation to Pipes
 in almost every case mentioned above, the output designation is identical with the starting point of a [[Pipe]]. This might be a global pipe or a ClipSourcePort. Thus it sounds reasonable to use pipe-~IDs directly as output designation. Pipes, as an accountable entity (=asset) just //leap into existence by being referred.// On the other hand, the //actual//&nbsp; pipe is a semantic concept, a specific structural arrangement of objects. Basically it means that some object acts as attachment point and thereby //claims//&nbsp; to be the entrance side of a pipe, while other processor objects chain up in sequence.
 !!!system outputs
-System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. But the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
+System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. Yet the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
 !!!consequences of mentioning an output designation
 The immediate consequence is that a [[Pipe]] with the given ID exists as an accountable entity. Only if &mdash; additionally &mdash; a suitable object within the model //claims to root this pipe,//  a connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence then is for the mentioned output designation to become a possible candidate for a ModelPort, an exit node of the built render nodes network. By default, only those designations without further outgoing connections actually become active model ports (but an existing and wired pipe exit node can be promoted to a model port explicitly).
 &rarr; OutputManagement
 
 !!Challenge: mapping of output designations
-An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that BindingMO also acts as [[Timeline]] implementation &mdash; indeed the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same fashion as any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified.
+An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that, within another context, BindingMO also acts as [[Timeline]] implementation &mdash; indeed even the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same manner like any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified.
 &rarr; see OutputMapping regarding details and implementation of this mapping mechanism
 
 
@@ -3205,7 +3205,7 @@ An entire sequence can be embedded within another sequence as a VirtualClip. Whi
 
 !Output designation definition
 OutputDesignation is a handle, denoting a target [[Pipe]] to connect.
-It exposes a function to resolve to a Pipe, and to retrieve the StreamType of that resolved output. It can be ''defined'' either explicitly by ~Pipe-ID, or by an indirect or relative specification. The later cases are resolved on demand only (which may be later and might change the meaning depending on the context). It's done this way intentionally to gain flexibility and avoid hard wiring (=explicit ~Pipe-ID)
+It exposes a function to resolve to a Pipe, and to retrieve the StreamType of that resolved output. It can be ''defined'' either explicitly by ~Pipe-ID, or by an indirect or relative specification. The later cases are resolved on demand only (which may be later and might even change the meaning, depending on the context). It's done this way intentionally to gain flexibility and avoid hard wiring (=explicit ~Pipe-ID)
 
 !!!Implementation notes
 Thus the output designation needs to be a copyable value object, but opaque beyond that. Mandated by the various ways to specify an output designation, a hidden state arises regarding the partial resolution. The implementation solves that dilemma by relying on the [[State|http://en.wikipedia.org/wiki/State_pattern]] pattern in combination with an opaque in-place buffer.
@@ -3216,10 +3216,10 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 * first of all, we need to know //what to route// -- kind of the traits of the data. This is given by the //current pipe.//
 * then we'll need to determine an output designation //suitable for this data.// This is given by a "Plug" (WiringRequest) in the placement, and may be derived.
 * finally, this output designation will be //resolved// -- at least partially, resulting in a target pipe to be used for the wiring
-As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
+As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3236,9 +3236,9 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and &mdash; at the same time &mdash; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
+[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
 
-We should note that in both cases this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
+We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
 !Connection to external outputs
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
@@ -3250,14 +3250,14 @@ Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputMa
 While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
 
-
-
The term &raquo;''Output Manager''&laquo; might deonte two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global OutputManager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
 
 &rarr; see [[output management overview|OutputManagement]]
 &rarr; see OutputSlot
 &rarr; see ViewerPlayConnection
-
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
@@ -3284,9 +3284,36 @@ All these mapping steps are listed here, because they exhibit a common pattern.
 * there is an //unconnected//&nbsp; state.
 
 !Implementation notes
-Thus the mapping is a copyable value object, based on a associative array. It may be attached to a model object and persisted alongside. The mapping is assumed to run a defaults query when necessary. To allow for that, it should be configured with a query template (string). Frequently, special //default pipe// markers will be used at places, where no distinct pipe-ID is explicitly specified. Besides that, invocations might supply additional predicates (e.g. {{{ord(2)}}} to denote "the second stream of this kind") to hint the defaults resolution. Moreover, the mapping needs a way to retrieve the set of possible results, allowing to filter the results of the rules based default. Mappings might be defined explicitly. Instead of storing a //bottom value,// an {{{isDefined()}}} predicate might be preferable.
+Thus the mapping is a copyable value object, based on a associative array. It may be attached to a model object and persisted alongside. The mapping is assumed to run a defaults query when necessary. To allow for that, it should be configured with a query template (string). Frequently, special //default pipe// markers will be used at places where no distinct pipe-ID is specified explicitly. Besides that, invocations might supply additional predicates (e.g. {{{ord(2)}}} to point at "the second stream of this kind") thereby hinting the defaults resolution. Moreover, the mapping needs a way to retrieve the set of possible results, allowing to filter the results of the rules based default. Mappings might be defined explicitly. Instead of storing a //bottom value,// an {{{isDefined()}}} predicate might be preferable.
 
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
+
+
+
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
+This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
+
+!Properties of an output slot
+Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). In some cases, this explicit registration may be extened to a //factory for additional output slots// -- to be used on demand. An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
+
+In order to be usable as //output sink,// an output slot needs to be allocated and locked. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
+
+Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The size and other characteristics of the data frames is assumed to be suitable. Typically this won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
+
+!!!data exchange models
+Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. There are two different models how this data hand-over might be performed. On allocation of a slot, the client has to commit to one of them, allowing the output slot to adapt accordingly
+;buffer handover model
+:the client owns the data buffer and cares for allocation and de-allocation. The {{{emit()}}}-call just propagates a pointer to the buffer holding the data ready for output. The output slot implementation in turn has the liability to copy or otherwise use this data within a given time limit.
+;shared buffer model
+:here the output mechanism owns the buffer. Within a certain time window prior to the expected time of the {{{emit()}}}-call, the client may obtain this buffer (pointer) to fill in the data. The slot implementation won't touch this buffer until the {{{emit()}}} handover, which in this case just provides the time and states that the client is done with that buffer. If the data emitting handshake doesn't happen at all, it counts as late and superseded by the next handshake.
+
+!!!timing expectations
+Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, latency means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run within certain limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+
+{{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
+
 
From 4ece1352572902de34e548bf3f3022a60583c60e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 18 Jun 2011 03:34:27 +0200 Subject: [PATCH 034/108] cont.. drafting OutputSlot --- src/proc/play/output-manager.hpp | 18 +----- src/proc/play/output-slot.cpp | 48 +++++++++++++++ src/proc/play/output-slot.hpp | 101 +++++++++++++++++++++++++++++++ wiki/renderengine.html | 18 +++--- 4 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/proc/play/output-slot.cpp create mode 100644 src/proc/play/output-slot.hpp diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index df3b4e216..a6fd3b91a 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -32,6 +32,7 @@ #include "lib/error.hpp" +#include "proc/play/output-slot.hpp" #include //#include @@ -50,23 +51,6 @@ namespace play { - /******************************************************************** - * Interface: Generic output sink. - * - * @todo write type comment - */ - class OutputSlot - : boost::noncopyable - { - public: - virtual ~OutputSlot(); - - private: - - }; - -//typedef lib::ScopedPtrVect DisplayerTab; - /****************************************************** diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp new file mode 100644 index 000000000..ff2798ee0 --- /dev/null +++ b/src/proc/play/output-slot.cpp @@ -0,0 +1,48 @@ +/* + OutputSlot - capability to transfer data to a physical output + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "proc/play/output-slot.hpp" + + + +namespace proc { +namespace play { + + + + namespace { // hidden local details of the service implementation.... + + } // (End) hidden service impl details + + + + OutputSlot::~OutputSlot() { } // emit VTables here.... + + + + /** */ + + + + +}} // namespace proc::play diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp new file mode 100644 index 000000000..bf74ff6ed --- /dev/null +++ b/src/proc/play/output-slot.hpp @@ -0,0 +1,101 @@ +/* + OUTPUT-SLOT.hpp - capability to transfer data to a physical output + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + +/** @file output-slot.hpp + ** An (abstract) capability to send media data to an external output. + ** OutputSlot is the central metaphor for the organisation of actual (system level) outputs; + ** using this concept allows to separate and abstract the data calculation and the organisation + ** of playback and rendering from the specifics of the actual output sink. Actual output + ** possibilities can be added and removed dynamically from various components (backend, GUI), + ** all using the same resolution and mapping mechanisms + ** + ** @see output-test-slot.hpp ////TODO + */ + + +#ifndef PROC_PLAY_OUTPUT_SLOT_H +#define PROC_PLAY_OUTPUT_SLOT_H + + +#include "lib/error.hpp" +#include "lib/handle.hpp" +#include "lib/time/timevalue.hpp" +//#include "lib/sync.hpp" + +#include +//#include +//#include +//#include +//#include + + +namespace proc { +namespace play { + +//using std::string; +//using std::vector; +//using std::tr1::shared_ptr; +//using boost::scoped_ptr; + + + + class Connection; + + class BufferHandoverSink + : public lib::Handle + { + + public: + void emit(Time); + }; + + + class SharedBufferSink + : public lib::Handle + { + + public: + void emit(Time); + }; + + + + + /******************************************************************** + * Interface: Generic output sink. + * + * @todo write type comment + */ + class OutputSlot + : boost::noncopyable + { + public: + virtual ~OutputSlot(); + + private: + + }; + + + +}} // namespace proc::play +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index f86c6087b..e35b52c9e 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3219,7 +3219,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3244,10 +3244,12 @@ We should note that in both cases this [[mapping operation|OutputMapping]] is co
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
-&rarr; see also the PlayerDummy
+&rarr; see also the PlayService
 
 !the global output manager
-While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager''. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities.
+While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+
+The relation between the central OutputDirector and the peripheral OutputManger implementations is hierarchical. Because outpust slots are usually registered at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. {{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
 
@@ -3257,13 +3259,13 @@ While mostly the model just denotes the destination for output as OutputDesignat &rarr; see OutputSlot &rarr; see ViewerPlayConnection
-
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
 ;external outputs
 :external outputs aren't addressed directly. Rather we set up a default (channel) mapping, which then can be overridden by local rules.
-:Thus, in this case we query with an internal OutputDesignation as parameter and expect an OutputManager //slot//
+:Thus, in this case we query with an internal OutputDesignation as parameter and expect an OutputSlot
 ;viewer connections
 :any Timeline produces a number of [[model ports|ModelPort]]. On the other hand, any viewer exposes a small number of output designations, representing the image and sound output(s).
 :Thus, in this case we resolve similar to a bus connection, possibly overridden by already pre-existing or predefined connections.
@@ -3289,14 +3291,14 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
 !Properties of an output slot
 Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). In some cases, this explicit registration may be extened to a //factory for additional output slots// -- to be used on demand. An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
 
-In order to be usable as //output sink,// an output slot needs to be allocated and locked. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
+In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
 
 Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The size and other characteristics of the data frames is assumed to be suitable. Typically this won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
 
@@ -3308,7 +3310,7 @@ Data is handed over by the client invoking an {{{emit(time,...)}}} function on t
 :here the output mechanism owns the buffer. Within a certain time window prior to the expected time of the {{{emit()}}}-call, the client may obtain this buffer (pointer) to fill in the data. The slot implementation won't touch this buffer until the {{{emit()}}} handover, which in this case just provides the time and states that the client is done with that buffer. If the data emitting handshake doesn't happen at all, it counts as late and superseded by the next handshake.
 
 !!!timing expectations
-Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, latency means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
 
 The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run within certain limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
 

From a19562942cf48ed68ab084caf6bc520f2464ffd7 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 21 Jun 2011 04:24:01 +0200
Subject: [PATCH 035/108] further considerations regarding output and output
 slots

---
 src/include/play-facade.h     |  4 ++--
 src/lib/streamtype.hpp        |  7 ++++++-
 src/proc/play/output-slot.hpp |  9 ++++++---
 wiki/renderengine.html        | 32 ++++++++++++++++++++++----------
 4 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/src/include/play-facade.h b/src/include/play-facade.h
index 84865444c..852fe5b78 100644
--- a/src/include/play-facade.h
+++ b/src/include/play-facade.h
@@ -115,8 +115,8 @@ namespace lumiera {
           };
         
         
-        typedef lib::IterSource&    ModelPorts;
-        typedef lib::IterSource& Pipes;
+        typedef lib::IterSource::iterator    ModelPorts;
+        typedef lib::IterSource::iterator Pipes;
         typedef proc::play::POutputManager Output;
         typedef mobject::session::PClipMO Clip;
         typedef mobject::PTrack  Track;
diff --git a/src/lib/streamtype.hpp b/src/lib/streamtype.hpp
index 745f50d86..e344b811d 100644
--- a/src/lib/streamtype.hpp
+++ b/src/lib/streamtype.hpp
@@ -110,7 +110,12 @@ namespace lumiera {
       
       class TypeTag ; 
       
-      /** placeholder definition for the contents of a data buffer */
+      /** 
+       * placeholder type for the contents of a data buffer.
+       * The actual buffer will always be provided by a 
+       * library implementation; throughout the engine,
+       * it's just hidden behind a DataBuffer pointer.
+       */
       struct DataBuffer { };
       
       
diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp
index bf74ff6ed..2b7a1e754 100644
--- a/src/proc/play/output-slot.hpp
+++ b/src/proc/play/output-slot.hpp
@@ -39,6 +39,7 @@
 #include "lib/error.hpp"
 #include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
+#include "proc/engine/buffhandle.hpp"
 //#include "lib/sync.hpp"
 
 #include 
@@ -50,14 +51,16 @@
 
 namespace proc {
 namespace play {
-  
+
+  using ::engine::BuffHandle;
 //using std::string;
+
 //using std::vector;
 //using std::tr1::shared_ptr;
 //using boost::scoped_ptr;
   
   
-  
+  /** established output channel */
   class Connection;
   
   class BufferHandoverSink
@@ -65,7 +68,7 @@ namespace play {
     {
       
     public:
-      void emit(Time);
+      void emit(Time, BuffHandle);
     };
   
   
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index e35b52c9e..292e26c24 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1774,8 +1774,10 @@ To make the intended use of the classes more clear, consider the following two e
 * a video clip placed relatively, with an attached HUE effect &rarr;[[Example2]]
 
-
+
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
+
+The render nodes network is always buils separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
 
@@ -3219,7 +3221,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3236,7 +3238,7 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
+[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
 
 We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
@@ -3244,20 +3246,30 @@ We should note that in both cases this [[mapping operation|OutputMapping]] is co
 External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
+&rarr; the OutputManager interface describes handling this mapping association
 &rarr; see also the PlayService
 
 !the global output manager
-While mostly the model just denotes the destination for output as OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+While within the model routing is done mostly just by referring to an OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
 
-The relation between the central OutputDirector and the peripheral OutputManger implementations is hierarchical. Because outpust slots are usually registered at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. {{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
+The relation between the central OutputDirector and the peripheral OutputManager implementations is hierarchical. Because output slots are usually registered rather at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. Resolving a global pipe into an output slot is the core concern of any OutputManager implementation. Thus, when there is a locally preconfigured mapping, like e.g. for a viewer's video master pipe to the output slot installed by the corresponding GUI viewer element, then this mapping will picked up foremost to resolve the video master output.
+
+For a viewer widget in the GUI this yields exactly the expeted behaviour, but in other cases, e.g. for sound output, we need more general, more globally scoped output slots. In these cases, when a local mapping is absent, the query for output resolution is passed on up to the  OutputDirector, drawing on the collection of globally available output slots for that specific kind of media.
+{{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
 
-
-
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, the OutputDirector, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
 
 &rarr; see [[output management overview|OutputManagement]]
 &rarr; see OutputSlot
-&rarr; see ViewerPlayConnection
+&rarr; see ViewerPlayConnection + +!Role of an output manager +The output manager interface describes an entity handling two distinct concerns, tied together within a local //scope.// +* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot (or several in case of multichannel media) +* the ''registration'' and management of possible output slots, thereby creating a preferred local mapping for future connections +
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
@@ -5427,7 +5439,7 @@ if (oldText.indexOf("SplashScreen")==-1)
 * besides, between different stream //implementation types,// there can be a ''rendering'' (lossy conversion) &mdash; or no conversion at all.
 
-
+
The stream Prototype is part of the specification of a media stream's type. It is a semantic (or problem domain oriented) concept and should be distinguished from the actual implementation type of the media stream. The latter is provided by an [[library implementation|StreamTypeImplFacade]]. While there are some common predefined prototypes, mostly, they are defined within the concrete [[Session]] according to the user's needs. 
 
 Prototypes form an open (extensible) collection, though each prototype belongs to a specific media kind ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}).
@@ -5440,7 +5452,7 @@ Consequently, as we can't get away with an fixed Enum of all stream prototypes,
 * we can determine if another prototype is //convertible//
 
 !!Examples
-NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
+In practice, several things might be considered "quite different" and thus be distinguished by protorype: NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
 
From dea1fa57a21ac2b0478b763cf45532bf6a647685 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 22 Jun 2011 02:42:50 +0200 Subject: [PATCH 036/108] draft play process structure; clarify handling of multiple channels --- doc/devel/uml/fig143877.png | Bin 0 -> 13790 bytes doc/devel/uml/fig144005.png | Bin 0 -> 15144 bytes src/proc/engine/buffhandle.hpp | 16 +- src/proc/engine/nodeinvocation.hpp | 1 + src/proc/engine/nodeoperation.hpp | 2 +- src/proc/play/output-slot.hpp | 23 ++ uml/lumiera/129285 | 30 +-- uml/lumiera/133637 | 348 +++++++++++++++++++++++++++++ uml/lumiera/143877.diagram | 88 ++++++++ uml/lumiera/144005.diagram | 94 ++++++++ uml/lumiera/5.session | 39 +--- uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 34 ++- 13 files changed, 607 insertions(+), 70 deletions(-) create mode 100644 doc/devel/uml/fig143877.png create mode 100644 doc/devel/uml/fig144005.png create mode 100644 uml/lumiera/133637 create mode 100644 uml/lumiera/143877.diagram create mode 100644 uml/lumiera/144005.diagram diff --git a/doc/devel/uml/fig143877.png b/doc/devel/uml/fig143877.png new file mode 100644 index 0000000000000000000000000000000000000000..1e628bc4158d075b837cc675779831ed96881bc6 GIT binary patch literal 13790 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 literal 0 HcmV?d00001 diff --git a/doc/devel/uml/fig144005.png b/doc/devel/uml/fig144005.png new file mode 100644 index 0000000000000000000000000000000000000000..93627a5ac9bd816faa12a234164a0fdbff947f54 GIT binary patch literal 15144 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 literal 0 HcmV?d00001 diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 6cdca586c..0f6378396 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -52,6 +52,9 @@ namespace engine { /** * Handle for a buffer for processing data, abstracting away the actual implementation. * The real buffer pointer can be retrieved by dereferencing this smart-handle class. + * + * @todo as of 6/2011 it isn't clear how buffer handles are actually created + * and how the lifecycle (and memory) management works */ struct BuffHandle : lib::BoolCheckable @@ -78,15 +81,24 @@ namespace engine { } - //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> Ticket 249 + //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> TICKET #249 BuffHandle() : pBuffer_(0), sourceID_(0) { } + /** + * @deprecated placeholder implementation + * @todo rework the Lifecycle handling of buffers //////////TICKET #249 + */ + BuffHandle(PBuff existingBuffer) + : pBuffer_(existingBuffer) + , sourceID_(0) + { } + private: PBuff pBuffer_; - long sourceID_; + long sourceID_; ////TICKET #249 this is a placeholder for a "type-like information", to be used for lifecycle management and sanity checks.... }; diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp index dd9b17ea4..ad9c6d402 100644 --- a/src/proc/engine/nodeinvocation.hpp +++ b/src/proc/engine/nodeinvocation.hpp @@ -172,6 +172,7 @@ namespace engine { }; + ////////////TICKET #249 this strategy should better be hidden within the BuffHanle ctor (and type-erased after creation) struct AllocBufferFromParent ///< using the parent StateAdapter for buffer allocations : Invocation { diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index ce587862b..63753cee6 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -245,7 +245,7 @@ namespace config { template struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor?? - { ///////////////// this would require BuffHandle to be a smart ref.... + { ///////////////// this would require BuffHandle to be a smart ref.... --> ///TICKET #249 BuffHandle step (Invocation& ivo) { diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 2b7a1e754..36f6de9b7 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -40,6 +40,7 @@ #include "lib/handle.hpp" #include "lib/time/timevalue.hpp" #include "proc/engine/buffhandle.hpp" +#include "lib/iter-source.hpp" //#include "lib/sync.hpp" #include @@ -53,6 +54,7 @@ namespace proc { namespace play { using ::engine::BuffHandle; + using lib::time::Time; //using std::string; //using std::vector; @@ -94,6 +96,27 @@ namespace play { public: virtual ~OutputSlot(); + typedef lib::IterSource::iterator Opened_BufferHandoverSinks; + typedef lib::IterSource::iterator Opened_SharedBufferSinks; + + template + struct Allocation + { + typedef typename lib::IterSource::iterator OpenedSinks; + + OpenedSinks getOpenedSinks(); + + bool isActive(); + + /////TODO add here the getters for timing constraints + }; + + + bool isFree() const; + + Allocation + allocate(); + private: }; diff --git a/uml/lumiera/129285 b/uml/lumiera/129285 index 117e269b7..bb1c705fe 100644 --- a/uml/lumiera/129285 +++ b/uml/lumiera/129285 @@ -1,6 +1,6 @@ format 58 "ProcessingLayer" // ProcessingLayer - revision 26 + revision 27 modified_by 5 "hiv" // class settings //class diagram settings @@ -30,6 +30,8 @@ format 58 package_ref 129029 // Control + package_ref 133637 // Play + package_ref 128261 // MObject package_ref 128389 // RenderEngine @@ -330,8 +332,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131333 // ouput end end @@ -350,8 +350,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131589 // end end @@ -360,8 +358,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131717 // vid_a end end @@ -370,8 +366,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 131461 // input end end @@ -430,8 +424,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 133893 // HUE end end @@ -440,8 +432,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134021 // vid_a end end @@ -464,8 +454,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 133253 // HUE end end @@ -474,8 +462,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 132869 // input end end @@ -506,8 +492,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 133125 // ouput end end @@ -516,8 +500,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 132613 // devnull end end @@ -534,8 +516,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134021 // vid_a end end @@ -558,8 +538,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134149 // input end end @@ -568,8 +546,6 @@ format 58 attributes end relations - relation_ref 135429 // - classinstance_ref 134405 // ouput end end diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 new file mode 100644 index 000000000..d788a91a9 --- /dev/null +++ b/uml/lumiera/133637 @@ -0,0 +1,348 @@ +format 58 +"Play" // ProcessingLayer::Play + revision 1 + modified_by 5 "hiv" + // class settings + //class diagram settings + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default class_drawing_mode default shadow default show_stereotype_properties default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default show_stereotype_properties default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default show_stereotype_properties default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default show_stereotype_properties default + //component diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default show_stereotype_properties default + //deployment diagram settings + package_name_in_tab default show_context default write_horizontally default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default show_stereotype_properties default + //state diagram settings + package_name_in_tab default show_context default auto_label_position default write_trans_label_horizontally default show_trans_definition default draw_all_relations default shadow default + show_activities default region_horizontally default drawing_language default show_stereotype_properties default + //activity diagram settings + package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default + show_infonote default drawing_language default show_stereotype_properties default + + classview 136837 "PlayOut" + //class diagram settings + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default show_stereotype_properties default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default show_stereotype_properties default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default show_stereotype_properties default + //state diagram settings + package_name_in_tab default show_context default auto_label_position default write_trans_label_horizontally default show_trans_definition default draw_all_relations default shadow default + show_activities default region_horizontally default drawing_language default show_stereotype_properties default + //class settings + //activity diagram settings + package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default + show_infonote default drawing_language default show_stereotype_properties default + classdiagram 143877 "Player Entities" + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + size A4 + end + + objectdiagram 144005 "Play Process Structure" + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default show_stereotype_properties default + size A4 + end + + class 176133 "OutputSlot" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 176261 "Controller" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 176389 "PlayProcess" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216837 // + relation 205701 *--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 216837 // + b parent class_ref 177285 // Feed + end + end + + class 176517 "OutputManager" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216709 // + relation 205573 o--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 216709 // + b parent class_ref 176133 // OutputSlot + end + end + + class 176645 "OutputDirector" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216581 // + relation 205445 -_-|> + a public + cpp default "${type}" + classrelation_ref 216581 // + b parent class_ref 176517 // OutputManager + end + end + + class 176773 "ModelPort" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 176901 "CalcStream" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 217093 // + relation 205957 ---> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 217093 // + b parent class_ref 177029 // Dispatcher + end + end + + class 177029 "Dispatcher" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 177157 "PlayService" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + classinstance 145669 "" + type class_ref 176261 // Controller + attributes + end + relations + end + end + + classinstance 145797 "" + type class_ref 176389 // PlayProcess + attributes + end + relations + end + end + + class 177285 "Feed" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 216965 // + relation 205829 *--> + a role_name "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value}; +" + classrelation_ref 216965 // + b parent class_ref 176901 // CalcStream + end + end + + classinstance 145925 "" + type class_ref 177285 // Feed + attributes + end + relations + end + end + + classinstance 146053 "" + type class_ref 177285 // Feed + attributes + end + relations + end + end + + classinstance 146181 "video" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146309 "video" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146437 "sound-W" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146565 "sound-X" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146693 "sound-Z" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 146821 "" + type class_ref 176773 // ModelPort + attributes + end + relations + end + end + + classinstance 146949 "" + type class_ref 176773 // ModelPort + attributes + end + relations + end + end + + classinstance 147077 "" + type class_ref 176133 // OutputSlot + attributes + end + relations + end + end + + classinstance 147205 "" + type class_ref 176133 // OutputSlot + attributes + end + relations + end + end + end +end diff --git a/uml/lumiera/143877.diagram b/uml/lumiera/143877.diagram new file mode 100644 index 000000000..f49ed5f08 --- /dev/null +++ b/uml/lumiera/143877.diagram @@ -0,0 +1,88 @@ +format 58 + +classcanvas 128005 class_ref 176133 // OutputSlot + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 349 106 2000 +end +classcanvas 128133 class_ref 176261 // Controller + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 71 172 2000 +end +classcanvas 128261 class_ref 176389 // PlayProcess + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 143 222 2000 +end +classcanvas 128389 class_ref 176517 // OutputManager + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 472 47 2000 +end +classcanvas 128517 class_ref 176645 // OutputDirector + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 474 165 2000 +end +classcanvas 129541 class_ref 176773 // ModelPort + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 355 266 2000 +end +classcanvas 129669 class_ref 176901 // CalcStream + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 207 380 2000 +end +classcanvas 129797 class_ref 177029 // Dispatcher + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 355 381 2000 +end +fragment 130053 "output management" + xyzwh 299 17 2000 324 218 +end +fragment 130181 "Model / Fixture" + xyzwh 299 239 1995 323 91 +end +fragment 130309 "Player" + xyzwh 16 87 1990 274 243 +end +classcanvas 130437 class_ref 177157 // PlayService + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 40 118 2005 +end +fragment 130565 "Engine" + xyzwh 16 346 1995 606 208 +end +classcanvas 130693 class_ref 177285 // Feed + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default + xyz 218 273 2000 +end +relationcanvas 128645 relation_ref 205445 // + from ref 128517 z 1999 to ref 128389 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 128773 relation_ref 205573 // + geometry HVH + from ref 128389 z 1999 to point 439 64 + line 129285 z 1999 to point 439 123 + line 129413 z 1999 to ref 128005 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 130821 relation_ref 205701 // + geometry VH + from ref 128261 z 1999 to point 178 290 + line 130949 z 1999 to ref 130693 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131077 relation_ref 205829 // + from ref 130693 z 1999 to ref 129669 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +relationcanvas 131205 relation_ref 205957 // + from ref 129669 z 1999 to ref 129797 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +end +line 131333 -_-_ geometry VH + from ref 128133 z 1999 to point 99 239 + line 131461 z 1999 to ref 128261 +end diff --git a/uml/lumiera/144005.diagram b/uml/lumiera/144005.diagram new file mode 100644 index 000000000..42a499609 --- /dev/null +++ b/uml/lumiera/144005.diagram @@ -0,0 +1,94 @@ +format 58 + +classinstancecanvas 128005 classinstance_ref 145669 // + xyz 41 85 2000 +end +classinstancecanvas 128133 classinstance_ref 145797 // + xyz 105 142 2000 +end +classinstancecanvas 128261 classinstance_ref 145925 // + xyz 171 173 2000 +end +classinstancecanvas 128389 classinstance_ref 146053 // + xyz 171 240 2000 +end +classinstancecanvas 128517 classinstance_ref 146181 // video + xyz 233 194 2000 color lightgreen +end +classinstancecanvas 128645 classinstance_ref 146309 // video + xyz 233 216 2000 color lightgreen +end +classinstancecanvas 128773 classinstance_ref 146437 // sound-W + xyz 233 269 2000 color lightgreen +end +classinstancecanvas 128901 classinstance_ref 146565 // sound-X + xyz 233 291 2000 color lightgreen +end +classinstancecanvas 129029 classinstance_ref 146693 // sound-Z + xyz 233 313 2000 color lightgreen +end +classinstancecanvas 129157 classinstance_ref 146693 // sound-Z + xyz 233 335 2000 color lightgreen +end +classinstancecanvas 132101 classinstance_ref 146821 // + xyz 420 269 2000 color lightblue +end +classinstancecanvas 132229 classinstance_ref 146949 // + xyz 420 194 2000 color lightblue +end +note 132357 "stateless (functional)" + xyzwh 348 140 2000 124 35 +note 132485 "stateful" + color verylightorange xyzwh 131 85 2000 72 36 +classinstancecanvas 132613 classinstance_ref 147077 // + xyz 233 23 2000 +end +classinstancecanvas 132741 classinstance_ref 147205 // + xyz 233 46 2000 +end +objectlinkcanvas 129285 norel + geometry VH + from ref 128133 z 1999 to point 139 181 + line 129413 z 1999 to ref 128261 + no_role_a no_role_b +objectlinkcanvas 129541 norel + geometry VH + from ref 128133 z 1999 to point 139 248 + line 129797 z 1999 to ref 128389 + no_role_a no_role_b +objectlinkcanvas 129925 norel + geometry VH + from ref 128261 z 1999 to point 194 202 + line 130053 z 1999 to ref 128517 + no_role_a no_role_b +objectlinkcanvas 130181 norel + geometry VH + from ref 128261 z 1999 to point 194 224 + line 130309 z 1999 to ref 128645 + no_role_a no_role_b +objectlinkcanvas 130437 norel + geometry VH + from ref 128389 z 1999 to point 194 277 + line 130565 z 1999 to ref 128773 + no_role_a no_role_b +objectlinkcanvas 130693 norel + geometry VH + from ref 128389 z 1999 to point 194 299 + line 130821 z 1999 to ref 128901 + no_role_a no_role_b +objectlinkcanvas 130949 norel + geometry VH + from ref 128389 z 1999 to point 194 321 + line 131077 z 1999 to ref 129029 + no_role_a no_role_b +objectlinkcanvas 131205 norel + geometry VH + from ref 128389 z 1999 to point 194 343 + line 131333 z 1999 to ref 129157 + no_role_a no_role_b +objectlinkcanvas 131845 norel + geometry VH + from ref 128005 z 1999 to point 68 150 + line 131973 z 1999 to ref 128133 + no_role_a no_role_b +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 4e22d4c88..0e80581b8 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -4,12 +4,10 @@ diagrams 631 352 100 4 0 0 objectdiagram_ref 138885 // ModelAssetRelations 730 488 100 4 0 0 - classdiagram_ref 142725 // Time flavours - 595 646 100 4 0 0 - classdiagram_ref 131205 // Struct-Asset Relations - 530 608 100 4 0 0 - active classdiagram_ref 130309 // Asset Kinds - 855 805 100 4 0 0 + classdiagram_ref 143877 // Player Entities + 663 648 100 4 0 0 + active objectdiagram_ref 144005 // Play Process Structure + 562 424 100 4 0 0 end show_stereotypes selected @@ -17,36 +15,13 @@ selected open package_ref 128005 // design - class_ref 160389 // VirtualMedia - class_ref 136837 // Proc - class_ref 152197 // Sequence - class_ref 160901 // Timeline - class_ref 174981 // Viewer - class_ref 139269 // DoRecurse - class_ref 162821 // TypedID::Link classview_ref 128389 // Controller Workings - class_ref 139653 // Session - class_ref 145541 // Timeline - class_ref 128133 // Seq - class_ref 160517 // Root - class_ref 128389 // Track - class_ref 152325 // Binding - class_ref 129797 // ExplicitPlacement - class_ref 129029 // Effect - class_ref 139909 // LocatingPin - class_ref 152453 // PlacementRef - classrelation_ref 178437 // + classview_ref 136837 // PlayOut class_ref 153733 // QueryFocusStack usecaseview_ref 128261 // config examples package_ref 128389 // RenderEngine - classdiagram_ref 142725 // Time flavours - class_ref 134917 // Time - class_ref 168837 // Duration - class_ref 169221 // TimeGrid - class_ref 170373 // TimeVar - class_ref 170501 // QuTimeSpan - classview_ref 128645 // Service Components - classview_ref 128266 // SmartPointers + + package_ref 129157 // BackendLayer end end diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index 35fe3a3e2..e9189265f 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 69 + revision 70 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 292e26c24..927c2b79b 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3037,7 +3037,7 @@ __Note__: nothing within the PlacementIndex requires the root object to be of a
-
+
Based on practical experiences, Ichthyo tends to consider Multichannel Media as the base case, while counting media files providing just one single media stream as exotic corner cases. This may seem counter intuitive at first sight; you should think of  it as an attempt to avoid right from start some of the common shortcomings found in many video editors, especially
 * having to deal with keeping a "link" between audio and video clips
 * silly limitations on the supported audio setups (e.g. "sound is mono, stereo or Dolby-5.1")
@@ -3070,6 +3070,12 @@ While the general approach and reasoning remains valid, a lot of the details loo
 * while the asset related parts remain as specified, we get a distinct ChannelConfig asset instead of the clip asset (which seems to be redundant)
 * either the ~ClipMO referres this ChannelConfig asset &mdash; or in case of the VirtualClip a BindingMO takes this role. Clip Asset and MO could be joined into a single entity
 * as the BindingMO is also used to implement the top-level timelines, the treatment of global and local pipes is united
+* every pipe (bus) should be able to carry multiple channels, //but with the limitation to only a single media StreamType//
+* this "multichannel-of-same-kind" capability carries over to the ModelPort entries and even the OutputSlot elements
+* only when allocating / "opening" an OutputSlot, we get multiple handles for plain single channels.
+* this can be considered the breaking point, where we enter the realm of the render engine. Here, indeed, only single channels are processed
+
+
 
@@ -3258,7 +3264,7 @@ For a viewer widget in the GUI this yields exactly the expeted behaviour, but in {{red{Question: is it possible to retrieve a slot from another peripheral node?}}}
-
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, the OutputDirector, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
 
 &rarr; see [[output management overview|OutputManagement]]
@@ -3267,8 +3273,12 @@ For a viewer widget in the GUI this yields exactly the expeted behaviour, but in
 
 !Role of an output manager
 The output manager interface describes an entity handling two distinct concerns, tied together within a local //scope.//
-* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot (or several in case of multichannel media)
+* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot
 * the ''registration'' and management of possible output slots, thereby creating a preferred local mapping for future connections
+
+Note that an OutputSlot acts as a unit for registration and also for allocating / "opening" an output, while generally there might still be multiple physical outputs grouped into a single slot. This is relevant especially for sound output. A single slot is just the ability to allocate output ports up to a given limit (e.g. 2 for a stereo device, or 6 for a 5.1 device). These multiple channels are allways connected following a natural channel ordering. Thus the mapping is a simple 1:1 association from pipe to slot, assuming that the media types are compatible (and this has been checked already on a higher level).
+
+The //registration//&nbsp; of an output slot installs a functor or association rule, which later on allows to claim and connect up to a preconfigured number of channels. This allocation or usage of a slot is exclusive (i.e. only a single client at a time can allocate a slot, even if not using all the possible channels). Each output manager instance may or may not be configured with a //fall-back rule:// when no association or mapping can be established locally, the connection request might be passed down to the global OutputDirector. Again, we can expect this to be the standard behaviour for sound, while video likely will rather be handled locally, e.g. within a GUI widget (but we're not bound to configure it exactly this way)
 
@@ -3303,12 +3313,12 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
 !Properties of an output slot
-Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). In some cases, this explicit registration may be extened to a //factory for additional output slots// -- to be used on demand. An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
+Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right soundcard output channels), or an output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
 
 In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
 
@@ -3328,6 +3338,9 @@ The assumption is for the client to have elaborate timing capabilities at his di
 
 {{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
 
+!!!Lifecycle and storage
+The concrete OutputSlot implementation is owned and managed by the facility actually providing this output possibility. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. This implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be deregistered explicitly prior to destruction -- and this deregistration may block until all clients using this slot are terminated. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferrably just emitting a signal (callback functor).
+
 
@@ -4188,14 +4201,21 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>>
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
 The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
 ''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalculationStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
 * the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the CalculationStream
 * the RenderEngine has the ability to cary out individual frame calculations
-* the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
+* the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery + +[>img[Anatomy of a Play Process|uml/fig144005.png]] +!Anatomy of a Play Process +The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind. + +Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, hooked up into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded. +
The [[Player]] is an independent [[Subsystem]] within Lumiera, located at Proc-Layer level. A more precise term would be "rendering and playback coordination subsystem". It provides the capability to generate media data, based on a high-level model object, and send this generated data to an OutputDesignation, creating an continuous and timing controlled output stream. Clients may utilise these functionality through the ''play service'' interface.

From 9e53053944683e88056a94af9ec24ca67d4e8731 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 23 Jun 2011 17:54:28 +0200
Subject: [PATCH 037/108] WIP: draft building a PlayProcess

---
 src/proc/play/play-process.hpp | 45 ++++++++++++++++++++++++++++++++--
 src/proc/play/play-service.cpp | 33 +++++++++++++++++++++----
 wiki/renderengine.html         | 13 +++++++---
 3 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp
index 9207a3b10..5045544b7 100644
--- a/src/proc/play/play-process.hpp
+++ b/src/proc/play/play-process.hpp
@@ -21,7 +21,27 @@
 */
 
 /** @file play-process.hpp
- *  
+ ** Organisational unit of an ongoing render- or playback process.
+ ** A process object doesn't perform any work in itself, rather it's
+ ** an entry in the process table maintained within the PlayService.
+ ** This table entry is used to keep track of the individual data feeds,
+ ** each corresponding to one of the global pipes to be "performed" in order
+ ** to generate output data. Usually, these global pipes all belong to a given
+ ** Timeline (but other setups are possible as well).
+ ** 
+ ** Each of these Feed objects comprising a play process is in turn responsible
+ ** for getting one or multiple CalculationStream entities configured and operative
+ ** within the actual render engine. Each of these calculation streams corresponds
+ ** to a running series of calculations for consecutive frames, to be delivered
+ ** in a time-bound fashion from the render engine into an OutputSlot, allocated
+ ** for rendering this specific feed.
+ ** 
+ ** A PlayProcess isn't exposed directly to client code -- it's the body object,
+ ** while a Play::Controller handle is returned to the client (PImpl pattern).
+ ** Using this controller frontend, clients are allowed to control and change
+ ** the playback or rendering state and goals, which then causes the PlayProcess
+ ** to reconfigure the ongoing or planned calculations.
+ ** 
  ** @see lumiera::DummyPlayer
  ** @see gui::PlaybackController usage example 
  */
@@ -31,6 +51,7 @@
 #define PROC_PLAY_PLAY_PROCESS_H
 
 
+#include "lib/error.hpp"
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
 //#include "common/instancehandle.hpp"
@@ -49,8 +70,21 @@ namespace play {
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
   
+  namespace error = lumiera::error;
   
   
+  /**
+   * Rendering data feed, corresponding to a single
+   * global pipe and to be delivered into a single OutputSlot.
+   * A feed may still be comprised of multiple channels, but is
+   * bound to operate on a single type of media data only.
+   */
+  class Feed
+    : boost::noncopyable
+    {
+      
+    };
+  
   /******************************************************
    * Playback/Render process within the Lumiera Player.
    * This is a top-level implementation entity, created
@@ -72,7 +106,14 @@ namespace play {
     {
       
     public:
-      
+      template
+      PlayProcess (CONS pipeConnections)
+        {
+          if (isnil (pipeConnections))
+            throw error::State ("creating a PlayProcess without any usable output connections");
+          
+          UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects");
+        }
     };
   
   
diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp
index 50077545a..72e89fa35 100644
--- a/src/proc/play/play-service.cpp
+++ b/src/proc/play/play-service.cpp
@@ -23,12 +23,13 @@
 
 #include "proc/play/play-service.hpp"
 #include "proc/play/play-process.hpp"
-#include "lib/scoped-ptrvect.hpp" 
+#include "lib/itertools.hpp"
 
 
 #include 
 //#include 
-//#include 
+#include 
+#include 
 //#include 
 
 
@@ -53,6 +54,10 @@ namespace play {
 //using std::tr1::bind;
   using lib::Sync;
   using lib::RecursiveLock_NoWait;
+  using lib::filterIterator;
+  using std::tr1::weak_ptr;
+  using std::tr1::bind;
+  using std::tr1::placeholders::_1;
   
   
   namespace { // hidden local details of the service implementation....
@@ -62,8 +67,6 @@ namespace play {
     
     
     
-    /* ================== define an lumieraorg_DummyPlayer instance ======================= */
-    
     
   } // (End) hidden service impl details
   
@@ -71,12 +74,29 @@ namespace play {
   class ProcessTable
     : public Sync
     {
-      typedef lib::ScopedPtrVect ProcTable;
+      typedef std::vector > ProcTable;
       
       ProcTable processes_;
       
     public:
       
+      lumiera::Play::Controller
+      establishProcess (PlayProcess* newProcess)
+        {
+          lumiera::Play::Controller frontend;
+          
+          frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 ));
+          processes_.push_back (frontend);
+          return frontend;
+        }
+      
+    private:
+      void
+      endProcess (PlayProcess* dyingProcess)
+        {
+          delete dyingProcess;
+          UNIMPLEMENTED ("process deregistration");   /// note the process might not be registered at all
+        }
     };
   
   
@@ -114,6 +134,9 @@ namespace play {
   PlayService::connect(ModelPorts dataGenerators, Output outputDestinations)
   {
     UNIMPLEMENTED ("build a PlayProcess");
+    pTable_->establishProcess(
+                 new PlayProcess (filterIterator (dataGenerators,
+                                                  resolve(outputDestinations))));
   }
   
 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 927c2b79b..ce2cd3f38 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3341,6 +3341,13 @@ The assumption is for the client to have elaborate timing capabilities at his di
 !!!Lifecycle and storage
 The concrete OutputSlot implementation is owned and managed by the facility actually providing this output possibility. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. This implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be deregistered explicitly prior to destruction -- and this deregistration may block until all clients using this slot are terminated. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferrably just emitting a signal (callback functor).
 
+-----
+!Implementation / design problems
+How to handle the selection of those two data exchange models!
+-- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. Yet I wanted to handle these through //generic programming// -- because they can't be subsumed under a single interface without distorting the usage or the structure.
+
+Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy, note, at //compile time!//
+
 
@@ -4201,7 +4208,7 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T <<tasksum end>>
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
 The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
@@ -4214,7 +4221,7 @@ The play process is an conceptual entity linking together several activities in
 !Anatomy of a Play Process
 The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind.
 
-Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, hooked up into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
+Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
 
From 511d08adad4c8f47389319e8e375c2d67980f6d3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 24 Jun 2011 18:40:19 +0200 Subject: [PATCH 038/108] WIP: bilding a PlayProcess... --- src/include/play-facade.h | 2 ++ src/proc/play/output-manager.hpp | 10 +++++- src/proc/play/play-process.hpp | 6 +++- src/proc/play/play-service.cpp | 61 +++++++++++++++++++++++++++----- src/proc/play/play-service.hpp | 1 + 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index 852fe5b78..c1739bac6 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -112,6 +112,8 @@ namespace lumiera { double getSpeed() const; uint getQuality() const; bool usesProxy() const; + + operator std::tr1::weak_ptr(); }; diff --git a/src/proc/play/output-manager.hpp b/src/proc/play/output-manager.hpp index a6fd3b91a..165c1af2f 100644 --- a/src/proc/play/output-manager.hpp +++ b/src/proc/play/output-manager.hpp @@ -33,6 +33,7 @@ #include "lib/error.hpp" #include "proc/play/output-slot.hpp" +#include "proc/mobject/model-port.hpp" #include //#include @@ -48,6 +49,10 @@ namespace play { using std::tr1::shared_ptr; + LUMIERA_ERROR_DECLARE(CANT_PLAY); ///< unable to build playback or render process for this configuration + + + @@ -62,7 +67,10 @@ namespace play { : boost::noncopyable { public: - OutputManager() {} + virtual ~OutputManager() { } + + + virtual OutputSlot& getOutputFor (mobject::ModelPort port) =0; }; typedef shared_ptr POutputManager; diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index 5045544b7..aebd971dd 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -56,6 +56,8 @@ //#include "include/display-facade.h" //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" +#include "proc/play/output-manager.hpp" +#include "lib/util.hpp" // #include //#include @@ -69,6 +71,7 @@ namespace play { // using lumiera::Subsys; // using lumiera::Display; // using lumiera::DummyPlayer; + using util::isnil; namespace error = lumiera::error; @@ -110,7 +113,8 @@ namespace play { PlayProcess (CONS pipeConnections) { if (isnil (pipeConnections)) - throw error::State ("creating a PlayProcess without any usable output connections"); + throw error::State ("creating a PlayProcess without any usable output connections" + , LUMIERA_ERROR_CANT_PLAY); UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects"); } diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 72e89fa35..802823631 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -24,6 +24,7 @@ #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" #include "lib/itertools.hpp" +#include "lib/util.hpp" #include @@ -54,10 +55,15 @@ namespace play { //using std::tr1::bind; using lib::Sync; using lib::RecursiveLock_NoWait; - using lib::filterIterator; + using lib::transformIterator; using std::tr1::weak_ptr; using std::tr1::bind; + using std::tr1::function; using std::tr1::placeholders::_1; + using util::remove_if; + using mobject::ModelPort; + + typedef proc::play::POutputManager POutputManager; namespace { // hidden local details of the service implementation.... @@ -74,7 +80,8 @@ namespace play { class ProcessTable : public Sync { - typedef std::vector > ProcTable; + typedef weak_ptr Entry; + typedef std::vector ProcTable; ProcTable processes_; @@ -85,6 +92,7 @@ namespace play { { lumiera::Play::Controller frontend; + Lock sync(this); frontend.activate (newProcess, bind (&ProcessTable::endProcess, this, _1 )); processes_.push_back (frontend); return frontend; @@ -95,7 +103,15 @@ namespace play { endProcess (PlayProcess* dyingProcess) { delete dyingProcess; - UNIMPLEMENTED ("process deregistration"); /// note the process might not be registered at all + + Lock sync(this); + remove_if (processes_, isDead); + } + + static bool + isDead (Entry const& e) + { + return e.expired(); } }; @@ -112,13 +128,39 @@ namespace play { * rendered data for output. Client code is assumed to access * this service through the lumiera::Play facade. */ - PlayService::PlayService() /////TODO Subsys::SigTerm terminationHandle); + PlayService::PlayService() : facadeAccess_(*this, "Player") , pTable_(new ProcessTable) { } + namespace { // details... + + OutputSlot& + resolveOutputConnection (ModelPort port, POutputManager outputResolver) + { + REQUIRE (outputResolver); + OutputSlot& slot = outputResolver->getOutputFor (port); + if (!slot.isFree()) + throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 + , LUMIERA_ERROR_CANT_PLAY); + } + + /** try to establish an output slot for the given + * global bus or data production exit point. + * @param outputResolver a facility able to resolve to + * a concrete output slot within the actual context + * @throw error::State when resolution fails + */ + function + resolve (POutputManager outputResolver) + { + return bind (resolveOutputConnection, _1, outputResolver); + } + } + + /** * @note this is the core operation of the play and render service * @@ -133,12 +175,15 @@ namespace play { Play::Controller PlayService::connect(ModelPorts dataGenerators, Output outputDestinations) { - UNIMPLEMENTED ("build a PlayProcess"); - pTable_->establishProcess( - new PlayProcess (filterIterator (dataGenerators, - resolve(outputDestinations)))); + return pTable_->establishProcess( + new PlayProcess (transformIterator (dataGenerators, + resolve(outputDestinations)))); } + + + LUMIERA_ERROR_DEFINE (CANT_PLAY, "unable to build playback or render process for this configuration"); + }} // namespace proc::play diff --git a/src/proc/play/play-service.hpp b/src/proc/play/play-service.hpp index b0b39e4a1..9d70e23b6 100644 --- a/src/proc/play/play-service.hpp +++ b/src/proc/play/play-service.hpp @@ -58,6 +58,7 @@ namespace play { + /****************************************************** * Implementation access point: Player subsystem. * The PlayService is the primary way for clients to From 585adb88b6e53143386ca21cec32e94121e2591e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 25 Jun 2011 17:44:28 +0200 Subject: [PATCH 039/108] Refactoring: use the output Feed as intermediary construction step --- src/include/play-facade.h | 4 +- src/proc/play/output-slot.cpp | 12 +++- src/proc/play/play-controller.cpp | 10 ++++ src/proc/play/play-process.cpp | 91 ++++++++++++++++++++++++++++--- src/proc/play/play-process.hpp | 27 +++++---- src/proc/play/play-service.cpp | 38 ++----------- 6 files changed, 127 insertions(+), 55 deletions(-) diff --git a/src/include/play-facade.h b/src/include/play-facade.h index c1739bac6..e2b03713b 100644 --- a/src/include/play-facade.h +++ b/src/include/play-facade.h @@ -55,6 +55,8 @@ namespace play { namespace lumiera { namespace time = lib::time; + + using std::tr1::weak_ptr; /****************************************************************** @@ -113,7 +115,7 @@ namespace lumiera { uint getQuality() const; bool usesProxy() const; - operator std::tr1::weak_ptr(); + operator weak_ptr() const; }; diff --git a/src/proc/play/output-slot.cpp b/src/proc/play/output-slot.cpp index ff2798ee0..ee04ed818 100644 --- a/src/proc/play/output-slot.cpp +++ b/src/proc/play/output-slot.cpp @@ -40,8 +40,16 @@ namespace play { - /** */ - + /** whether this output slot is occupied + * @return true if currently unconnected and + * able to connect and handle output data + */ + bool + OutputSlot::isFree() const + { + UNIMPLEMENTED ("connection state"); + } + diff --git a/src/proc/play/play-controller.cpp b/src/proc/play/play-controller.cpp index f8a3de185..5eb14b9f2 100644 --- a/src/proc/play/play-controller.cpp +++ b/src/proc/play/play-controller.cpp @@ -49,6 +49,7 @@ namespace lumiera { using lib::time::Time; using lib::time::Duration; using lib::time::TimeSpan; + using proc::play::PlayProcess; @@ -188,6 +189,15 @@ namespace lumiera { { UNIMPLEMENTED ("determine if the current render/playback uses proxy media"); } + + + /** @internal expose a weak reference to this handle object. + * Used by the ProcessTable to keep a link to all processes, + * without influencing their reference count */ + Play::Controller::operator weak_ptr() const + { + return weak_ptr (this->smPtr_); + } diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 3859244a5..305ec6d82 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -22,34 +22,109 @@ #include "proc/play/play-process.hpp" +#include "lib/itertools.hpp" //#include //#include -//#include +#include //#include -namespace proc { - namespace play{ +namespace proc { +namespace play { // using std::string; // using lumiera::Subsys; // using std::auto_ptr; // using boost::scoped_ptr; // using std::tr1::bind; + + + namespace { // Implementation details... + + using std::tr1::bind; + using std::tr1::function; + using std::tr1::placeholders::_1; + using lib::transformIterator; + using lib::wrapIter; - namespace { // hidden local details of the service implementation.... + Feed + resolveOutputConnection (ModelPort port, POutputManager outputResolver) + { + REQUIRE (outputResolver); + OutputSlot& slot = outputResolver->getOutputFor (port); + if (!slot.isFree()) + throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 + , LUMIERA_ERROR_CANT_PLAY); + return Feed (port,slot); + } - - - } // (End) hidden service impl details + + typedef function ConnectFunction; + + /** try to establish an output slot for the given + * global bus or data production exit point. + * @param outputResolver a facility able to resolve to + * a concrete output slot within the actual context + * @throw error::State when resolution fails + */ + ConnectFunction + resolve (POutputManager outputResolver) + { + return bind (resolveOutputConnection, _1, outputResolver); + } + Feed::Connections + transform (ModelPorts dataGenerators, ConnectFunction outputResolution) + { + return wrapIter( + transformIterator (dataGenerators, outputResolution)); + } - /** */ + } // (End) hidden service impl details + + + + + /** + * Factory: Initialise and configure a new PlayProcess. + * The caller gets to own and manage the returned process entry. + */ + PlayProcess* + PlayProcess::initiate (ModelPorts dataGenerators, POutputManager outputDestinations) + { + return new PlayProcess (transform (dataGenerators, + resolve(outputDestinations))); + + } + + + + /** @internal actually create and configure a play process instance */ + PlayProcess::PlayProcess (Feed::Connections pipeConnections) + { + if (isnil (pipeConnections)) + throw error::State ("creating a PlayProcess without any usable output connections" + , LUMIERA_ERROR_CANT_PLAY); + + UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects"); + while (pipeConnections) + { + + } + } + + + /** */ + Feed::Feed (ModelPort port, OutputSlot& output) + { + UNIMPLEMENTED("build an active playback/render feed"); + } + }} // namespace proc::play diff --git a/src/proc/play/play-process.hpp b/src/proc/play/play-process.hpp index aebd971dd..ae8b8d3a6 100644 --- a/src/proc/play/play-process.hpp +++ b/src/proc/play/play-process.hpp @@ -56,12 +56,15 @@ //#include "include/display-facade.h" //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" +#include "proc/mobject/model-port.hpp" #include "proc/play/output-manager.hpp" +#include "lib/iter-source.hpp" #include "lib/util.hpp" // #include //#include //#include +#include namespace proc { @@ -72,6 +75,10 @@ namespace play { // using lumiera::Display; // using lumiera::DummyPlayer; using util::isnil; + using mobject::ModelPort; + + typedef proc::play::POutputManager POutputManager; + typedef lib::IterSource::iterator ModelPorts; namespace error = lumiera::error; @@ -83,10 +90,14 @@ namespace play { * bound to operate on a single type of media data only. */ class Feed - : boost::noncopyable { + public: + typedef lib::IterSource::iterator Connections; + + Feed (ModelPort, OutputSlot&); }; + /****************************************************** * Playback/Render process within the Lumiera Player. @@ -107,17 +118,13 @@ namespace play { class PlayProcess : boost::noncopyable { + std::vector outputFeeds_; + + PlayProcess (Feed::Connections pipeConnections); public: - template - PlayProcess (CONS pipeConnections) - { - if (isnil (pipeConnections)) - throw error::State ("creating a PlayProcess without any usable output connections" - , LUMIERA_ERROR_CANT_PLAY); - - UNIMPLEMENTED ("iterate over the connections and allocate/establish each of them, creating and storing Feed objects"); - } + static PlayProcess* + initiate (ModelPorts dataGenerators, POutputManager outputDestinations); }; diff --git a/src/proc/play/play-service.cpp b/src/proc/play/play-service.cpp index 802823631..aab3306b7 100644 --- a/src/proc/play/play-service.cpp +++ b/src/proc/play/play-service.cpp @@ -23,13 +23,12 @@ #include "proc/play/play-service.hpp" #include "proc/play/play-process.hpp" -#include "lib/itertools.hpp" #include "lib/util.hpp" #include //#include -#include +//#include #include //#include @@ -55,15 +54,11 @@ namespace play { //using std::tr1::bind; using lib::Sync; using lib::RecursiveLock_NoWait; - using lib::transformIterator; using std::tr1::weak_ptr; using std::tr1::bind; - using std::tr1::function; +//using std::tr1::function; using std::tr1::placeholders::_1; using util::remove_if; - using mobject::ModelPort; - - typedef proc::play::POutputManager POutputManager; namespace { // hidden local details of the service implementation.... @@ -135,30 +130,6 @@ namespace play { - namespace { // details... - - OutputSlot& - resolveOutputConnection (ModelPort port, POutputManager outputResolver) - { - REQUIRE (outputResolver); - OutputSlot& slot = outputResolver->getOutputFor (port); - if (!slot.isFree()) - throw error::State("unable to acquire a suitable output slot" /////////////////////TICKET #197 #816 - , LUMIERA_ERROR_CANT_PLAY); - } - - /** try to establish an output slot for the given - * global bus or data production exit point. - * @param outputResolver a facility able to resolve to - * a concrete output slot within the actual context - * @throw error::State when resolution fails - */ - function - resolve (POutputManager outputResolver) - { - return bind (resolveOutputConnection, _1, outputResolver); - } - } /** @@ -173,11 +144,10 @@ namespace play { * calculated media data to the outputs. */ Play::Controller - PlayService::connect(ModelPorts dataGenerators, Output outputDestinations) + PlayService::connect (ModelPorts dataGenerators, Output outputDestinations) { return pTable_->establishProcess( - new PlayProcess (transformIterator (dataGenerators, - resolve(outputDestinations)))); + PlayProcess::initiate(dataGenerators, outputDestinations)); } From f46cc268511e0d60e31d3faf96ae640702a29244 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 26 Jun 2011 03:59:03 +0200 Subject: [PATCH 040/108] factor out convenience shortcut transform iterator and wrap result into IterSource --- src/lib/iter-source.hpp | 31 +++++++++++++++++++++++++++++++ src/proc/play/play-process.cpp | 12 +----------- tests/40components.tests | 1 + tests/lib/iter-source-test.cpp | 24 +++++++++++++++++++++++- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/lib/iter-source.hpp b/src/lib/iter-source.hpp index 7910ecd5c..83f55e373 100644 --- a/src/lib/iter-source.hpp +++ b/src/lib/iter-source.hpp @@ -279,6 +279,14 @@ namespace lib { typedef typename IterSource::iterator Iter; }; + template + struct _TransformIterT + { + typedef typename _ProducedOutput::Type ResVal; + typedef TransformIter TransIter; + typedef typename IterSource::iterator Iter; + }; + template struct _PairIterT { @@ -329,6 +337,28 @@ namespace lib { } + /** pipes a given Lumiera Forward Iterator through + * a transformation function and wraps the resulting + * transforming Iterator, exposing just a IterSource. + * This convenience shortcut can be used to build a + * processing chain; the resulting IterSource will + * hide any involved detail types. + * @note as with any IterSource, there is one virtual + * function call for every fetched element. + */ + template + typename _TransformIterT::Iter + transform (IT const& source, FUN processingFunc) + { + typedef typename _TransformIterT::ResVal ValType; + typedef typename _TransformIterT::TransIter TransIT; + + return IterSource::build ( + new WrappedLumieraIterator ( + transformIterator (source, processingFunc))); + } + + /** @return a Lumiera Forward Iterator to yield * all the keys of the given Map or Hashtable */ @@ -424,6 +454,7 @@ namespace lib { } using iter_source::wrapIter; + using iter_source::transform; using iter_source::eachMapKey; using iter_source::eachDistinctKey; using iter_source::eachValForKey; diff --git a/src/proc/play/play-process.cpp b/src/proc/play/play-process.cpp index 305ec6d82..ca1417659 100644 --- a/src/proc/play/play-process.cpp +++ b/src/proc/play/play-process.cpp @@ -46,8 +46,7 @@ namespace play { using std::tr1::bind; using std::tr1::function; using std::tr1::placeholders::_1; - using lib::transformIterator; - using lib::wrapIter; + using lib::transform; Feed @@ -76,15 +75,6 @@ namespace play { return bind (resolveOutputConnection, _1, outputResolver); } - - - Feed::Connections - transform (ModelPorts dataGenerators, ConnectFunction outputResolution) - { - return wrapIter( - transformIterator (dataGenerators, outputResolution)); - } - } // (End) hidden service impl details diff --git a/tests/40components.tests b/tests/40components.tests index 999817faa..a81b77d3b 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -470,6 +470,7 @@ END TEST "Iterable data source" IterSource_test 13 < (arg[0]); verify_simpleIters(); + verify_transformIter(); verify_MapWrappers(); verify_MapWrappers(); @@ -203,6 +204,28 @@ namespace test{ } + void + verify_transformIter() + { + WrappedList customList(NUM_ELMS); + WrappedList::iterator sourceValues = customList.begin(); + + TimeIter tIt (transform (sourceValues, makeTime)); + CHECK (!isnil (tIt)); + pullOut (tIt); + CHECK (!tIt); + } + + /** transformation function, to be applied for each element: + * just build a time value, using the input as 1/4 seconds + */ + static TimeVar + makeTime (int input_sec) + { + return time::Time (time::FSecs (input_sec, 4)); + } + + template void @@ -280,7 +303,6 @@ namespace test{ CHECK (vals); pullOut (vals); // should produce anything between 1 and 100 entries CHECK (!vals); - } }; From 08df994fb1ad73c4ca6bab51c8b8a9ff21a2d22d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 26 Jun 2011 04:02:39 +0200 Subject: [PATCH 041/108] fix unit test broken by 7dad280 (14.6.2011) --- tests/lib/subsystem-runner-test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/lib/subsystem-runner-test.cpp b/tests/lib/subsystem-runner-test.cpp index 6db7ebf99..f15fe9fe2 100644 --- a/tests/lib/subsystem-runner-test.cpp +++ b/tests/lib/subsystem-runner-test.cpp @@ -76,6 +76,7 @@ namespace test { LUMIERA_ERROR_DEFINE( TEST, "simulated failure."); using error::LUMIERA_ERROR_LOGIC; + using error::LUMIERA_ERROR_STATE; @@ -400,7 +401,7 @@ namespace test { unit3.depends (unit2); SubsystemRunner runner(dummyOpt); - VERIFY_ERROR (LOGIC, runner.maybeRun (unit4) ); // failure to bring up prerequisites is detected + VERIFY_ERROR (STATE, runner.maybeRun (unit4) ); // failure to bring up prerequisites is detected CHECK ( unit1.isRunning()); CHECK ( unit2.isRunning()); CHECK (!unit3.isRunning()); From ea8841b7ad6c2da7084a46cc797f9bc610fce63f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 27 Jun 2011 03:09:38 +0200 Subject: [PATCH 042/108] analysis of a design problem and decision for the OutputSlot --- wiki/renderengine.html | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index ce2cd3f38..00dee8255 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3344,10 +3344,20 @@ The concrete OutputSlot implementation is owned and managed by the facility actu
 -----
 !Implementation / design problems
 How to handle the selection of those two data exchange models!
--- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. Yet I wanted to handle these through //generic programming// -- because they can't be subsumed under a single interface without distorting the usage or the structure.
+-- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. But, as it stands, the client needs to cooperate and behave differently to a certain degree. Unless we manage to factor out an common denominator of both models.
 
-Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy, note, at //compile time!//
+Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy?
+Solving this problem through //generic programming// -- i.e coding both cases effectively different, but similar -- would require to provide both implementation options already at //compile time!//
 
+{{red{currently}}} I see two possible, yet quite different approaches...
+;generic
+:when creating individual jobs, we utilise a //factory optained from the output slot.//
+;unified
+:extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
+!!!!discussion
+the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
+If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calulation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
+&rArr; conclusion: go for the unified approach!
 
From 650e73c45455b765307e2f1329a588cf25b25487 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 5 Jul 2011 00:56:57 +0200 Subject: [PATCH 043/108] OutputSlot: draft buffer handover protocol, remove the diferent models --- src/proc/play/output-slot.hpp | 17 +++++++---------- wiki/renderengine.html | 14 ++++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index 36f6de9b7..65fbb1c86 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -65,16 +65,17 @@ namespace play { /** established output channel */ class Connection; - class BufferHandoverSink - : public lib::Handle + class BufferProvider { public: - void emit(Time, BuffHandle); + ~BufferProvider() { } + + BuffHandle lockBufferFor(Time); }; - class SharedBufferSink + class DataSink : public lib::Handle { @@ -96,14 +97,10 @@ namespace play { public: virtual ~OutputSlot(); - typedef lib::IterSource::iterator Opened_BufferHandoverSinks; - typedef lib::IterSource::iterator Opened_SharedBufferSinks; + typedef lib::IterSource::iterator OpenedSinks; - template struct Allocation { - typedef typename lib::IterSource::iterator OpenedSinks; - OpenedSinks getOpenedSinks(); bool isActive(); @@ -114,7 +111,7 @@ namespace play { bool isFree() const; - Allocation + Allocation allocate(); private: diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 00dee8255..e218ca445 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3322,10 +3322,10 @@ Each OutputSlot is an unique and distinguishable entity. It corresponds explicit
 
 In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (yet some special kinds of external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
 
-Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The size and other characteristics of the data frames is assumed to be suitable. Typically this won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
+Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames is assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Yet the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
 
 !!!data exchange models
-Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. There are two different models how this data hand-over might be performed. On allocation of a slot, the client has to commit to one of them, allowing the output slot to adapt accordingly
+Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. Theoretically there are two different models how this data hand-over might be performed. This corresponds to the fact, that in some cases our own code manages the output and the buffers, while in other situations we intend to use existing library solutions or even external server applications to handle output
 ;buffer handover model
 :the client owns the data buffer and cares for allocation and de-allocation. The {{{emit()}}}-call just propagates a pointer to the buffer holding the data ready for output. The output slot implementation in turn has the liability to copy or otherwise use this data within a given time limit.
 ;shared buffer model
@@ -3354,10 +3354,16 @@ Solving this problem through //generic programming// -- i.e coding both cases ef
 :when creating individual jobs, we utilise a //factory optained from the output slot.//
 ;unified
 :extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
-!!!!discussion
+!!!discussion
 the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
 If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calulation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
 &rArr; conclusion: go for the unified approach!
+
+!!!unified data exchange cycle
+The nominal time of a frame to be delivered is used as an ID throughout that cycle
+# within a defined time window prior to delivery, the client can retrieve the buffer from the ''buffer provider''.
+# the client has to ''emit'' within a (short) time window pior to deadline
+# now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
 
From 7e3054df18384cd7c531929fc221d638ef652a43 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 8 Jul 2011 03:54:26 +0200 Subject: [PATCH 044/108] considering timing glitches in detail --- wiki/renderengine.html | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index e218ca445..7d50e9aa7 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3313,7 +3313,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3334,12 +3334,13 @@ Data is handed over by the client invoking an {{{emit(time,...)}}} function on t
 !!!timing expectations
 Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These timings are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
 
-The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run within certain limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is a job running within the engine scheduler and thus can be configured to run //after// another job has finished, and to run within certain time limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
 
 {{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
 
 !!!Lifecycle and storage
 The concrete OutputSlot implementation is owned and managed by the facility actually providing this output possibility. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. This implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be deregistered explicitly prior to destruction -- and this deregistration may block until all clients using this slot are terminated. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferrably just emitting a signal (callback functor).
+{{red{TODO 7/11: Deregistration is an unsolved problem....}}}
 
 -----
 !Implementation / design problems
@@ -3351,20 +3352,38 @@ Solving this problem through //generic programming// -- i.e coding both cases ef
 
 {{red{currently}}} I see two possible, yet quite different approaches...
 ;generic
-:when creating individual jobs, we utilise a //factory optained from the output slot.//
+:when creating individual jobs, we utilise a //factory obtained from the output slot.//
 ;unified
 :extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
 !!!discussion
 the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
-If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calulation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
+If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
 &rArr; conclusion: go for the unified approach!
 
 !!!unified data exchange cycle
 The nominal time of a frame to be delivered is used as an ID throughout that cycle
 # within a defined time window prior to delivery, the client can retrieve the buffer from the ''buffer provider''.
-# the client has to ''emit'' within a (short) time window pior to deadline
+# the client has to ''emit'' within a (short) time window prior to deadline
 # now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
-
+ +!!!lapses +This data exchange protocol operates on a rather low level; there is only limited protection against timing glitches +| !step|!problem ||!consequences | !protection | +| (1)|out of time window ||interference with previous/later use of the buffer | prevent in scheduler! | +|~|does not happen ||harmless as such | emit ignored | +|~|buffer unavailable ||inhibits further operation | ↯ | +| (2)|out of time window ||harmless as such | emit ignored | +|~|out of order ||allowed, unless out of time | -- | +|~|does not happen ||frame treated as glitch | -- | +|~|buffer unallocated ||frame treated as glitch | emit ignored | +| (3)|emit missing ||frame treated as glitch | -- | +|~|fail to release buffer ||unable to use buffer further | mark unavailable | +|~|buffer unavailable ||serious malfunction of playback | request playback stop | + +Thus there are two serious problem situations +* allocating the buffer out of time window bears the danger of output data corruption; but the general assumption is for the scheduler to ensure each job start time remains within the defined window and all prerequisite jobs have terminated successfully. To handle clean-up, we additionally need special jobs running always, in order, and be notified of prerequisite failures. +* failure to release a buffer timely blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole. +&rarr; SchedulerRequirements
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].
@@ -4999,6 +5018,17 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin
 
 &rarr; QuantiserImpl
+
+
The Scheduler is responsible for geting the individual render jobs to run. The basic idea is that individual render jobs //should never block// -- and thus the calculation of a single frame might be split into several jobs, including resource fetching. This, together with the data exchange protocol defined for the OutputSlot, and the requirements of storage management (especially releasing of superseded render nodes), leads to certain requirements to be ensured by the scheduler:
+;ordering of jobs
+:the scheduler has to ensure all prerequisites of a given job are met
+;job time window
+:when it's not possible to run a job within the defined target time window, it must be marked as failure
+;failure propagation
+:when a job fails, either due to an job internal error, or by timing glitch, any dependent jobs need to receive that failure state
+;guaranteed execution
+:some jobs are marked as "ensure run". These need to run reliable, even when prerequisite jobs fail -- and this failure state needs to be propagated
+
A link to relate a compound of [[nested placement scopes|PlacementScope]] to the //current// session and the //current//&nbsp; [[focus for querying|QueryFocus]] and exploring the structure. ScopeLocator is a singleton service, allowing to ''explore'' a [[Placement]] as a scope, i.e. discover any other placements within this scope, and allowing to locate the position of this scope by navigating up the ScopePath finally to reach the root scope of the HighLevelModel.
 

From a199bff92b89cee380ec7688062865a62fa14895 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 9 Jul 2011 02:17:37 +0200
Subject: [PATCH 045/108] BufferProvider as a frontend to buffer management

---
 .../{buffhandle.cpp => buffer-provider.cpp}   |  7 +-
 src/proc/engine/buffer-provider.hpp           | 72 +++++++++++++++++++
 src/proc/engine/buffhandle.hpp                | 21 +++---
 src/proc/play/output-slot.hpp                 |  2 +-
 wiki/renderengine.html                        | 27 ++++---
 5 files changed, 110 insertions(+), 19 deletions(-)
 rename src/proc/engine/{buffhandle.cpp => buffer-provider.cpp} (84%)
 create mode 100644 src/proc/engine/buffer-provider.hpp

diff --git a/src/proc/engine/buffhandle.cpp b/src/proc/engine/buffer-provider.cpp
similarity index 84%
rename from src/proc/engine/buffhandle.cpp
rename to src/proc/engine/buffer-provider.cpp
index 24573990e..e58168c9a 100644
--- a/src/proc/engine/buffhandle.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -1,5 +1,5 @@
 /*
-  BuffHandle  -  Buffer handling support for the render engine
+  BufferProvider  -  Abstraction for Buffer management during playback/render
 
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -21,11 +21,14 @@
 * *****************************************************/
 
 
-#include "proc/engine/buffhandle.hpp"
+#include "proc/engine/buffer-provider.hpp"
 
 namespace engine {
 
 
+  BufferProvider::~BufferProvider() { }
+  
+  
   /**  */
 
 
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
new file mode 100644
index 000000000..981bfe799
--- /dev/null
+++ b/src/proc/engine/buffer-provider.hpp
@@ -0,0 +1,72 @@
+/*
+  BUFFER-PROVIDER.hpp  -  Abstraction for Buffer management during playback/render
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+/** @file buffer-provider.hpp
+ ** Abstraction to represent buffer management and lifecycle within the render engine.
+ ** It turns out that --  throughout the render engine implementation -- we never need
+ ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed,
+ ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementatino detail.
+ ** The code within the render engine just pushes around BufferHandle objects, which act as a front-end,
+ ** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle
+ ** of buffers automatically, because the use of buffers is embedded into the render calculation cycle,
+ ** which follows a rather strict protocol anyway. Relying on the capabilities of the scheduler,
+ ** the sequence of individual jobs in the engine ensures...
+ ** - that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
+ ** - that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
+ ** - that buffers are marked as free ("released") after doing the actual calculations.
+ ** 
+ ** @see state.hpp
+ ** @see output-slot.hpp
+ */
+
+#ifndef PROC_ENGINE_BUFFR_PROVIDER_H
+#define PROC_ENGINE_BUFFR_PROVIDER_H
+
+
+#include "lib/error.hpp"
+#include "proc/engine/buffhandle.hpp"
+
+#include 
+
+
+namespace engine {
+  
+  
+  
+  /**
+   * Handle for a buffer for processing data, abstracting away the actual implementation.
+   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * 
+   * @todo as of 6/2011 buffer management within the engine is still a bit vague
+   */
+  class BufferProvider
+    : boost::noncopyable
+    {
+      
+    public:
+      virtual ~BufferProvider();  ///< this is an interface
+    };
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 0f6378396..3d7bcbfac 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -24,12 +24,17 @@
  ** Various bits needed to support the buffer management within the render nodes.
  ** When pulling data from predecessor nodes and calculating new data, each render node
  ** needs several input and output buffers. These may be allocated and provided by several
- ** different "buffer providers" (for example the frame cache). For accessing those buffers,
- ** the node needs to keep a table of buffer pointers, and for releasing the buffers later
- ** on, we need some handles. The usage pattern of those buffer pointer tables is stack-like,
- ** thus it makes sense to utilise a single large buffer pointer array per pull() calldown
- ** sequence and dynamically claim small chunks for each node.
- **
+ ** different "buffer providers" (for example the frame cache). Typically, the real buffers
+ ** will be passed as parameters to the actual job instance when scheduled, drawing on the
+ ** results of prerequisite jobs. Yet the actual job implementation remains agnostic with
+ ** respect to the way actual buffers are provided; the invocation just pushes BuffHandle
+ ** objects around. The actual render function gets an array of C-pointers to the actual
+ ** buffers, and for accessing those buffers, the node needs to keep a table of buffer
+ ** pointers, and for releasing the buffers later on, we utilise the buffer handles.
+ ** The usage pattern of those buffer pointer tables is stack-like, thus the actual
+ ** implementation utilises a single large buffer pointer array per pull() call
+ ** sequence and dynamically claims small chunks for each node.
+ ** 
  ** @see nodewiring-def.hpp
  ** @see nodeoperation.hpp
  ** @see bufftable.hpp       storage for the buffer table
@@ -105,11 +110,11 @@ namespace engine {
   /**
    * Buffer Type information.
    * Given a BufferDescriptor, it is possible to allocate a buffer
-   * of suitable size and type by using State::allocateBuffer().
+   * of suitable size and type by using BufferProvider::allocateBuffer().
    */
   struct BufferDescriptor
     {
-      lumiera::StreamType& sType_;
+      long typeToken_; 
     };
   
   
diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp
index 65fbb1c86..b5d0c3d45 100644
--- a/src/proc/play/output-slot.hpp
+++ b/src/proc/play/output-slot.hpp
@@ -39,7 +39,7 @@
 #include "lib/error.hpp"
 #include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
-#include "proc/engine/buffhandle.hpp"
+#include "proc/engine/buffer-provider.hpp"
 #include "lib/iter-source.hpp"
 //#include "lib/sync.hpp"
 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 7d50e9aa7..1826d59ef 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1077,6 +1077,17 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''buffer handles'', which act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
+* that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
+* that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
+* that buffers are marked as free ("released") after doing the actual calculations.
+
+__see also__
+&rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
+&rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
+
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
 {{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
@@ -3313,7 +3324,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3357,12 +3368,12 @@ Solving this problem through //generic programming// -- i.e coding both cases ef
 :extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
 !!!discussion
 the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling.
-If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the buffer provider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
+If we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the BufferProvider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution is like extending the shared buffer model to both cases.
 &rArr; conclusion: go for the unified approach!
 
 !!!unified data exchange cycle
 The nominal time of a frame to be delivered is used as an ID throughout that cycle
-# within a defined time window prior to delivery, the client can retrieve the buffer from the ''buffer provider''.
+# within a defined time window prior to delivery, the client can ''allocate and retrieve the buffer'' from the BufferProvider.
 # the client has to ''emit'' within a (short) time window prior to deadline
 # now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
 
@@ -4893,7 +4904,7 @@ At first sight the link between asset and clip-MO is a simple logical relation b
 [img[Entities comprising the Render Engine|uml/fig128389.png]]
 
-
+
Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
 
 !Layered structure of State
@@ -4923,7 +4934,7 @@ We //do employ//&nbsp; some virtual calls for the buffer management in order
 * only output buffers are allocated. It is //never necessary//&nbsp; to allocate input buffers!
 * buffers are to be allocated as late as possible, typically just before invoking {{{process()}}}
 * buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility of writing the result to cache.
-* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StaeProxy.
+* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StateProxy.
 * any other buffers filled with results in the course of the same {{{process()}}} call can be released immediately before returning from the {{{pull()}}}
 * similar, and input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
 * buffers are allways released by calling to the "current state" (which is a StateProxy), providing the buffer-ID to be released
@@ -5497,11 +5508,11 @@ if (oldText.indexOf("SplashScreen")==-1)
 
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
-
+
An Object representing a //Render Process// and containing associated state information.
-* it is created in the Controller subsystem while initiating the BuildProcess
+* it is created in the Player subsystem while initiating the RenderProcess
 * it is passed on to the generated Render Engine, which in turn passes it down to the individual Processors
-* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
+* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of state synchronisation and management if necessary. (thus the name Proxy)
 * in a future version, it may also encapsulate the communication in a distributed render farm
 
From 7adb8149db3036b752e3ffb0874aeda2f324e3f9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 9 Jul 2011 02:44:24 +0200 Subject: [PATCH 046/108] back to test-driven brainstorming again --- src/proc/engine/buffer-provider.hpp | 6 ++ src/proc/engine/buffhandle.hpp | 2 +- src/proc/play/diagnostic-output-slot.hpp | 73 +++++++++++++++++++ src/proc/play/output-slot.hpp | 11 +-- .../proc/play/output-slot-protocol-test.cpp | 66 +++++++++++++++++ 5 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 src/proc/play/diagnostic-output-slot.hpp create mode 100644 tests/components/proc/play/output-slot-protocol-test.cpp diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 981bfe799..907cb9806 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -64,6 +64,12 @@ namespace engine { public: virtual ~BufferProvider(); ///< this is an interface + + ///////TODO: is there any generic way to obtain a BufferDescriptor; then we should expose it here... + + virtual BuffHandle lockBufferFor (BufferDescriptor const&) =0; + virtual void releaseBuffer (BuffHandle const&) =0; ////////TODO not quite sure what information to pass here + }; diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp index 3d7bcbfac..d7c5ecd87 100644 --- a/src/proc/engine/buffhandle.hpp +++ b/src/proc/engine/buffhandle.hpp @@ -110,7 +110,7 @@ namespace engine { /** * Buffer Type information. * Given a BufferDescriptor, it is possible to allocate a buffer - * of suitable size and type by using BufferProvider::allocateBuffer(). + * of suitable size and type by using BufferProvider::lockBuffer(). */ struct BufferDescriptor { diff --git a/src/proc/play/diagnostic-output-slot.hpp b/src/proc/play/diagnostic-output-slot.hpp new file mode 100644 index 000000000..e0e4a7f0a --- /dev/null +++ b/src/proc/play/diagnostic-output-slot.hpp @@ -0,0 +1,73 @@ +/* + DIAGNOSTIC-OUTPUT-SLOT.hpp - helper for testing against the OutputSlot interface + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + +/** @file diagnostic-output-slot.hpp + ** An facility for writing unit-tests against the OutputSlot interface. + ** + ** @see output-slot-protocol-test.cpp + */ + + +#ifndef PROC_PLAY_DIAGNOSTIC_OUTPUT_SLOT_H +#define PROC_PLAY_DIAGNOSTIC_OUTPUT_SLOT_H + + +#include "lib/error.hpp" +#include "proc/play/output-slot.hpp" +//#include "lib/sync.hpp" + +//#include +//#include +//#include +//#include +//#include + + +namespace proc { +namespace play { + +//using std::string; + +//using std::vector; +//using std::tr1::shared_ptr; +//using boost::scoped_ptr; + + + + /******************************************************************** + * Helper for unit tests: Mock output sink. + * + * @todo write type comment + */ + class DiagnosticOutputSlot + : public OutputSlot + { + public: + + private: + + }; + + + +}} // namespace proc::play +#endif diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index b5d0c3d45..d784bad85 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -28,7 +28,7 @@ ** possibilities can be added and removed dynamically from various components (backend, GUI), ** all using the same resolution and mapping mechanisms ** - ** @see output-test-slot.hpp ////TODO + ** @see diagnostic-output-slot.hpp ////TODO */ @@ -54,6 +54,7 @@ namespace proc { namespace play { using ::engine::BuffHandle; + using ::engine::BufferProvider; using lib::time::Time; //using std::string; @@ -65,14 +66,6 @@ namespace play { /** established output channel */ class Connection; - class BufferProvider - { - - public: - ~BufferProvider() { } - - BuffHandle lockBufferFor(Time); - }; class DataSink diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp new file mode 100644 index 000000000..2bdc7b09d --- /dev/null +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -0,0 +1,66 @@ +/* + OutputSlotProtocol(Test) - covering the basic usage cycle of an output slot + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "lib/test/run.hpp" +//#include "lib/util.hpp" +#include "proc/play/diagnostic-output-slot.hpp" + +//#include +//#include + +//using boost::format; +//using std::string; +//using std::cout; + + +namespace engine{ +namespace test { + +// using lib::AllocationCluster; +// using mobject::session::PEffect; + + + namespace { // Test fixture + + } + + + /******************************************************************* + * @test basic render node properties and behaviour. + */ + class OutputSlotProtocol_test : public Test + { + virtual void + run (Arg) + { + UNIMPLEMENTED ("build a mock output slot and perform a full lifecycle"); + } + }; + + + /** Register this test class... */ + LAUNCHER (OutputSlotProtocol_test, "unit player"); + + + +}} // namespace engine::test From 691ab4b8b5b8c725702749108dfb52ce270155ab Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 10 Jul 2011 18:18:20 +0200 Subject: [PATCH 047/108] WIP draft simple OutputSlot usage --- .../proc/play/output-slot-protocol-test.cpp | 89 ++++++++++++++++++- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp index 2bdc7b09d..c83d6bff3 100644 --- a/tests/components/proc/play/output-slot-protocol-test.cpp +++ b/tests/components/proc/play/output-slot-protocol-test.cpp @@ -24,6 +24,7 @@ #include "lib/test/run.hpp" //#include "lib/util.hpp" #include "proc/play/diagnostic-output-slot.hpp" +#include "proc/engine/buffhandle.hpp" //#include //#include @@ -33,11 +34,13 @@ //using std::cout; -namespace engine{ +namespace proc { +namespace play { namespace test { // using lib::AllocationCluster; // using mobject::session::PEffect; + using ::engine::BuffHandle; namespace { // Test fixture @@ -46,7 +49,11 @@ namespace test { /******************************************************************* - * @test basic render node properties and behaviour. + * @test verify the OutputSlot interface and base implementation + * by performing full data exchange cycle. This is a + * kind of "dry run" for documentation purposes, + * both the actual OutputSlot implementation + * as the client using this slot are Mocks. */ class OutputSlotProtocol_test : public Test { @@ -54,7 +61,81 @@ namespace test { run (Arg) { UNIMPLEMENTED ("build a mock output slot and perform a full lifecycle"); - } + verifyStandardCase(); + } + + + void + verifyStandardCase() + { + // Create Test fixture. + // In real usage, the OutputSlot will be preconfigured + // (Media format, number of channels, physical connections) + // and then registered with / retrieved from an OutputManager + OutputSlot& oSlot = DiagnosticOutputSlot::build(); + + // Client claims the OutputSlot + // and opens it for exclusive use. + Allocation alloc = oSlot.allocate(); + + // Now the client is able to prepare + // "calculation streams" for the individual + // Channels to be output through this slot. + OutputSlot::OpenedSinks sinks = alloc.getOpenedSinks(); + DataSink sink1 = *sinks++; + DataSink sink2 = *sinks++; + + // within the frame-calculation "loop" + // we perform an data exchange cycle + int64_t frameNr = 123; + BuffHandle buff00 = sink1.lockBufferFor (frameNr); + BuffHandle buff10 = sink2.lockBufferFor (frameNr); + buff00.create(); + buff10.create(); + + // rendering process calculates content.... + buff00.access() = testData[0,0]; + + // while further frames might be processed in parallel + BuffHandle buff11 = sink2.lockBufferFor (++frameNr); + buff11.create(); + buff11.access() = testData[1,1]; + buff10.access() = testData[1,0]; + + // Now it's time to emit the output + sink2.emit (frameNr-1); + sink2.emit (frameNr ); + sink1.emit (frameNr-1); + // that's all for the client + + // Verify sane operation.... + DiagnosticOutputSlot checker = DiagnosticOutputSlot::access(oSlot); + CHECK (checker.buffer_was_used (0,0)); + CHECK (checker.buffer_unused (0,1)); + CHECK (checker.buffer_was_used (1,0)); + CHECK (checker.buffer_was_used (1,1)); + + CHECK (checker.buffer_was_closed (0,0)); + CHECK (checker.buffer_was_closed (1,0)); + CHECK (checker.buffer_was_closed (1,1)); + + CHECK ( checker.emitted (0,0)); + CHECK (!checker.emitted (0,1)); + CHECK ( checker.emitted (1,0)); + CHECK ( checker.emitted (1,1)); + + DiagnosticOutputSlot::OutFrames stream0 = checker.getChannel(0); + DiagnosticOutputSlot::OutFrames stream1 = checker.getChannel(1); + + CHECK ( stream0); + CHECK (*stream0++ == testData[0,0]); + CHECK (!stream0); + + CHECK ( stream1); + CHECK (*stream1++ == testData[1,0]); + CHECK (*stream1++ == testData[1,1]); + CHECK (!stream1); + } }; @@ -63,4 +144,4 @@ namespace test { -}} // namespace engine::test +}}} // namespace proc::play::test From 9f7a46110b7ce0ce3f55d1ee29da1add4bb00428 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 11 Jul 2011 00:12:18 +0200 Subject: [PATCH 048/108] fix labels in UML --- doc/devel/uml/fig144005.png | Bin 15144 -> 15263 bytes uml/lumiera/133637 | 16 ++++++++++++---- uml/lumiera/144005.diagram | 22 +++++++++++----------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/doc/devel/uml/fig144005.png b/doc/devel/uml/fig144005.png index 93627a5ac9bd816faa12a234164a0fdbff947f54..6ca73fc491d307d8571546033fe88bf9535039c6 100644 GIT binary patch literal 15263 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 diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 index d788a91a9..6336ed674 100644 --- a/uml/lumiera/133637 +++ b/uml/lumiera/133637 @@ -1,6 +1,6 @@ format 58 "Play" // ProcessingLayer::Play - revision 1 + revision 2 modified_by 5 "hiv" // class settings //class diagram settings @@ -273,7 +273,7 @@ ${inlines} end end - classinstance 146181 "video" + classinstance 146181 "video-L" type class_ref 176901 // CalcStream attributes end @@ -281,7 +281,7 @@ ${inlines} end end - classinstance 146309 "video" + classinstance 146309 "video-R" type class_ref 176901 // CalcStream attributes end @@ -305,7 +305,15 @@ ${inlines} end end - classinstance 146693 "sound-Z" + classinstance 146693 "sound-Y" + type class_ref 176901 // CalcStream + attributes + end + relations + end + end + + classinstance 148357 "sound-Z" type class_ref 176901 // CalcStream attributes end diff --git a/uml/lumiera/144005.diagram b/uml/lumiera/144005.diagram index 42a499609..8a28bdf33 100644 --- a/uml/lumiera/144005.diagram +++ b/uml/lumiera/144005.diagram @@ -12,10 +12,10 @@ end classinstancecanvas 128389 classinstance_ref 146053 // xyz 171 240 2000 end -classinstancecanvas 128517 classinstance_ref 146181 // video +classinstancecanvas 128517 classinstance_ref 146181 // video-L xyz 233 194 2000 color lightgreen end -classinstancecanvas 128645 classinstance_ref 146309 // video +classinstancecanvas 128645 classinstance_ref 146309 // video-R xyz 233 216 2000 color lightgreen end classinstancecanvas 128773 classinstance_ref 146437 // sound-W @@ -24,12 +24,9 @@ end classinstancecanvas 128901 classinstance_ref 146565 // sound-X xyz 233 291 2000 color lightgreen end -classinstancecanvas 129029 classinstance_ref 146693 // sound-Z +classinstancecanvas 129029 classinstance_ref 146693 // sound-Y xyz 233 313 2000 color lightgreen end -classinstancecanvas 129157 classinstance_ref 146693 // sound-Z - xyz 233 335 2000 color lightgreen -end classinstancecanvas 132101 classinstance_ref 146821 // xyz 420 269 2000 color lightblue end @@ -46,6 +43,9 @@ end classinstancecanvas 132741 classinstance_ref 147205 // xyz 233 46 2000 end +classinstancecanvas 132869 classinstance_ref 148357 // sound-Z + xyz 233 335 2000 color lightgreen +end objectlinkcanvas 129285 norel geometry VH from ref 128133 z 1999 to point 139 181 @@ -81,14 +81,14 @@ objectlinkcanvas 130949 norel from ref 128389 z 1999 to point 194 321 line 131077 z 1999 to ref 129029 no_role_a no_role_b -objectlinkcanvas 131205 norel - geometry VH - from ref 128389 z 1999 to point 194 343 - line 131333 z 1999 to ref 129157 - no_role_a no_role_b objectlinkcanvas 131845 norel geometry VH from ref 128005 z 1999 to point 68 150 line 131973 z 1999 to ref 128133 no_role_a no_role_b +objectlinkcanvas 132997 norel + geometry VHr + from ref 132869 z 1999 to point 194 343 + line 133253 z 1999 to ref 128389 + no_role_a no_role_b end From f8842c75ed4e4650fe58a8490267eb96363b9ebc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 11 Jul 2011 02:57:16 +0200 Subject: [PATCH 049/108] WIP draft OutputSlot internal transitions --- doc/devel/uml/fig145157.png | Bin 0 -> 13381 bytes src/proc/engine/buffer-provider.hpp | 8 ++ src/proc/play/output-slot.hpp | 10 ++- uml/lumiera/133637 | 52 ++++++++++- uml/lumiera/145157.diagram | 130 ++++++++++++++++++++++++++++ uml/lumiera/5.session | 4 +- uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 35 +++++++- 8 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 doc/devel/uml/fig145157.png create mode 100644 uml/lumiera/145157.diagram diff --git a/doc/devel/uml/fig145157.png b/doc/devel/uml/fig145157.png new file mode 100644 index 0000000000000000000000000000000000000000..dac8e23cb5105c210433a1973005e52182184529 GIT binary patch literal 13381 zcmbumcOaGh|37|093ze`8Rt;;mX&dY%(C|u+1VoT%(6OLKBo{ zKSLj(1Pxjl5AI;Us#2=-j{9j%I6*PsA$a1ssd=63oqU}g1>eKOXUd3O?4lUs0aA2o z-`TvQjT$D>A|=)RTlxrQw!NcAEDLkw`^`qG{Up8bC|!^!HxVr7Nk-OpO7L&03=UYG z1sv-Nm zB8+IHW9U#e9|0Z&(iZEBBaqfB?nM_BarD4rSJr;-XK1Ldml*19&r$cor)O!I zd2k41l;sGz+ktG_o(;26&ERmb|A{%ptOlH|vL3l#DK;y1708Odp(3euHJ?UNBaBP^uzqH-u(X;(Pl0+c$=d5J8_WpH8iZ3rD6K^6V`B z);LY5#Flhu@249gFP%n9esrbRe#+OSWJI2?6&PqCj}|6&7RPx_C4Tkj!Z~o;Bhj&h z)J-p9%o^Pmu~u=Mn+(MHTs(GvX-p8ZVk82Z95yB<%r1_LEQUIcun*!Aw#3x^{KODB z+4J?+1)UbDeL0l+V^PZh*edh*?rjv9sBj`dnf{m zoZ+#s5JSh>Wckh4E)P%oVjiGl%}yV_HO)EzPpp+?<^|^%^#nhxy=S~E*SOF$EinLzTQa1h&*X0$(rXd=Sx>p>e9*|vA^wu+wK!tDDC1%GMm!76iMBj z$v-c=U-+3-6>3no%fDZ4X_k(ht~3wS&F)fBG4kGDyT?DFMisWf8P{etP$$(E7S-;A zNnZmVKW?-Vl)Ru(EPQeLD_bDL=`(YTgrwEn!4&Ek)7O0ezHvfk)Arow0Dn>j{~D{+ z;q(|Oj`4=0^P&7K7SSHE>s4VTG}coeG*-N)8&~=jHmj4H%68Za_hGL=xS zej&0K(j!HCq%yk2=K&W-S>geQt%^`F*S9NsBQeuYSNo2--l<(Azk4T{=>L7{{CAPb zMHlx0rhLL%A`@pKL{j3F)R|e+;oD%NCmY5cKk}MqSj&AN9?Kc-P{j4?5f2_vhuwGN zt4MlTbTJoscJ=B3x6!_6bwlk@w+}`e9eae)$?_YPiub#sW{!eJ4v_RL%~w_ieC@mF z>Ou$4TcYrW38`7!d^C^mdV4SO2Bdy5bC~4!KkBaVo|ZgozBoDH)GI2ZW``oO0?vnm zuHr9rO#QkaQ55Hb^E?uj0@GA?F|)V#csNI!yAv1`WK!I?U$2qvU#5jRpei!Jbkw@L zmzM{KJpReO??y;i-b;$xF2vbn z`np|A)RHoD_4CUwbjT64kNUpM3S)u(L^;9Rdr+s_9b+Rko^+07BYI(}T@ubx4P^A= zX%hpoa!xgp${&}dr7rFUc|>n*bq@CBfALmuk=j?>iQ8q%lZQk4+p?ax5J{q$XH%g7 zX6(ZMfDL|B+tFwWI|Neli6QV%Yp*6N0|H~E;|{d^g`fg&L2|GZRAy+Nh6@?vT|yqz zhxij;)U(|BsQPJwf{0UG8wiXNkrmOx_b`AdSm^F=t|g(&e-aLrTcNyWDc+7O1h z9y4t4+b%c6@XXZ`j<%y0M17<~{{?ofiWa&gkQ&_OKkMO_hkHT#k5=oRHcq7ytv6nU zK<bNKY#eNI_EVH`1JBHqSA}%GR z=E?RPXcw1mN`lWvm}P1%{n!1r0|Nu!G9|ltY-rxSehrt9kU%1{VDU*w_n<3lYYV~! zA3mgrdpp_N+Z!9xojg!yqreSDu(R83Ow>Az6g^59>oGPY3Z+hawSQY!*LQEo8^Co% zs`b2l_jK4|fuWA?Ov`W#^4#5EEL11|yoIO1zwryRXGj+9PDz*RLw-8#%atb4{KkJP zAJYCZGd%{&2{f*Jm;0G{j>I38j2AMceJ?)?Nz**3pb#?6#8m5dizv#`BO!8|rd$KWM zCL_ZFN?j?Jpx^c_6Z3m&iV1}kyo0GU8ECD?%C%LUM1MY?og4tZ7YEJF4#JGh;9dVJ zG%UmEp8n{%kB>@C2ygT0uVVkC;Sp~%XR^uOVGMd*HOSJtxwKTHP?wdJ^?goG34PQ~ z-L;dsDb(gIv~itB1gy*lv$U|V@adCW)!-dPM-wVUPDu$L2Z#Oo&&qt=!dH^0{dN+h zBX3_-RaO0FllZM$w}gZ$^YS8$oHBwN6Ypg3aB*?*^2V6uPu6?qYjZ@)lV87XpssGN zVIdRVh&#KyJF#o_(b!jsWd6ArW2n(D_AO9W~q8 z>-zfm#)u>pYxg0Wtj5GN)aKfl)^e}b(C~Q|6V}kk=EjOocVAzOA87X3wh$!|r-nWf zWVp2N+D1o5hn|j(hB7#1Vr*=S*`u={*v>ccO?BmIzzfd#Up7)xJtCDcG%~8MuOFkH z_B+VP%F1e#Sf}qZSmrBWMi%NC>FFu3uWxKvJCBuqsBOe8?dYB7nwU9Hgay< zY9qAlc=6c5K}Au~Tw$Mync2bKzO1b51-qYTVA#iP8+MPtPCw~IxZ^S z3~g@S@{{j*m6o>Lo2CFwMF#kLc?kzlj->c{Px-JSZZb17v$EQBej1jMl|AAeEp%M+ zIo@3v9o65jY;JB2rd!L^&eJ~SY1PahdfMne_bFn{BHi=ax=?uU#2RT=HOYkP;i~lF z;^KJKV-+Q(ypJEh_Vg?i|E%}kd?n&)X<5bV!J&hlh)EdwcsBU<8}Z z%{e(}lN!{)#>H{*#q*e$;-Vr3-6gJV-z!BXtL6Fw)5xt+OO$SraclxcTdC{Sz`$N@ z?E%@;*-@fqT7H%Dw4N4*y`8Umav$S5PeCAc@`RqyI&S&Sh&Q~e!5@XoB?|EcyG0q zg@BOoWT%@KAK@@uprovPdH0Hoz0*hQxE?w*e6t3ZQ&)GV(y*5Y`E>WgUTFJ+u3>wi zdrC`7BdK{oKi^kUV&~ym(o2CoEWcae?mqDmE~e`5@wXk?WrwLJ|1$Jnu-$@HOEO3o% zmKqXp?AJ4tuXii%{XxXmI9?|F^ghm}%PfPN?-dA6pmUZl)R%anH_VGDpUd}(wDtl< z!9WW2o1V#8Es!~OyK%)bp zI6&t~|C`W+1wtUwG=CjH4yZdAF4I3BM8APmQg51CrbO+d zZmJOhJisC3;2NVfuGx(`y}i9)ibcz3ul)Xfe2VF7GywWOMh1trYG-F>baeE`j~^dC zd}wQviL3?P&+55rui4*WaQ-3yr#a3Uny66%gf53h>VPk<-RPl z67xX$Y$X*HanwFnnuUSESo|%OQL9If9+^(+fHS;p+qPbD;!?>U8Xg+bPzICpdg$!3 z8Z-hF;>V9~6B+J!uptnEny>y7Sm_-zGc%_~M-|y0+u4D-eW=%)E>>P%o}l@3Wgs^; z7FN9$6lQ2Tx^Yu9W}~TRoXVI*yU(@EIF#Uk@afvLSyz&dfx*$28_xIBMf<0x&5B3L z$I6ZbiL%w%c+8p#)Y+os=W~=2z}L}I%duOsPEw#Wv->+)Fb4;YruuX_T!?X)Ztl3l<=~~_E;G_#?ZbsekS~$8N!!p;N>H)7SrSHVmJf z1&u~SO{f)ehCVup>`dA)!xkT?ZW|lG$(fLN!1Sq(^U1H{#3Gd$T@C-$0ym)v!H(sK zyC@TQRv$%GhZWSCJV9kbiB*gwlg~01tJirAqwe_bBE&@+{KA! zrgMnA-bT`i2bwh;e)UA)SKKE2@q%tnnGPvpX33Teu;E;e@<>LK!TSrO;8CpZlt|>MN3Jk zqpzYJrw_`Av+8|;&(GrgpO%$sUginIlAAfXE!mw_>CuGvK)yMXLyX=9gYEeZ%L*HNr8$pM3(z*1Sn!iCZt@&tx zWPl=SYilRkKpjtcPx}ElTQdp>2#^UkEF50lBWG3JJvexelypb>9ar3Yh`Mv<&b#}m zb8ybId?H*~J3GJ@HX9G7${uw$o$VW)vv4GR?d$7v6fSoLkFxprcYafoREN+Nh>g!} zqB~3d1N1gLYcEh)-gSZZ0Bz_I0~T6tMMcGKlAh@-nVg*5Z6P5I(`rHuvq>*F?! zV902J+Zr4k3<^dKdA56cgsG*U^$NQfwi3vF_f87aBiPl|#b7UQ;{qh$MpTnz^pw8w1q!oU zg}4w{tZ!f7D1z7@His+e>xmC}_MuY*3zUBkcd6?94HXy~B8|3^BnU(H0&6_p6NicC zDk9WEn?uLLqe3(9$BG9~Fv1Bddrwhf07`Fy7f?qmEW^XYZeu^W9P~Z0A+fMisqZBY zZMQZyH^GR1WM#Eb^>{o~C$KAll@Zj$aDhPnx?xjMwOF$c z2{*2s2tJcM^n>HOtdkQ!p==x+Kbr#re0+Qy9UZ~2(bm?sus|Sga&kt(2FAwh8lMBR z_Wpwh51gHyeSJ^E82vkWwAm=uJ_ZE^ZEd;ikK8lBBc{`@^T=yV;D$gV+QhzxyEXZr z9rpoga(a5YwzdYwFhF4=r`pes!tPxjcQW=qE-ftq?EwVIS?!bZXn_;Qb2Ea5jjAz6Yno6Cn({FTi#D89opS5mryI4) z0s;cBUcGwBW#Bg3cC}3$@1fxG)T`d5%Exqv zX^EJa7#9ae;%s*iv)sqJ&kW1`@$YT&(BYce0<%WAG+ZWJdcXUnHBS6ngpDUNRx};c z<++(*Mc7?f4DR^gGRH+FEISCH0Cet1pGEp^G!aS$@Urqh7HyLQEI=7T_e9BiT5|T zUZAk?cbL3$t(}h}i31Gb+qXR~&d-Fazkd7nA|_@N_&cUV2H*c7wKn#|5V&E~vt?T% zW~-IAQCWHS_3} zik`%xrPS48k2bKk#Ev{$#A3_A#0SC%0E6vq+~7diz{G@a2fg=A78V}UhQqH(oQ`GK zc*I;LbrsFcS>xXwg#jkJeu=uiX0)Llfhq++xc?)}&-MKD=qe$V?d?}!?w8y2xkXeO zuGe|40_X$fCUXh69Gsk(FPYeoCs4{4A}Wj)2I$zs!$Uw0l~>-#h5@p`mJ5a_n8S$! z8RFgyQobp$GVjf)O9-oR4IY1vnZ7uirV>IRO>b7{|0g}38`jm;)l>!qb8Ku(iMMSqyMF#>Xpj{rC164jDb{9Ux9cG{EDVU+*(jcOC-ZVr5M=j@+6~+UU%l4!z3`Kl zTJabQ^7F<0jy=@XVR~%2yMRz~>K6R^{dB6S#{jLzBys2>#*caVKna7%kysqDalWg2KXEm)$Hc zCkK-O#6m6O&OInQC#U$6EjYreFH+g-M|Ra5vZYEV}oaHb7O3@ug6%MDS?EY$<3ovp2U{t&pq z_LV=)`P7wHS67GZD-8>qK=VIEp~fWe@bD565|GEgn7#5-{=C51UM44(8=UcM=_o~56HDFykU4KvOA~@I zm=J?4_STdq4{#UG_G^ezFEQ0Unwlx$VAl@7Ex`i|;GK3 z@?|sC9ov-`g4A_GS7+ReYV&&)Qp&+cCIy^b^pkcCoiZKlxAenyZO2f(T-_1*pM$7^vK^uT*DeLqw%9l7f z-p{4!4?t%&DYBb7A&=ZL|GqgJ^rw9z;)RGdT~D{E*G5sk2aOkTK4C`osIy~Ff88~s zYQ1)*2Y?p?W5?TDBzG5RAg%l%M;He?yFrC*f2G3+TW*&Bv7Nqt%9!Jl@aFH|&!eLy zz1C8K-D*PsM0LO9=H%d@-PjOS`cO1yRU0lX71*F5gRXJ z0a%@hnlhd59;9JmV*^-`&yI+S;$mYnPau`?xMQNPuMa4EZbd_b1UtJ@c7IQ*;PRMF z)+2I#tD(`+PE%Uo(^xPrIb_6m3OPD`F0~y6!t(g|>BeId6B7@Q+qoWKVrBX5U$1V+ z%d;~#e>T}rUM|SN@v*9E*fevPurEV=xwif)@MMysp$pvM(xx+Cptlze(G{;=1w7O< zFvuISU;X`?Eth|rJ^%ut__G%P@WqKi)W0$F#DSR^bFU^PXXi~|PuAQ&G%%QEvi@sL zk4$uiFSRx~{VL18!dbQ*s&1s0JwvUY|lz}2nA@P9jh{*L9Ln{<>1cO z%Y>xS#^=(XL$}V?_74s&_KE`gur2Q2&ETZ(&S@>8~mG*Kxa?B5q|s?iy^VI&>E5aD!U$6UEx*O^9Md zHs$9>YW^G;cv$)z6_=IOm+4zkRfUU(r)7#WH_-9TFnb$$HYO0v$Z|z*o)cAN!P0hE1 zD_p|(BqVC8stic~Q}B3{lp=ZyzIJzGc6OdWe@+vc_u+#}8Lh0-=LdR%G(^DWXF^+9 zSrHKt!DQUr-9a8F3uR|x<4fxWDwC*v!ej6Oh$w%4(8xYa1zAA@=9@Tg!J@XsY}F$?Fz5Pex3tk|3B+*cIp!kEF-gKUw;i}PHvo2{$ygh(7f zA6oDf`A=b&4qRS>?IGfiVMg)CkPGGQ`|@&w=$@K1;5(y-jyPpBG)YHV_rKFZ{*2K7 zVky2}fI9tlSt0gi00scf>;Qe|75Ul!L3Cm}z%)V(@)2cg=Dp3%PEyWQeZRW7=`98v zv^?0(&N~s_B7!G~k<_=fw*er604pjgYNG0Knu9@YzVcj`E{8_Wt8*&Mfj0tE7w0X78U@v|9+|fa2&_GzqWEfAne;#6m&r)xmwEe^Ydfw zuYdoxuM!|7B?T9sH~_LVU<;IV)jTCmwLU%E2J`LmN~mPQ0p-_A?X^xba;;$km-KH^ z`V-KW+WHKWeyP>mHGkn9cmz+kKU0JH#w;YLv9Pd|E#CqOgGsq{^uLTS;cB;7`TQYk zYirevJ34vV?{aby6BB=V)RmN!ym;{f;jWvfZC&nTZVnd|)OtUtp}hX+Q8|TL1xYyG z=PsTaxdllD9`{*C4h{~@JZE~RA)~)=>;Fw9gnRc<*Y?}5jtRvPP^TkAvXGVu^ zIJEJErE*c#9@iLO3}2BZvO2qu1IgQpmSxCQpkRT}yN6dGFf1lm z8wuDnwZISv^9L?B(`u~?)hu2==&Cd;`arha(+|P;Um=jGn+F=Aa$q5EIwi44fw+bQ z!m&t$fU<_;fTK#oKtBrn>A8P5!T&coumIKS11kf%;3fp`+6?XGvfx)rVq}L__a@?d z(?qf)eB1zDw&@T+TE=04I4%H#WjX=R9IO8C*7emwTFA2lI96@P|Hz1~Xsj_yz5V_D zKo*lo?yBqSj{*}HA3+ep^tUcnx+IxKWmfo?N;$uv;C@X81hT~;-=ZNCx@``4j5n$u zgL0Hzyxw}*($7_vKL4l9jLtngfgJAs=#ZTMVE5Qs;4t_u*;*{QRYh`wFaxx7Vz1j26gpp7R7|dnw zw~kl-AEVh{M7M{9r1G@09NZY-+|Vcy1Cj@_rc1r~?!Iff8@RqmD(;UTKOUcL1|Z|f zE~5fE!0l;5++#p8CGxk^T5;Rxi9%CIaFB z;O)wWGIp7u$op(?2AFm-=etS6OZ(Ozt%Cr!_mzB%o&NeHecCZL^SlIIN zGPw-shRduEIFZ!U9jd$_ceb;!dEdzCZdm9wS?5`^j|(RtKIij!^*2d#nq5pb^Ph`g zWy=M2L%?E^-b51j1!hmxAxeVLKZNr?=T)AK-q6U%!S3$i(UFES@TsohV&?)p_wIN> zZ%q83bI-pjaR4xUFE1}mWnPmy3NkW*HhUxODRQ|X%eyk@-(spSBUh!{ZqU&gS2>P# z^EUeelFKb?>QDQkT^Z z24hLiDEZW5y2)=k;KI+`d?uJgDkU|wi>2KAQ4uhJfxnkov(I7SMmt*BN7(Lg|g+|pI zBmsx*fM$*V{0Sb^`T0R}xdRApNf;z(C@J^AtpqHNkeUI3R>E_ho7IvEw8-PlHQqT4Y@QqLC|29Nr=aF znpai@G$2Siv-@rNXv)gVLyEqs&79&cV3T%sAWlbbKi&N`H#Y~W7T7`UCR)ge&W<41 z1)_n+(*Ib(jQUk)sy#sQxml{E#YI;;5Pa37{e~g{ndC`J85c$(rynYLdR8{3>-=Nu znbzDeapWUdEL7=RxIoQzM)$1q5W zihk&~l{!DPTxEYZJWhN>zd!|;Yq4=H@Sog()8GCx4`D$;jOH(JTJGsNL)3CIM$jH@ zpgwX!znX~98cFo&y-}wj3I!SyDAayiBHZ9!--3J#_k2We7V_e7d;U*W4Yq4UHyCi? z;NVbDQm#)oPp^wbW*9Z0cmk%;UyVoU52S$Jn=l+|>-5gLDBR5@zAx{a&7X{q%>Pf{ z;ct4pJ*a=-a}y2DtCO#*p$w{T7Hn)u%Zl3zYcqSuYr_)=RstN!GX5}?KYE5HB;jh_ zL>z}*3ZYG3r}J&o3R_al;PR2ce~;NlZ^Ux%loovfg;`i#R`l_+O!-)WZhj1%Sebci z5C}cWW_v+8gN_A^q^GAR4oPcv(>s3FA8tGaS&36OzD~8jDTJ^BjUpx{HWB^B5$B=a z(a{;^Z41~SvDZe(>mou69M#WvICC`(jEvIa;xvqmZjh3;-kMnk%pYYW_Jywp6g${3 z<}qU8?K4NB5)g1<86#Qcwx~QJoV<}?C3Pc^jxGuko>1)7A2)65D3}k*qoJYU?OUc$ z3ox|O3%vTI#73bR}@&q#fv{*c}T6&$QB ze$fwDFFZ&TE_WEuFyHhx4g%@%4B<^`e<%o=pOM5q{EO^rvi5JuE|3T2pmK>JEqWF_ z>Bewe>}R8$i*$-hp}vlV-zjkrwO$@=7p>L*;Oj;{MJGUiNX^6fFVSm}z|M7UIs%ED zho@)sLa7~G7NAK}utf^DQPqc`-#IxsAb)TnJmM_~IE{vtxz2y7si^_FmrQ2uZ0E+s z)r*d~zYMN69xmW+4L1d;xhyH5B|fF)64t+eIR9oi)`z*?`+$Dg+L;@%kl{V`Cb8*g zDNRX9i7H}@S~%de5JWT8)yaOZavIJ2%MTtd^`wGeIdMQGnI{B|uBmCCVJX_9&M2gU*^+4 zPq#(Ri|fWtuZcw7*H6?8wE%oPO5PIe>FT#l=d%bB@gnoR3WS527}%mnt|nn2km|KL z)yvb>+xtLCsjIUS9OGnLiMqUQZ1h_lE(E^An>TMjrER%geWqrZ?Q*$VaX^Tc&t$;vlqukL+@`&T zE9>P|b-)Q32yustwTZ>DJ~^hd?L3jc>UjDe03tJVMStK92v6gg<=|>tAot(ljdV~G ziN(BMLgi2f@{wO@@vhnGxJp8Va|Ho*)E`pd)9Zj8?}sd@B+tJNm*6^I4Xbs5NGZxF zw8CvJs|0rzFGq%lRIz9I%~aU%mIV|EN7NAG%!qTfk-Uu~G6?uZr6T7Ec@DjN%L$)s zXqtA5w&MWXr<_GLzMGBi*?su$XXwjQ(BdFs2>^{`M8k37;mOdORsCufZ-f@4CGIK~ zkHkojR}H!u)P4`k$ioFjF@uxReXt)URS;~b^+9#5apQDsS!s&3B`8VWB~oMY)LYqD zd|3xfudBGYZU&{!lcv}3di}w!0pN&CO_`z$A{LloO32nt>?_ZaWp#CF( zmiO;Fz$P_KS~@yMJ{q=M5XOpot<4C`+5i{9Oh@M)yQIf2bs3qUzhHsWpY~gJjFf%p z0*Ll2RlgL+)0|g4&&d)sfXUA;K(k#kpJ|AKJ^B1ZgJ3U&j?Uh{Z;Wuy2T%>RE2bU( z>dyvt!wJ}O1?(9{->uerkyNh8XP9SGCXS)vMD1mu#hjGnKUwJK zhm;3QNaV%ON{7S&Kx%+j;UtFiGd7+8qLn>YM@I+b(Wkw68MwF8dWEa!-j`N?augQt zoS2f?oT289_n))6`-BAx@utby&p7iBm+-0 zJzWAM?STG(gg(t6DLHw0agmae5<#?wb4^26h-QB5IqZAfQX6u(DmsZcPT0 z0Cxin{Vw_tQ1FmLtgaBoKXV;BhsGaTgndOZ+#~FyHTx#*<&=M>y>;<*W`l9-+NJgJ zufK3vAPeNPY7HBPK=@lc;gGnt25gsT^iyK+`SB+kX?;?z1LBVYc|i(9dN?*}%GrHv z87tUViTmXM(&Cdch=2L_D&0UBLSQ#Q6hqXy{YD5PO$3e#xq0osB-wx4&-A}Zxl4cK a0x#|Jr}tl~U#&vGOX;4fT#<}<(EkS=zo~@) literal 0 HcmV?d00001 diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp index 907cb9806..6b6429c21 100644 --- a/src/proc/engine/buffer-provider.hpp +++ b/src/proc/engine/buffer-provider.hpp @@ -51,6 +51,14 @@ namespace engine { + enum BufferState + { NIL, + FREE, + LOCKED, + EMITTED, + BLOCKED + }; + /** * Handle for a buffer for processing data, abstracting away the actual implementation. diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp index d784bad85..e88d486fa 100644 --- a/src/proc/play/output-slot.hpp +++ b/src/proc/play/output-slot.hpp @@ -66,6 +66,7 @@ namespace play { /** established output channel */ class Connection; + typedef int64_t FrameNr; class DataSink @@ -73,7 +74,7 @@ namespace play { { public: - void emit(Time); + void emit(FrameNr); }; @@ -107,6 +108,13 @@ namespace play { Allocation allocate(); + protected: + friend class DataSink; + + virtual void lock (FrameNr, uint channel) =0; + virtual void transfer (FrameNr, uint channel) =0; + virtual void pushout (FrameNr, uint channel) =0; + private: }; diff --git a/uml/lumiera/133637 b/uml/lumiera/133637 index 6336ed674..9857cb0e0 100644 --- a/uml/lumiera/133637 +++ b/uml/lumiera/133637 @@ -1,6 +1,6 @@ format 58 "Play" // ProcessingLayer::Play - revision 2 + revision 3 modified_by 5 "hiv" // class settings //class diagram settings @@ -352,5 +352,55 @@ ${inlines} relations end end + + sequencediagram 145157 "output data exchange" + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default show_stereotype_properties default + overlapping_bars size A4 + end + + class 178437 "Client" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 178565 "DataSink" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 178693 "BufferProvider" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + php_decl "" + python_2_2 python_decl "" + idl_decl "" + explicit_switch_type "" + + end end end diff --git a/uml/lumiera/145157.diagram b/uml/lumiera/145157.diagram new file mode 100644 index 000000000..f75dd4db5 --- /dev/null +++ b/uml/lumiera/145157.diagram @@ -0,0 +1,130 @@ +format 58 + +classinstance 128005 class_ref 178437 // Client + name "" xyz 53 4 2000 life_line_z 2000 +classinstance 128133 class_ref 176133 // OutputSlot + name "" xyz 281 5 2000 life_line_z 2000 +classinstance 128261 class_ref 178565 // DataSink + name "" xyz 140 28 2000 life_line_z 2000 +classinstance 128389 class_ref 178693 // BufferProvider + name "" xyz 463 5 2000 life_line_z 2000 +fragment 131845 "client side" + xyzwh 41 28 1995 156 279 +end +fragment 131973 "output implementation" + xyzwh 281 29 1995 262 277 +end +durationcanvas 128517 classinstance_ref 128005 // :Client + xyzwh 72 83 2010 11 52 +end +durationcanvas 129541 classinstance_ref 128005 // :Client + xyzwh 72 211 2010 11 41 +end +durationcanvas 129669 classinstance_ref 128261 // :DataSink + xyzwh 162 238 2010 11 25 +end +durationcanvas 129925 classinstance_ref 128133 // :OutputSlot + xyzwh 307 242 2010 11 63 + overlappingdurationcanvas 133637 + xyzwh 313 272 2020 11 28 + end +end +durationcanvas 130309 classinstance_ref 128389 // :BufferProvider + xyzwh 497 250 2010 11 26 +end +durationcanvas 131333 classinstance_ref 128133 // :OutputSlot + xyzwh 307 85 2010 11 34 +end +durationcanvas 132101 classinstance_ref 128261 // :DataSink + xyzwh 162 107 2010 11 25 +end +durationcanvas 132485 classinstance_ref 128005 // :Client + xyzwh 72 167 2010 11 27 +end +durationcanvas 132613 classinstance_ref 128261 // :DataSink + xyzwh 162 169 2010 11 25 +end +durationcanvas 132869 classinstance_ref 128133 // :OutputSlot + xyzwh 307 173 2010 11 25 +end +durationcanvas 133125 classinstance_ref 128005 // :Client + xyzwh 72 211 2010 11 29 +end +durationcanvas 133381 classinstance_ref 128389 // :BufferProvider + xyzwh 497 180 2010 11 25 +end +durationcanvas 133893 classinstance_ref 128389 // :BufferProvider + xyzwh 497 280 2010 11 25 +end +msg 129797 synchronous + from durationcanvas_ref 129541 + to durationcanvas_ref 129669 + yz 238 2015 explicitmsg "emit()" + show_full_operations_definition default drawing_language default + label_xy 109 230 +msg 130053 synchronous + from durationcanvas_ref 129669 + to durationcanvas_ref 129925 + yz 242 2020 explicitmsg "transfer()" + show_full_operations_definition default drawing_language default + label_xy 217 234 +msg 130437 synchronous + from durationcanvas_ref 129925 + to durationcanvas_ref 130309 + yz 250 2030 explicitmsg "transition to EMITTED" + show_full_operations_definition default drawing_language default + label_xy 374 243 +msg 131461 synchronous + from durationcanvas_ref 128517 + to durationcanvas_ref 131333 + yz 85 2015 explicitmsg "allocate()" + show_full_operations_definition default drawing_language default + label_xy 102 75 +msg 132229 synchronous + from durationcanvas_ref 131333 + to durationcanvas_ref 132101 + yz 108 2020 explicitmsg "create DataSink" + show_full_operations_definition default drawing_language default + label_xy 201 99 +msg 132357 return + from durationcanvas_ref 132101 + to durationcanvas_ref 128517 + yz 116 2025 explicitmsg "DataSink" + show_full_operations_definition default drawing_language default + label_xy 101 121 +msg 132741 synchronous + from durationcanvas_ref 132485 + to durationcanvas_ref 132613 + yz 169 2015 explicitmsg "lockBuffer()" + show_full_operations_definition default drawing_language default + label_xy 97 159 +msg 132997 synchronous + from durationcanvas_ref 132613 + to durationcanvas_ref 132869 + yz 175 2020 explicitmsg "lock()" + show_full_operations_definition default drawing_language default + label_xy 225 165 +reflexivemsg 133253 synchronous + to durationcanvas_ref 133125 + yz 211 2020 explicitmsg "generate Data" + show_full_operations_definition default drawing_language default + label_xy 84 199 +msg 133509 synchronous + from durationcanvas_ref 132869 + to durationcanvas_ref 133381 + yz 180 2025 explicitmsg "transition to LOCKED" + show_full_operations_definition default drawing_language default + label_xy 374 173 +reflexivemsg 133765 synchronous + to durationcanvas_ref 133637 + yz 272 2040 explicitmsg "pushout()" + show_full_operations_definition default drawing_language default + label_xy 325 260 +msg 134021 synchronous + from durationcanvas_ref 133637 + to durationcanvas_ref 133893 + yz 289 2025 explicitmsg "transition to FREE" + show_full_operations_definition default drawing_language default + label_xy 374 282 +preferred_whz 586 416 1 +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 0e80581b8..87c7f7827 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -6,8 +6,10 @@ diagrams 730 488 100 4 0 0 classdiagram_ref 143877 // Player Entities 663 648 100 4 0 0 - active objectdiagram_ref 144005 // Play Process Structure + objectdiagram_ref 144005 // Play Process Structure 562 424 100 4 0 0 + active sequencediagram_ref 145157 // output data exchange + 586 416 100 4 0 0 end show_stereotypes selected diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index e9189265f..6c6cb4c64 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 58 "lumiera" - revision 70 + revision 71 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 1826d59ef..042b26d15 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -3324,7 +3324,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3393,8 +3393,37 @@ This data exchange protocol operates on a rather low level; there is only limite
 
 Thus there are two serious problem situations
 * allocating the buffer out of time window bears the danger of output data corruption; but the general assumption is for the scheduler to ensure each job start time remains within the defined window and all prerequisite jobs have terminated successfully. To handle clean-up, we additionally need special jobs running always, in order, and be notified of prerequisite failures.
-* failure to release a buffer timely blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole.
-&rarr; SchedulerRequirements
+* failure to release a buffer in a timely fashion blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole. +&rarr; SchedulerRequirements +&rarr; OutputSlotImpl +
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+!operation steps
+[>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.
+;lock()
+:claims the specified buffer for exclusive use.
+:Client may now dispose output data
+;transfer()
+:transfers control from the client to the actual output connection for feeding data.
+:Triggered by the client invoking {{{emit()}}}
+;pushout()
+:request the actual implementation to push the data to output.
+:Usually invoked from the {{{transfer()}}} implementation, alternatively from a separate thread.
+
+
+!buffer states
+While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the client (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
+|        !state||>| !lock()   |>| !transfer() |>| !pushout() |
+|     {{{NIL}}}||↯| ·        |↯|             | ↯ |          |
+|    {{{FREE}}}||✔|↷ locked  |✔|· (ignore)  | · | ·         |
+|  {{{LOCKED}}}||↯|↷ emitted |✔|↷ emitted   | · |↷ free    |
+| {{{EMITTED}}}||↯|↷ blocked |↯|↷ blocked   | · |↷ free    |
+| {{{BLOCKED}}}||↯| ✗        |↯| ✗          | ∅ |↷ free    |
+where · means no operation, ✔ marks the standard cases (OK response to caller), ↯ will throw and thus kill the calling job, ∅ will treat this frame as //glitch,// ✗ will request playback stop.
+The rationale is for all states out-of-order to transition into the {{{BLOCKED}}}-state eventually, which, when hit by the next operation, will request playback stop.
+
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].

From 4a62444ad439e2492dc82d744e74a03ab2c4efbf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 14 Aug 2011 03:09:05 +0200
Subject: [PATCH 050/108] WIP pick up on the design work regarding Engine,
 OutputSlot and Player

---
 src/proc/engine/calc-stream.hpp    | 91 ++++++++++++++++++++++++++++++
 src/proc/engine/engine-service.hpp | 30 ++++++----
 src/proc/engine/renderengine.hpp   |  5 ++
 src/proc/engine/rendergraph.hpp    |  4 ++
 wiki/renderengine.html             |  8 +--
 5 files changed, 124 insertions(+), 14 deletions(-)
 create mode 100644 src/proc/engine/calc-stream.hpp

diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp
new file mode 100644
index 000000000..f026b56d2
--- /dev/null
+++ b/src/proc/engine/calc-stream.hpp
@@ -0,0 +1,91 @@
+/*
+  CALC-STREAM.hpp  -  abstraction representing a series of scheduled calculations
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+
+#ifndef PROC_ENGINE_CALC_STREAM_H
+#define PROC_ENGINE_CALC_STREAM_H
+
+
+//#include "include/dummy-player-facade.h"
+//#include "include/display-facade.h"
+//#include "common/instancehandle.hpp"
+//#include "lib/singleton-ref.hpp"
+//
+//#include 
+//#include 
+//#include 
+
+
+namespace proc {
+namespace play {
+
+//    using std::string;
+//    using lumiera::Subsys;
+//    using lumiera::Display;
+//    using lumiera::DummyPlayer;
+  
+  
+  
+  
+  
+  /*********************************************************
+   * A calculation stream groups and abstracts a series of
+   * calculation jobs, delivering frames into the configured
+   * OutputSlot in a timely fashion. Behind the scenes, this
+   * gets translated into several jobs enqueued with the
+   * scheduler in the backend layer. The calculation stream
+   * implementation cares to create and configure these
+   * jobs and to manage the necessary dependencies and
+   * callbacks.
+   * 
+   * Regarding the implementation, a CalcStream is an const
+   * value object holding the metadata necessary to manage
+   * the underlying jobs. The only way to create a CalcStream
+   * properly is to retrieve it from the  factory functions
+   * of the EngineService. At that point, the corresponding
+   * jobs will be configured and enqueued. 
+   */
+  class CalcStream
+    {
+      
+      friend class EngineService;
+      
+      CalcStream()
+        {
+          
+        }
+      
+    public:
+      CalcStream (CalcStream const& o)
+        { }
+      
+     ~CalcStream() { }
+      
+    };
+  
+  
+  
+  
+} // namespace play
+} // namespace proc
+#endif
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index 7798307b0..b7babf91a 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -21,10 +21,14 @@
 */
 
 /** @file engine-service.hpp
- ** A public service provided by
- **
- ** @see lumiera::DummyPlayer
- ** @see gui::PlaybackController usage example 
+ ** Access point for the (core) calculation service of the render engine.
+ ** This public service is provided by the Proc-Layer, but actually implemented
+ ** using backend services (especially the scheduler). The central concept provided
+ ** through this facade interface is that of a calculation stream. On the
+ ** implementation side, these get translated into a series of jobs invoking
+ ** render nodes, to be invoked through the scheduler in the backend layer. 
+ ** 
+ ** @see proc::play::PlayerService
  */
 
 
@@ -34,6 +38,7 @@
 
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
+#include "proc/engine/calc-stream.hpp"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
 //
@@ -55,12 +60,14 @@ namespace play {
   
   
   /******************************************************
-   * Actual implementation of the DummyPlayer service.
-   * Creating an instance of this class automatically
-   * registers the interface lumieraorg_DummyPlayer with
-   * the Lumiera Interface/Plugin system and creates
-   * a forwarding proxy within the application core to
-   * route calls through this interface.
+   * A service to schedule series of calculations,
+   * delivering the rendered data into an external output
+   * sink in a timely fashion. Actually the CalculationStream
+   * instances provided through this (facade) interface are
+   * backed by jobs executed through the scheduler in the
+   * backend layer. The implementation of this service
+   * cares to create the right job entries in the correct
+   * order and to enqueue these into the scheduler.
    */
   class EngineService
     : boost::noncopyable
@@ -84,6 +91,9 @@ namespace play {
       
      ~EngineService() { } /////TODO notifyTermination_(&error_); }
       
+      CalcStream
+      
+      
     };
   
   
diff --git a/src/proc/engine/renderengine.hpp b/src/proc/engine/renderengine.hpp
index c3030271f..ebbd46e5a 100644
--- a/src/proc/engine/renderengine.hpp
+++ b/src/proc/engine/renderengine.hpp
@@ -31,6 +31,11 @@
 
 using std::list;
 
+/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network.
+/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure,
+/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network
+
+//////////TODO for the "real" engine API: look at engine-serivce.hpp
 
 namespace engine {
   
diff --git a/src/proc/engine/rendergraph.hpp b/src/proc/engine/rendergraph.hpp
index a77eac68b..eb811de6f 100644
--- a/src/proc/engine/rendergraph.hpp
+++ b/src/proc/engine/rendergraph.hpp
@@ -29,6 +29,10 @@
 #include "lib/time/timevalue.hpp"
 
 
+/////////////////////////////TODO 7/11 this is a piece of debris, left over from the first attempt to complete the render nodes network.
+/////////////////////////////TODO Meanwhile the intention is to treat the render nodes network more like a data structure,
+/////////////////////////////TODO consequently this will become some kind of root or anchor point for this network
+
 
 namespace engine {
   
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 042b26d15..2c0859c66 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1785,10 +1785,10 @@ To make the intended use of the classes more clear, consider the following two e
 * a video clip placed relatively, with an attached HUE effect &rarr;[[Example2]]
 
-
+
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
 
-The render nodes network is always buils separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
+The render nodes network is always built separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
 
@@ -3397,7 +3397,7 @@ Thus there are two serious problem situations &rarr; SchedulerRequirements &rarr; OutputSlotImpl
-
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
 
 !operation steps
@@ -3414,7 +3414,7 @@ Thus there are two serious problem situations
 
 
 !buffer states
-While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the client (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
+While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the server side (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
 |        !state||>| !lock()   |>| !transfer() |>| !pushout() |
 |     {{{NIL}}}||↯| ·        |↯|             | ↯ |          |
 |    {{{FREE}}}||✔|↷ locked  |✔|· (ignore)  | · | ·         |

From 1e7da409bb0a24531673dd7a08046b6b3cfb62b9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:11:12 +0200
Subject: [PATCH 051/108] Fix logic error uncovered by GCC 4.4

benefits of using a newer compiler, yay!

Explanation: the Link<...> template combines
various policy templates and exposes a set
of functions, which can be bound as functor
into an concrete time::Control instance.

Actually, we need to mix in only the Mutation
baseclass, because we just want to inherit
the privilege to change time values, which
are otherwise immutable. We don't need to
mix in the Mutator template anymore (this
was a leftover from an earlier design)
---
 src/lib/time/control-policy.hpp | 8 ++++----
 src/lib/time/control.hpp        | 2 ++
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/lib/time/control-policy.hpp b/src/lib/time/control-policy.hpp
index a941c3c96..7ffed6426 100644
--- a/src/lib/time/control-policy.hpp
+++ b/src/lib/time/control-policy.hpp
@@ -234,7 +234,7 @@ namespace mutation {
    */
   template
   struct Link
-    : Mutator
+    : Mutation
     , Builder
     {
       
@@ -255,15 +255,15 @@ namespace mutation {
       static TI
       mutateLength (TimeSpan& target, Duration const& change)
         {
-          Mutator::imposeChange (target.duration(), change);
+          imposeChange (target.duration(), change);
           return Builder::buildChangedValue(target);
         }
       
       static TimeSpan
       mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
         {
-          Mutator::imposeChange (target.duration(), change.duration());
-          Mutator::imposeChange (target,change.start());
+          imposeChange (target.duration(), change.duration());
+          imposeChange (target,change.start());
           return Builder::buildChangedValue(target);
         }
       
diff --git a/src/lib/time/control.hpp b/src/lib/time/control.hpp
index cbac65d33..486097934 100644
--- a/src/lib/time/control.hpp
+++ b/src/lib/time/control.hpp
@@ -94,6 +94,8 @@
  **   the change, which might or might not reflect the change value passed in.
  ** 
  ** @todo include support for QuTimeSpan values                               ////////////////////TICKET #760
+ ** 
+ ** @see TimeControl_test
  **
  */
 

From 2625eee07cd1b7d25cc46bbd3be924d16d758c5a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:12:13 +0200
Subject: [PATCH 052/108] disable work-in-progress to make the tree compile

TODO noted as Ticket #819
---
 tests/components/proc/play/output-slot-protocol-test.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tests/components/proc/play/output-slot-protocol-test.cpp b/tests/components/proc/play/output-slot-protocol-test.cpp
index c83d6bff3..124d49372 100644
--- a/tests/components/proc/play/output-slot-protocol-test.cpp
+++ b/tests/components/proc/play/output-slot-protocol-test.cpp
@@ -68,6 +68,7 @@ namespace test  {
       void
       verifyStandardCase()
         {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819
           // Create Test fixture.
           // In real usage, the OutputSlot will be preconfigured
           // (Media format, number of channels, physical connections)
@@ -135,6 +136,7 @@ namespace test  {
           CHECK (*stream1++ == testData[1,0]);
           CHECK (*stream1++ == testData[1,1]);
           CHECK (!stream1);
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #819
         }
     };
   

From d1b6f7a57b9205cfea1874e75e5d9d9d48025773 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:52:12 +0200
Subject: [PATCH 053/108] polymorphic value builder functions: how to take
 arguments

Previously, I've added an additional '&' to be able
to pass references without much ado. This turned out
to be problematic when using constant values at the
invocation site. Well. C++ has really a fixation
ont passing things by value. This is fine, but
doesn't play so well at times when passing smart-ptrs
or similar ref-counting stuff, especially when we just
want to *use* the handle, not store it away.
Essentially the same situation as with for_each

Bottom line: from now on, we need to state the
template parameter for such arguments explicitly,
or just accept the overhead of creating an additional
transient copy of the smart-ptr.
---
 src/lib/polymorphic-value.hpp | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp
index 0f13a9b98..a05e04519 100644
--- a/src/lib/polymorphic-value.hpp
+++ b/src/lib/polymorphic-value.hpp
@@ -389,21 +389,21 @@ namespace lib {
         }
       
       template
-      PolymorphicValue (IMP*, A1& a1)
+      PolymorphicValue (IMP*, A1 a1)
         {
           REQUIRE (siz >= sizeof(IMP));
           new(&buf_) IMP (a1);
         }
       
       template
-      PolymorphicValue (IMP*, A1& a1, A2& a2)
+      PolymorphicValue (IMP*, A1 a1, A2 a2)
         {
           REQUIRE (siz >= sizeof(IMP));
           new(&buf_) IMP (a1,a2);
         }
       
       template
-      PolymorphicValue (IMP*, A1& a1, A2& a2, A3& a3)
+      PolymorphicValue (IMP*, A1 a1, A2 a2, A3 a3)
         {
           REQUIRE (siz >= sizeof(IMP));
           new(&buf_) IMP (a1,a2,a3);
@@ -444,13 +444,13 @@ namespace lib {
           Adapter() : IMP() { }
           
           template
-          Adapter (A1& a1) : IMP(a1) { }
+          Adapter (A1 a1) : IMP(a1) { }
           
           template
-          Adapter (A1& a1, A2& a2) : IMP(a1,a2) { }
+          Adapter (A1 a1, A2 a2) : IMP(a1,a2) { }
           
           template
-          Adapter (A1& a1, A2& a2, A3& a3) : IMP(a1,a2,a3) { }
+          Adapter (A1 a1, A2 a2, A3 a3) : IMP(a1,a2,a3) { }
           
           /* using default copy and assignment */
         };
@@ -505,31 +505,31 @@ namespace lib {
       static PolymorphicValue
       build ()
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer);
         }
       
       template
       static PolymorphicValue
-      build (A1& a1)
+      build (A1 a1)
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer, a1);
         }
       
       template
       static PolymorphicValue
-      build (A1& a1, A2& a2)
+      build (A1 a1, A2 a2)
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer, a1,a2);
         }
       
       template
       static PolymorphicValue
-      build (A1& a1, A2& a2, A3& a3)
+      build (A1 a1, A2 a2, A3 a3)
         {
-          Adapter* type_to_build_in_buffer;
+          Adapter* type_to_build_in_buffer(0);
           return PolymorphicValue (type_to_build_in_buffer, a1,a2,a3);
         }
       

From 3125d1c5735139bea31d51bbe4d344cde986ae69 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 15 Aug 2011 00:52:56 +0200
Subject: [PATCH 054/108] a first draft for the Engine Interface

---
 src/proc/engine/calc-stream.hpp    | 11 ++--
 src/proc/engine/engine-service.cpp | 93 +++++++++++++++++++++++++++---
 src/proc/engine/engine-service.hpp | 57 +++++++++++++++++-
 src/proc/play/output-slot.hpp      |  1 +
 src/proc/play/timings.hpp          | 89 ++++++++++++++++++++++++++++
 wiki/renderengine.html             | 36 ++++++------
 6 files changed, 255 insertions(+), 32 deletions(-)
 create mode 100644 src/proc/play/timings.hpp

diff --git a/src/proc/engine/calc-stream.hpp b/src/proc/engine/calc-stream.hpp
index f026b56d2..b9b32c9fc 100644
--- a/src/proc/engine/calc-stream.hpp
+++ b/src/proc/engine/calc-stream.hpp
@@ -26,6 +26,7 @@
 #define PROC_ENGINE_CALC_STREAM_H
 
 
+#include "lib/error.hpp"
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
 //#include "common/instancehandle.hpp"
@@ -36,8 +37,8 @@
 //#include 
 
 
-namespace proc {
-namespace play {
+namespace proc  {
+namespace engine{
 
 //    using std::string;
 //    using lumiera::Subsys;
@@ -77,7 +78,9 @@ namespace play {
       
     public:
       CalcStream (CalcStream const& o)
-        { }
+        { 
+          UNIMPLEMENTED("build a calculation stream");
+        }
       
      ~CalcStream() { }
       
@@ -86,6 +89,6 @@ namespace play {
   
   
   
-} // namespace play
+} // namespace engine
 } // namespace proc
 #endif
diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp
index 23078f68c..40531d4a5 100644
--- a/src/proc/engine/engine-service.cpp
+++ b/src/proc/engine/engine-service.cpp
@@ -40,14 +40,93 @@ namespace engine{
 //    using std::tr1::bind;
     
     
-    namespace { // hidden local details of the service implementation....
+  namespace { // hidden local details of the service implementation....
+    
+  } // (End) hidden service impl details
+    
+    
+  
+    
+  /** */  
+  EngineService::EngineService()
+    { }
       
-    } // (End) hidden service impl details
-    
-    
-    
-    
-    /** */  
+      
+  
+  /** */
+  CalcStream
+  EngineService::calculate(ModelPort mPort,
+                           Timings nominalTimings,
+                           OutputConnection output,
+                           Quality serviceQuality)
+  {
+    UNIMPLEMENTED ("build a standard calculation stream");
+  }
+  
+  
+  
+  /** */
+  CalcStream
+  EngineService::calculateBackground(ModelPort mPort,
+                                     Timings nominalTimings,
+                                     Quality serviceQuality)
+  {
+    UNIMPLEMENTED ("build a calculation stream for background rendering");
+  }
+  
+  
+  
+  /* ===== Quality-of-Service ===== */
+  
+  
+  EngineService::Quality::~Quality() { } // emit vtables here...
+  
+  enum CalcType {
+    PLAYBACK,
+    RENDER,
+    BACKGROUND
+  };
+  
+  
+  class DefaultQoS
+    : public EngineService::Quality
+    {
+      CalcType type_;
+      
+    public:
+      DefaultQoS (CalcType type)
+        : type_(type)
+        { }
+    };
+  
+  class PriorityQoS
+    : public DefaultQoS
+    {
+      CalcType type_;
+      
+    public:
+      PriorityQoS ()
+        : DefaultQoS(PLAYBACK)
+        { }
+    };
+  
+  class Compromise
+    : public DefaultQoS
+    {
+      
+    public:
+      Compromise (CalcType type)
+        : DefaultQoS(type)
+        { }
+    };
+  
+  
+  EngineService::QoS_Definition  EngineService::QoS_DEFAULT         = QoS_Definition::build (PLAYBACK);
+  EngineService::QoS_Definition  EngineService::QoS_BACKGROUND      = QoS_Definition::build (BACKGROUND);
+  EngineService::QoS_Definition  EngineService::QoS_COMPROMISE      = QoS_Definition::build (PLAYBACK); 
+  EngineService::QoS_Definition  EngineService::QoS_PERFECT_RESULT  = QoS_Definition::build (RENDER);
+  EngineService::QoS_Definition  EngineService::QoS_SYNC_PRIORITY   = QoS_Definition::build();
+  
   
   
 }} // namespace proc::engine
diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp
index b7babf91a..502e63329 100644
--- a/src/proc/engine/engine-service.hpp
+++ b/src/proc/engine/engine-service.hpp
@@ -36,24 +36,33 @@
 #define PROC_ENGINE_ENGINE_SERVICE_H
 
 
+#include "lib/error.hpp"
 //#include "include/dummy-player-facade.h"
 //#include "include/display-facade.h"
 #include "proc/engine/calc-stream.hpp"
+#include "proc/mobject/model-port.hpp"
+#include "proc/play/timings.hpp"
+#include "proc/play/output-slot.hpp"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
+#include "lib/polymorphic-value.hpp"
 //
 #include 
 //#include 
 //#include 
 
 
-namespace proc {
-namespace play {
+namespace proc  {
+namespace engine{
 
 //    using std::string;
 //    using lumiera::Subsys;
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
+  using mobject::ModelPort;
+  using proc::play::Timings;
+  
+  typedef proc::play::OutputSlot::Allocation OutputConnection;
   
   
   
@@ -86,19 +95,61 @@ namespace play {
 //    lib::SingletonRef implInstance_;
 //    ServiceInstanceHandle serviceInstance_;
       
+      /* The following typedefs allow to hand out predefined
+       * Quality-of-Service strategy definitions as value objects,
+       * without disclosing implementation details here in this header.
+       */
+      enum{ QoS_IMPL_SIZE = sizeof(size_t) };  /////////////////////////////////////////TODO is this correct??
+      
+      
     public:
+      /*************************************************************
+       * Quality-of-Service definition for an Render Engine usage.
+       * This strategy defines how to decide between conflicting goals like
+       * - timely delivery
+       * - image quality
+       * - niceness and resource usage
+       */
+      class Quality
+        {
+          public:
+            virtual ~Quality();  ///< this is an Interface
+        };
+      
+      
+//      typedef lib::polyvalue::CloneValueSupport _ClonableQoS_Strategy;
+      typedef lib::PolymorphicValue QoS_Definition;
+      
+      static QoS_Definition  QoS_DEFAULT;
+      static QoS_Definition  QoS_BACKGROUND;
+      static QoS_Definition  QoS_SYNC_PRIORITY;
+      static QoS_Definition  QoS_PERFECT_RESULT;
+      static QoS_Definition  QoS_COMPROMISE;
+      
+      
+      
       EngineService();    /////TODO (Subsys::SigTerm terminationHandle);
       
      ~EngineService() { } /////TODO notifyTermination_(&error_); }
       
       CalcStream
+      calculate(ModelPort mPort,
+                Timings nominalTimings,
+                OutputConnection output,
+                Quality serviceQuality =QoS_DEFAULT);
       
+      CalcStream
+      calculateBackground(ModelPort mPort,
+                          Timings nominalTimings,
+                          Quality serviceQuality =QoS_BACKGROUND);
       
     };
   
   
   
   
-} // namespace play
+  
+  
+} // namespace engine
 } // namespace proc
 #endif
diff --git a/src/proc/play/output-slot.hpp b/src/proc/play/output-slot.hpp
index e88d486fa..b3188724e 100644
--- a/src/proc/play/output-slot.hpp
+++ b/src/proc/play/output-slot.hpp
@@ -40,6 +40,7 @@
 #include "lib/handle.hpp"
 #include "lib/time/timevalue.hpp"
 #include "proc/engine/buffer-provider.hpp"
+#include "proc/play/timings.hpp"
 #include "lib/iter-source.hpp"
 //#include "lib/sync.hpp"
 
diff --git a/src/proc/play/timings.hpp b/src/proc/play/timings.hpp
new file mode 100644
index 000000000..a3613bbf9
--- /dev/null
+++ b/src/proc/play/timings.hpp
@@ -0,0 +1,89 @@
+/*
+  TIMINGS.hpp  -  timing specifications for a frame quantised data stream
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+/** @file timings.hpp
+ ** How to define a timing specification or constraint.
+ ** A timing spec is used to anchor an data stream with relation to a time axis or frame grid.
+ ** There are two kinds of timing specs:
+ ** - nominal timing specifications relate to the nominal time values
+ **   of the frames in a data stream, i.e. the "should be" time values.
+ **   These might be values derived from a timecode or just values in
+ **   in relation to the timeline axis, but without any connection to
+ **   the real wall clock time
+ ** - actual timing specifications are always connected or related to
+ **   an external time source, typically just wall clock time. For example,
+ **   actual timing specs dictate the constraints for real time frame
+ **   delivery to an external output connection.
+ **   
+ ** @todo WIP-WIP-WIP 8/2011
+ ** @see output-slot.hpp  ////TODO
+ */
+
+
+#ifndef PROC_PLAY_TIMINGS_H
+#define PROC_PLAY_TIMINGS_H
+
+
+#include "lib/error.hpp"
+//#include "lib/handle.hpp"
+#include "lib/time/timevalue.hpp"
+//#include "proc/engine/buffer-provider.hpp"
+//#include "lib/iter-source.hpp"
+//#include "lib/sync.hpp"
+
+//#include 
+//#include 
+//#include 
+//#include 
+//#include 
+
+
+namespace proc {
+namespace play {
+
+  using lib::time::Time;
+//using std::string;
+
+//using std::vector;
+//using std::tr1::shared_ptr;
+//using boost::scoped_ptr;
+  
+  
+  
+  
+  /********************************************************************
+   * Data-Record: Generic frame timing specification.
+   * 
+   * @note copyable value class
+   * 
+   * @todo write type comment
+   */
+  class Timings
+    {
+    public:
+      //////////////TODO accessor functions here
+    };
+  
+  
+  
+}} // namespace proc::play
+#endif
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 2c0859c66..49a1a6096 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1643,7 +1643,7 @@ The main tool used to implement this separation is the [[Builder Pattern|http://
 Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
 
-
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
 
 !Starting point
@@ -1659,7 +1659,7 @@ Introduce a new kind of MObject, a GeneratorMO, to represent what the current du
 Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger undertaking, and best broken up into several //stages.//
 # just moving the existing entities into the final location (typically down in the layer hierarchy)
 # introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
-# rework the ~ProcessImpl and the ~TickService to become the CalculationStream and use the real Engine Interface, but still with an mock implementation hooked up behind
+# rework the ~ProcessImpl and the ~TickService to become the [[calculation stream|CalcStream]] and use the real Engine Interface, but still with an mock implementation hooked up behind
 # extend and elaborate the handling of output, build the foundation of the real OutputManager
 # switch to the real Scheduler and Engine implementation
 
@@ -1734,11 +1734,11 @@ When building the low-level model, the actual processing code is resolved and a
 Initially, only the parameter (descriptors) are present on the effect ~MObject, while the actual [[parameter providers|ParamProvider]] are created or wired (by the ConManager) on demand.
 
-
-
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalculationStream]].
+
+
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalcStream]].
 
 Below this facade, there is a thin adaptadion and forwarding layer, mainly talking to
-* the individual CalculationStream elements created for each PlayProcess.
+* the individual [[Calculation Streams|CalcStream]] created for each PlayProcess.
 * the FrameDispatcher, which translates such streams into a series of RenderJob entries
 * the [[Scheduler]], which is responsible to perform these jobs in a timely fashion
 
@@ -1916,25 +1916,25 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm * moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
-Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes are known to be terminated.
+Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes (&rarr; CalcStream) are known to be terminated.
 
 Each segment owns an AllocationCluster, which in turn manages all the numerous small-sized objects comprising the render network implementing this segment -- thus the central question is when to //release the segment.//
-* for one, we easily detect the point when a segment is swapped out of the segmentation; at this point we also have to detect the //tainted render processes.//
-* but those render processes terminate asynchronously, and that forces us to do some kind of registration and deregistration.
+* for one, we easily detect the point when a segment is swapped out of the segmentation; at this point we also have to detect the //tainted calculation streams.//
+* but those render processes (calc streams) terminate asynchronously, and that forces us to do some kind of registration and deregistration.
 
 !!question: is ref-counting acceptable here?
 //Not sure yet.// Of course it would be the simplest approach. KISS.
-Basically the concern is that each new render process had to access the shared counts of all segments it touches.
+Basically the concern is that each new CalcStream had to access the shared counts of all segments it touches.
 
 ''Note'': {{{shared_ptr}}} is known to be implemented by a lock-free algorithm (yes it is, at least since boost 1.33. Don't believe what numerous FUD spreaders have written). Thus lock contention isn't a problem, but at least a memory barrier is involved (and if I&nbsp;judge GCC's internal documentation right, currently their barriers extend to //all// globally visible variables)
 
 !!question: alternatives?
-There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a render process had terminated and evaluate all those noted results on later occasion.
+There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a calculation stream is done and re-evaluate all those noted results on later occasion.
 
 !!exploiting the frame-dispatch step
-Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
+Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process / a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
 
 !!assessment {{red{WIP 12/10}}}
 But the primary question here is to judge the impact of such an implementation. What would be the costs?
@@ -2008,8 +2008,8 @@ Additionally, they may be used for resource management purposes by embedding a r
 #* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic)
 
-
-
An implementation facility within the RenderEngine, responsible for translating a logical CalculationStream (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
+
+
An implementation facility within the RenderEngine, responsible for translating a logical [[calculation stream|CalcStream]] (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
/***
@@ -4283,12 +4283,12 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 <<tasksum end>>
 
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
 The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
-''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalculationStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
-* the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the CalculationStream
+''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalcStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
+* the [[Backend]] is responsible for scheduling and [[dispatching|Dispatcher]] the [[calculation stream|CalcStream]]
 * the RenderEngine has the ability to cary out individual frame calculations
 * the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
 
@@ -4296,7 +4296,7 @@ The play process is an conceptual entity linking together several activities in
 !Anatomy of a Play Process
 The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind.
 
-Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalculationStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalculationStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
+Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalcStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalcStream is created, while the existing one is just used to mark any not-yet processed job as superseded.
 
From 737765260d9a4e609a41441ddbb4ac73b06a9b58 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 16 Aug 2011 03:02:55 +0200 Subject: [PATCH 055/108] Test-driven brainstorming: diagnostic adaptor for the engine --- src/proc/engine/engine-diagnostics.hpp | 105 +++++++++++++++++ src/proc/engine/engine-service.cpp | 18 +-- src/proc/engine/engine-service.hpp | 54 +++++---- tests/46engine.tests | 8 ++ tests/47playout.tests | 7 ++ .../proc/engine/calc-stream-test.cpp | 76 ++++++++++++ .../proc/engine/engine-interface-test.cpp | 111 ++++++++++++++++++ wiki/renderengine.html | 4 +- 8 files changed, 353 insertions(+), 30 deletions(-) create mode 100644 src/proc/engine/engine-diagnostics.hpp create mode 100644 tests/47playout.tests create mode 100644 tests/components/proc/engine/calc-stream-test.cpp create mode 100644 tests/components/proc/engine/engine-interface-test.cpp diff --git a/src/proc/engine/engine-diagnostics.hpp b/src/proc/engine/engine-diagnostics.hpp new file mode 100644 index 000000000..bbb4052fb --- /dev/null +++ b/src/proc/engine/engine-diagnostics.hpp @@ -0,0 +1,105 @@ +/* + ENGINE-DIAGNOSTICS.hpp - diagnostic facility to investigate engine operation + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + +/** @file engine-diagnostics.hpp + ** An facility to check and monitor engine operations. + ** Once created, an EngineDiagnostics object connects to the EngineService + ** interface to activate additional tracing facilities within the engine, + ** allowing to watch and verify the creation of individual jobs and further + ** engine state parameters. + ** + ** @see EngineInterface + ** @see engine-interface-test.cpp + ** @see calc-stream-test.cpp + */ + + +#ifndef PROC_ENGINE_ENGINE_DIAGNOSTICS_H +#define PROC_ENGINE_ENGINE_DIAGNOSTICS_H + + +#include "lib/error.hpp" +#include "proc/engine/engine-service.hpp" +//#include "include/dummy-player-facade.h" +//#include "include/display-facade.h" +//#include "proc/engine/calc-stream.hpp" +//#include "proc/mobject/model-port.hpp" +//#include "proc/play/timings.hpp" +//#include "proc/play/output-slot.hpp" +//#include "common/instancehandle.hpp" +//#include "lib/singleton-ref.hpp" +//#include "lib/polymorphic-value.hpp" +//#include "lib/singleton.hpp" +// +#include +//#include +//#include + + +namespace proc { +namespace engine{ + +// using std::string; +// using lumiera::Subsys; +// using lumiera::Display; +// using lumiera::DummyPlayer; + + + + + + /****************************************************** + * Render engine diagnostic facility. Creating an instance + * will activate additional tracing facilities within the + * render engine; results may be investigated through + * EngineDiagnostics public functions. The object acts + * like a smart handle, i.e. the tracing facilities will + * be disabled and disconnected when going out of scope. + */ + class EngineDiagnostics + : boost::noncopyable + { + EngineService& engine_; + + public: + EngineDiagnostics (EngineService& e) + : engine_(e) + { + UNIMPLEMENTED ("attach tracing connector"); + engine_.activateTracing(); + } + + ~EngineDiagnostics() + { + TODO ("detach tracing connector"); + engine_.disableTracing(); + } + }; + + + + + + +} // namespace engine +} // namespace proc +#endif diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp index 40531d4a5..b85353211 100644 --- a/src/proc/engine/engine-service.cpp +++ b/src/proc/engine/engine-service.cpp @@ -38,20 +38,24 @@ namespace engine{ // using std::auto_ptr; // using boost::scoped_ptr; // using std::tr1::bind; - - + + namespace { // hidden local details of the service implementation.... } // (End) hidden service impl details - - - + + + /** storage for the EngineService interface object */ + lib::Singleton EngineService::instance; + + + /** */ EngineService::EngineService() { } - - + + /** */ CalcStream diff --git a/src/proc/engine/engine-service.hpp b/src/proc/engine/engine-service.hpp index 502e63329..6b0d84e5f 100644 --- a/src/proc/engine/engine-service.hpp +++ b/src/proc/engine/engine-service.hpp @@ -22,12 +22,24 @@ /** @file engine-service.hpp ** Access point for the (core) calculation service of the render engine. - ** This public service is provided by the Proc-Layer, but actually implemented - ** using backend services (especially the scheduler). The central concept provided - ** through this facade interface is that of a calculation stream. On the - ** implementation side, these get translated into a series of jobs invoking - ** render nodes, to be invoked through the scheduler in the backend layer. + ** This Proc-Layer internal service is provided for use by the Player subsystem. + ** The actual implementation is forwarded to backend services (especially the scheduler). + ** The EngineService singleton has no state beyond the jobs currently managed by the + ** scheduler; when the latter isn't available, any invocation will throw. ** + ** The central concept provided through this facade interface is the calculation stream. + ** This represents a series of calculations, expected to happen in a timely fashion and in order + ** to deliver a frame data stream onto an opened output connection. On the implementation side, + ** a calculation stream will be translated into a series of jobs invoking render nodes, + ** to be executed through the scheduler in the backend layer. + ** + ** While the individual CalcStram is simple, linear and unmodifiable, any CalcStream may be + ** \em superseded by a new definition. In this case, the engine will care for a seamless + ** switch and continuation; under the hood, there is a mechanism to discard resources + ** tied to the original CalcStream, once the switch to the new definition is complete. + ** + ** @see EngineInterface_test + ** @see CalcStream_test ** @see proc::play::PlayerService */ @@ -46,6 +58,7 @@ //#include "common/instancehandle.hpp" //#include "lib/singleton-ref.hpp" #include "lib/polymorphic-value.hpp" +#include "lib/singleton.hpp" // #include //#include @@ -82,18 +95,6 @@ namespace engine{ : boost::noncopyable { -// string error_; -// Subsys::SigTerm notifyTermination_; - - - /* === Interface Lifecycle === */ - -// typedef lumiera::InstanceHandle< LUMIERA_INTERFACE_INAME(lumieraorg_DummyPlayer, 0) -// , DummyPlayer -// > ServiceInstanceHandle; - -// lib::SingletonRef implInstance_; -// ServiceInstanceHandle serviceInstance_; /* The following typedefs allow to hand out predefined * Quality-of-Service strategy definitions as value objects, @@ -117,8 +118,8 @@ namespace engine{ }; -// typedef lib::polyvalue::CloneValueSupport _ClonableQoS_Strategy; - typedef lib::PolymorphicValue QoS_Definition; + typedef lib::polyvalue::CloneValueSupport _Clonable_QoS_Strategy; + typedef lib::PolymorphicValue QoS_Definition; static QoS_Definition QoS_DEFAULT; static QoS_Definition QoS_BACKGROUND; @@ -127,10 +128,15 @@ namespace engine{ static QoS_Definition QoS_COMPROMISE; + /** access point to the Engine Interface. + * @internal this is an facade interface for internal use + * by the player. Client code should use the Player. + */ + static lib::Singleton instance; - EngineService(); /////TODO (Subsys::SigTerm terminationHandle); - ~EngineService() { } /////TODO notifyTermination_(&error_); } + EngineService(); + ~EngineService() { } CalcStream calculate(ModelPort mPort, @@ -143,6 +149,12 @@ namespace engine{ Timings nominalTimings, Quality serviceQuality =QoS_BACKGROUND); + + protected: + void activateTracing(); + void disableTracing(); ///< EX_FREE + + friend class EngineDiagnostics; }; diff --git a/tests/46engine.tests b/tests/46engine.tests index e669a11cb..28f68b6a6 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -7,6 +7,14 @@ return: 0 END +PLANNED "Engine Interface basics" EngineInterface_test < + + 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 "lib/test/run.hpp" +#include "lib/error.hpp" + +#include "proc/engine/engine-service.hpp" + +//#include +//#include +//#include + +using test::Test; +//using std::cout; +//using std::rand; + + +namespace proc { +namespace engine{ +namespace test { + + namespace { // test fixture... + + } // (End) test fixture + + + + /******************************************************************* + * @test detailed coverage of the various CalcStream flavours + * supported by the render engine interface. + * + * @todo WIP-WIP-WIP + * + * @see EngineInterface_test for the plain flat standard case + * @see EngineService + * @see CalcStream + * @see OutputSlotProtocol_test + */ + class CalcStream_test : public Test + { + + virtual void + run (Arg) + { + UNIMPLEMENTED ("in-depth coverage of calculation streams"); + } + + }; + + + /** Register this test class... */ + LAUNCHER (CalcStream_test, "function engine"); + + + +}}} // namespace proc::engine::test diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp new file mode 100644 index 000000000..be9ec028b --- /dev/null +++ b/tests/components/proc/engine/engine-interface-test.cpp @@ -0,0 +1,111 @@ +/* + EngineInterface(Test) - verify basics of the engine (scheduling) service + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "lib/test/run.hpp" +#include "lib/error.hpp" + +#include "proc/engine/calc-stream.hpp" +#include "proc/engine/engine-service.hpp" +#include "proc/engine/engine-diagnostics.hpp" +#include "proc/play/diagnostic-output-slot.hpp" +#include "proc/mobject/model-port.hpp" +#include "proc/asset/pipe.hpp" + +//#include +//#include +//#include + +using test::Test; +//using std::cout; +//using std::rand; + + +namespace proc { +namespace engine{ +namespace test { + + using asset::Pipe; + using asset::PPipe; + using mobject::ModelPort; + + typedef asset::ID PID; + + + namespace { // test fixture... + + } // (End) test fixture + + + + /******************************************************************* + * @test cover the basic service exposed at the engine interface: + * create a calculation stream and verify the translation + * into individual jobs. + * + * This test relies on the engine's diagnostic facilities, allowing + * to log and verify the generated jobs without needing to execute + * them. So this test doesn't actually run the engine. There are + * \link OutputSlotProtocol_test other tests \endlink covering + * the output generation separate from the engine. + * + * @see CalcStream_test more in-depth coverage of the various + * flavours of calculation streams supported by the engine + * @see EngineService + * @see CalcStream + * @see OutputSlotProtocol_test + */ + class EngineInterface_test : public Test + { + + virtual void + run (Arg) + { + UNIMPLEMENTED ("simple standard case of Engine interface usage"); + + EngineService& engine = EngineService::instance(); + EngineDiagnostics monitor(engine); + + PID pipe = Pipe::query("id(dummy)"); + ModelPort port(pipe); + + OutputSlot& oSlot = DiagnosticOutputSlot::build(); + Allocation output = oSlot.allocate(); + Timings timings; /////////TODO + + // Invoke test subject... + CalcStream calc = engine.calculate(port, timings, output); + + ////TODO some direct checks on the calculation stream?? + + CHECK (monitor.has_scheduled_jobs_for(timings)); + } + + }; + + + /** Register this test class... */ + LAUNCHER (EngineInterface_test, "function engine"); + + + +}}} // namespace proc::engine::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 49a1a6096..de9778782 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1916,7 +1916,7 @@ To support this usage pattern, the Fixture implementation makes use of the [[PIm * moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
 Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes (&rarr; CalcStream) are known to be terminated.
 
@@ -1934,7 +1934,7 @@ Basically the concern is that each new CalcStream had to access the shared count
 There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a calculation stream is done and re-evaluate all those noted results on later occasion.
 
 !!exploiting the frame-dispatch step
-Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process / a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
+Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process (maybe even each CalcStram)  a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
 
 !!assessment {{red{WIP 12/10}}}
 But the primary question here is to judge the impact of such an implementation. What would be the costs?

From 7efde06569343c818945ad747b81864fb3f63487 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 18 Aug 2011 12:31:26 +0200
Subject: [PATCH 056/108] some stubs to make it compile...

---
 src/proc/engine/engine-diagnostics.hpp                 | 10 +++++++++-
 src/proc/engine/engine-service.cpp                     |  3 +--
 src/proc/play/diagnostic-output-slot.hpp               |  8 ++++++++
 tests/components/proc/engine/engine-interface-test.cpp |  6 +++++-
 4 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/proc/engine/engine-diagnostics.hpp b/src/proc/engine/engine-diagnostics.hpp
index bbb4052fb..be4e50859 100644
--- a/src/proc/engine/engine-diagnostics.hpp
+++ b/src/proc/engine/engine-diagnostics.hpp
@@ -43,7 +43,7 @@
 //#include "include/display-facade.h"
 //#include "proc/engine/calc-stream.hpp"
 //#include "proc/mobject/model-port.hpp"
-//#include "proc/play/timings.hpp"
+#include "proc/play/timings.hpp"
 //#include "proc/play/output-slot.hpp"
 //#include "common/instancehandle.hpp"
 //#include "lib/singleton-ref.hpp"
@@ -62,6 +62,7 @@ namespace engine{
 //    using lumiera::Subsys;
 //    using lumiera::Display;
 //    using lumiera::DummyPlayer;
+  using proc::play::Timings;
   
   
   
@@ -93,6 +94,13 @@ namespace engine{
           TODO ("detach tracing connector");
           engine_.disableTracing();
         }
+      
+      /** */
+      bool
+      has_scheduled_jobs_for (Timings const& timings)
+        {
+          UNIMPLEMENTED ("Engine Diagnostics: query scheduled jobs");
+        }
     };
   
   
diff --git a/src/proc/engine/engine-service.cpp b/src/proc/engine/engine-service.cpp
index b85353211..e9d188a78 100644
--- a/src/proc/engine/engine-service.cpp
+++ b/src/proc/engine/engine-service.cpp
@@ -106,8 +106,7 @@ namespace engine{
   class PriorityQoS
     : public DefaultQoS
     {
-      CalcType type_;
-      
+    
     public:
       PriorityQoS ()
         : DefaultQoS(PLAYBACK)
diff --git a/src/proc/play/diagnostic-output-slot.hpp b/src/proc/play/diagnostic-output-slot.hpp
index e0e4a7f0a..b699e0ae8 100644
--- a/src/proc/play/diagnostic-output-slot.hpp
+++ b/src/proc/play/diagnostic-output-slot.hpp
@@ -62,6 +62,14 @@ namespace play {
     : public OutputSlot
     {
     public:
+      /** build a new Diagnostic Output Slot instance,
+       *  discard the existing one. Use the static query API
+       *  for investigating collected data. */
+      static OutputSlot&
+      build()
+        {
+          UNIMPLEMENTED ("Diagnostic Output Slot instance");
+        }
       
     private:
       
diff --git a/tests/components/proc/engine/engine-interface-test.cpp b/tests/components/proc/engine/engine-interface-test.cpp
index be9ec028b..fd55ee76f 100644
--- a/tests/components/proc/engine/engine-interface-test.cpp
+++ b/tests/components/proc/engine/engine-interface-test.cpp
@@ -27,6 +27,7 @@
 #include "proc/engine/calc-stream.hpp"
 #include "proc/engine/engine-service.hpp"
 #include "proc/engine/engine-diagnostics.hpp"
+#include "proc/play/output-slot.hpp"
 #include "proc/play/diagnostic-output-slot.hpp"
 #include "proc/mobject/model-port.hpp"
 #include "proc/asset/pipe.hpp"
@@ -47,8 +48,11 @@ namespace test  {
   using asset::Pipe;
   using asset::PPipe;
   using mobject::ModelPort;
+  using proc::play::OutputSlot;
+  using proc::play::DiagnosticOutputSlot;
   
   typedef asset::ID PID;
+  typedef OutputSlot::Allocation Allocation;
 
   
   namespace { // test fixture...
@@ -84,7 +88,7 @@ namespace test  {
           
           EngineService& engine = EngineService::instance();
           EngineDiagnostics monitor(engine);
-
+          
           PID pipe = Pipe::query("id(dummy)");
           ModelPort port(pipe);
           

From d1a5b9a9143bc354ec748a1c970ad7fa33184e8e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 24 Aug 2011 01:26:36 +0200
Subject: [PATCH 057/108] main: temporarily disable dependencies...

disable dependency declarations for subsystems
not yet implemented, to allow the lumiera executable
to start up
---
 src/lumiera/main.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp
index f738337bf..69b5ecc22 100644
--- a/src/lumiera/main.cpp
+++ b/src/lumiera/main.cpp
@@ -63,11 +63,11 @@ main (int argc, const char* argv[])
       lumiera::Option options (args);
       application.init (options);
       
-      session.depends (builder);
+//    session.depends (builder);
       netNode.depends (session);
       netNode.depends (engine);
-      playOut.depends (engine);
-      playOut.depends (session);
+//    playOut.depends (engine);
+//    playOut.depends (session);
 //    lumigui.depends (session);   //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin
 //    lumigui.depends (engine);
       player.depends (playOut);    //////TODO dummy player, until we're able to render

From 0706b83a46dc78895d79bbe8086e8fe6054e31bd Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 25 Aug 2011 15:08:27 +0200
Subject: [PATCH 058/108] fix a warning

---
 src/lib/lifecycle.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/lib/lifecycle.cpp b/src/lib/lifecycle.cpp
index 1894ae2fb..24bbf04ae 100644
--- a/src/lib/lifecycle.cpp
+++ b/src/lib/lifecycle.cpp
@@ -76,11 +76,11 @@ namespace lumiera {
 extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */
   
   
-  extern const char * lumiera_ON_BASIC_INIT       = lumiera::ON_BASIC_INIT;
-  extern const char * lumiera_ON_GLOBAL_INIT      = lumiera::ON_GLOBAL_INIT;
-  extern const char * lumiera_ON_GLOBAL_SHUTDOWN  = lumiera::ON_GLOBAL_SHUTDOWN;
+  const char * lumiera_ON_BASIC_INIT       = lumiera::ON_BASIC_INIT;
+  const char * lumiera_ON_GLOBAL_INIT      = lumiera::ON_GLOBAL_INIT;
+  const char * lumiera_ON_GLOBAL_SHUTDOWN  = lumiera::ON_GLOBAL_SHUTDOWN;
   
-  extern const char * lumiera_ON_EMERGENCY        = lumiera::ON_EMERGENCY;
+  const char * lumiera_ON_EMERGENCY        = lumiera::ON_EMERGENCY;
   
   
   

From 95bb5e64aa01393d05ad5479842f581efe079a2b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 4 Sep 2011 01:54:36 +0200
Subject: [PATCH 059/108] WIP desiging the interplay of BufferProvider and
 BuffHandle

---
 src/gui/display-service.hpp             |   1 +
 src/proc/engine/buffer-provider.cpp     |  35 ++++++-
 src/proc/engine/buffer-provider.hpp     |  14 ++-
 src/proc/engine/buffhandle.hpp          | 118 +++++++++++++-----------
 src/proc/engine/nodeinvocation.hpp      |   2 +-
 src/proc/mobject/output-designation.hpp |   2 +
 wiki/renderengine.html                  |  60 +++++++++---
 7 files changed, 162 insertions(+), 70 deletions(-)

diff --git a/src/gui/display-service.hpp b/src/gui/display-service.hpp
index 1dec2c192..1c5cda79f 100644
--- a/src/gui/display-service.hpp
+++ b/src/gui/display-service.hpp
@@ -50,6 +50,7 @@
 #include "common/instancehandle.hpp"
 #include "lib/singleton-ref.hpp"
 #include "lib/scoped-ptrvect.hpp"
+#include "include/logging.h"
 
 #include 
 #include 
diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index e58168c9a..a3d0bc51b 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -24,12 +24,45 @@
 #include "proc/engine/buffer-provider.hpp"
 
 namespace engine {
+  
+  
+  namespace { // impl. details and definitions
+    
+    const uint DEFAULT_DESCRIPTOR = 0;
+  }
 
 
   BufferProvider::~BufferProvider() { }
   
   
-  /**  */
+  /** @internal verify the given descriptor.
+   *  @return true if it corresponds to a buffer
+   *          currently locked and usable by client code
+   */
+  bool
+  BufferProvider::checkValidity (BufferDescriptor const&)
+  {
+    UNIMPLEMENTED ("BufferProvider basic and default implementation");
+  }
+  
+  
+  BufferDescriptor
+  BufferProvider::getDefaultDescriptor()
+  {
+    return BufferDescriptor (*this, DEFAULT_DESCRIPTOR);
+  }
+      
+      
+  
+  
+  /* === BufferDescriptor and BuffHandle === */
+  
+  bool
+  BufferDescriptor::checkValidity()
+  {
+    return provider_.checkValidity(*this);
+  }
+  
 
 
 } // namespace engine
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index 6b6429c21..7f8f4f99b 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -24,7 +24,7 @@
  ** Abstraction to represent buffer management and lifecycle within the render engine.
  ** It turns out that --  throughout the render engine implementation -- we never need
  ** direct access to the buffers holding media data. Buffers are just some entity to be \em managed,
- ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementatino detail.
+ ** i.e. "allocated", "locked" and "released"; the actual meaning of these operations is an implementation detail.
  ** The code within the render engine just pushes around BufferHandle objects, which act as a front-end,
  ** being created by and linked to a BufferProvider implementation. There is no need to manage the lifecycle
  ** of buffers automatically, because the use of buffers is embedded into the render calculation cycle,
@@ -73,10 +73,18 @@ namespace engine {
     public:
       virtual ~BufferProvider();  ///< this is an interface
       
-      ///////TODO: is there any generic way to obtain a BufferDescriptor; then we should expose it here...
       
       virtual BuffHandle lockBufferFor (BufferDescriptor const&)  =0;
-      virtual void releaseBuffer (BuffHandle const&)              =0;  ////////TODO not quite sure what information to pass here
+      virtual void releaseBuffer (BuffHandle const&)              =0;
+      
+      
+      /** describe the kind of buffer managed by this provider */
+      BufferDescriptor getDefaultDescriptor();
+      
+      
+      /* === API for BuffHandle internal access === */
+      
+      bool checkValidity (BufferDescriptor const&);
       
     };
   
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index d7c5ecd87..60992c058 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -52,71 +52,37 @@
 
 namespace engine {
   
+  class BufferProvider;
+  
   
   
   /**
-   * Handle for a buffer for processing data, abstracting away the actual implementation.
-   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * An opaque descriptor to identify the type and further properties of a data buffer.
+   * For each kind of buffer, there is somewhere a BufferProvider responsible for the
+   * actual storage management. This provider may "lock" a buffer for actual use,
+   * returning a BuffHandle.
    * 
-   * @todo as of 6/2011 it isn't clear how buffer handles are actually created
-   *       and how the lifecycle (and memory) management works
+   * @todo try to move that definition into buffer-provider.hpp   ////////////////////////////////////TICKET #249
    */
-  struct BuffHandle
-    : lib::BoolCheckable
+  class BufferDescriptor
     {
-      typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
-      typedef Buff* PBuff;
+      BufferProvider* provider_;
+      uint64_t subClassification_;
       
-      PBuff 
-      operator->()  const 
-        { 
-          return pBuffer_; 
-        }
-      Buff&
-      operator* ()  const
-        {
-          ENSURE (pBuffer_);
-          return *pBuffer_;
-        }
+      BufferDescriptor(BufferProvider& manager, uint64_t detail)
+        : provider_(&manager)
+        , subClassification_(detail)
+      { }
       
-      bool
-      isValid()  const
-        {
-          return pBuffer_;
-        }
+      friend class BufferProvider;
       
+    public:
+      // using standard copy operations
       
-      //////////////////////TODO: the whole logic how to create a BuffHandle needs to be solved in a more clever way. --> TICKET #249
-      BuffHandle()
-        : pBuffer_(0),
-          sourceID_(0)
-        { }
-      
-      /** 
-       * @deprecated placeholder implementation
-       * @todo rework the Lifecycle handling of buffers  //////////TICKET #249
-       */
-      BuffHandle(PBuff existingBuffer)
-        : pBuffer_(existingBuffer)
-        , sourceID_(0)
-        { }
-      
-    private:
-      PBuff pBuffer_; 
-      long sourceID_;   ////TICKET #249 this is a placeholder for a "type-like information", to be used for lifecycle management and sanity checks....
+      bool checkValidity();
     };
   
   
-  /**
-   * Buffer Type information.
-   * Given a BufferDescriptor, it is possible to allocate a buffer
-   * of suitable size and type by using BufferProvider::lockBuffer().
-   */
-  struct BufferDescriptor
-    {
-      long typeToken_; 
-    };
-  
   
   class ProcNode;
   typedef ProcNode* PNode;
@@ -135,5 +101,53 @@ namespace engine {
   
   
   
+  
+  
+  /**
+   * Handle for a buffer for processing data, abstracting away the actual implementation.
+   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * 
+   * @todo as of 6/2011 it isn't clear how buffer handles are actually created
+   *       and how the lifecycle (and memory) management works                  //////////////////////TICKET #249 rework BuffHandle creation and usage
+   */
+  class BuffHandle
+    : public lib::BoolCheckable
+    {
+      typedef lumiera::StreamType::ImplFacade::DataBuffer Buff;
+      
+      BufferDescriptor descriptor_;
+      Buff* pBuffer_; 
+      
+      
+    public:
+      /** @internal a buffer handle may be obtained by "locking"
+       *  a buffer from the corresponding BufferProvider */
+      BuffHandle(BufferDescriptor const& typeInfo, Buff* storage = 0)
+        : descriptor_(typeInfo)
+        , pBuffer_(storage)
+        { }
+      
+      // using standard copy operations
+      
+      
+      Buff&
+      operator* ()  const
+        {
+          ENSURE (pBuffer_);
+          return *pBuffer_;
+        }
+      
+      bool
+      isValid()  const
+        {
+          return bool(pBuffer_)
+              && descriptor_.checkValidity();
+        }
+      
+    };
+  
+  
+  
+  
 } // namespace engine
 #endif
diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
index ad9c6d402..1a7575297 100644
--- a/src/proc/engine/nodeinvocation.hpp
+++ b/src/proc/engine/nodeinvocation.hpp
@@ -172,7 +172,7 @@ namespace engine {
     };
     
     
-                                                                                                    ////////////TICKET #249  this strategy should better be hidden within the BuffHanle ctor (and type-erased after creation)
+                                                                                                    ////////////TICKET #249  this strategy should better be hidden within the BuffHandle ctor (and type-erased after creation)
     struct AllocBufferFromParent  ///< using the parent StateAdapter for buffer allocations
       : Invocation
       {
diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp
index e5d32813d..93f98f518 100644
--- a/src/proc/mobject/output-designation.hpp
+++ b/src/proc/mobject/output-designation.hpp
@@ -59,6 +59,8 @@ namespace mobject {
    * @todo couldn't the inline buffer be "downgraded" to InPlaceBuffer or PolymorphicValue??
    *       Seemingly we never-ever need to re-discover the erased type of the embedded spec.
    *       Thus for this to work, we'd just need to add an "empty" spec       ///////////////////TICKET #723
+   * 
+   * @see OutputMapping_test
    */
   class OutputDesignation
     {
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index de9778782..8cae9bde7 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1077,12 +1077,45 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
-
-
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''buffer handles'', which act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
+
+
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
+
+Obviously, overloading a pointer with semantic meaning isn't exactly a brilliant idea -- and the usual answer is to embed this pointer into a smart handle, which also yields the nice side-effect of explaining this design to the reader. Thus a buffer handle
+* can only be obtained from a BufferProvider
+* can be used to identify a buffer
+* can be dereferenced
+* can be copied 
+
+!design quest: buffer type information
+To perform anything useful with such a buffer handle, the client code needs some additional information, which can be generalised into a //type information:// Either, the client needs to know the size and kind of data to expect in the buffer, maybe just assume to get a specific buffer with suitably dimensions, or the client needs to know which buffer provider to contact for any management operations on that buffer (handle). And, at some point there needs to be a mechanism to verify the validity of a handle. But all of this doesn't mean that it's necessary to encode or embedd this information directly into the handle -- it might also be stored into a registration table (which has the downside of creating contention), or it might just be attached implicitly to the invocation context.
+
+Just linking this type information to the context is certainly the most elegant solution, but also by far the most difficult to achieve -- not to mention the implicit dependency on a very specific invocation situation. So for now (9/2011) it seems best to stick to the simple and explicit implementation, just keeping that structural optimisation in mind. And the link to this buffer type information should be made explicit within the definition anyway, even if we choose to employ another design tradeoff later.
+* thus the conclusion is: we introduce a ''descriptor object'', which will be stored within the handle
+* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to organise implementation details
+
+
+!sanity checks
+there are only limited sanity checks, and they can be expected to be optimised away for production builds.
+Basically the client is responsible for sane buffer access.
+
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
 * that buffers are marked as free ("released") after doing the actual calculations.
 
+!operations
+While BufferProvider is an interface meant to be backed by various different kinds of buffer and memory management approaches, there is a common set of operations to be supported by any of them
+;announcing
+:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations (or similar mechanisms to ensure the avialability) to happen right away; the BufferProvider will then return the actual number of buffers guraanteed to be available. This announcing step is optional an can happen anytime before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
+;locking
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, the BufferProvider might at that point allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking might fail and raise an exception. To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+;attaching
+:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be automatically destroyed when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, which will be invoked in a similar manner. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
+;releasing
+:buffers need to be released explicitly by the client code. This renders the corresponding BuffHandle invalid, (optionally) invokes a destructor function of an attached object and maybe reclaims the buffer memory
+
 __see also__
 &rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
 &rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
@@ -1853,8 +1886,9 @@ Some further details
 * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
 
-
-
a specially configured view -- joining together high-level and low-level model
+
+
a specially configured view -- joining together high-level and low-level model.
+The Fixture acts as //isolation layer// between the two models, and as //backbone to attach the render nodes.//
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
 * every MObject is attached by an ExplicitPlacement, which declares a fixed position (Time, [[Pipe|OutputDesignation]])
@@ -1886,8 +1920,8 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
 :Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{<number_of_segments>}}} separate exit nodes, but of course an exit node may be //mute.//
 
-
-
Generally speaking, the [[here|Fixture]] comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
+
+
Generally speaking, the datastructure to implement the ''Fixture'' (&rarr; see a more general description [[here|Fixture]]) is comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
 This page focusses on the actual data structure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
 
 !transactional switch
@@ -3238,7 +3272,7 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
@@ -3255,19 +3289,19 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created using a [[Pipe]]-ID and &mdash; they become real by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting to allocate connections with the "first of each kind".
+Initially, [[Output designations|OutputDesignation]] are typically just local or relative references to another OutputDesignation; yet after some resolution steps, we'll arrive at an OutputDesignation //defined absolute.// Basically, these are equivalent to a [[Pipe]]-ID choosen as target for the connection and -- they become //real//&nbsp; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these -- and only from these -- active exit points of the model. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping just relies on relative addressing of the output sinks, starting to allocate connections with the //first of each kind// (e.g. "connect to the first usable audio output destination").
 
-We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
+We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven and constrained by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format -- again with fixed and pre-settled channel configuration ({{red{TODO 9/11}}} when configurting a render process, it might be necessary to pre-compute the //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
 !Connection to external outputs
-External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
+External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read: actually there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
 Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
 &rarr; the OutputManager interface describes handling this mapping association
 &rarr; see also the PlayService
 
 !the global output manager
-While within the model routing is done mostly just by referring to an OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes mapping and the ability to control and manage it. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+While within the model routing is done mostly just by referring to an OutputDesignation, at some point we need to map these abstract designations to real output capabilities. This OutputManager interface exposes these mappings and allows to control and manage them. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating an mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
 
 The relation between the central OutputDirector and the peripheral OutputManager implementations is hierarchical. Because output slots are usually registered rather at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. Resolving a global pipe into an output slot is the core concern of any OutputManager implementation. Thus, when there is a locally preconfigured mapping, like e.g. for a viewer's video master pipe to the output slot installed by the corresponding GUI viewer element, then this mapping will picked up foremost to resolve the video master output.
 
@@ -3324,7 +3358,7 @@ Thus the mapping is a copyable value object, based on a associative array. It ma
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
-
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
 This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
 
@@ -3372,7 +3406,7 @@ If we accept to retrieve the buffer(s) via an indirection, which we kind of do a
 &rArr; conclusion: go for the unified approach!
 
 !!!unified data exchange cycle
-The nominal time of a frame to be delivered is used as an ID throughout that cycle
+The planned delivery time of a frame is used as an ID throughout that cycle
 # within a defined time window prior to delivery, the client can ''allocate and retrieve the buffer'' from the BufferProvider.
 # the client has to ''emit'' within a (short) time window prior to deadline
 # now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.

From 8016547d9c8a3ec4ec07e362c5309010b89054c6 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 16 Sep 2011 01:58:13 +0200
Subject: [PATCH 060/108] rework and clarify node invocation sequence

while passing by, identified quite some
node invocation code to be rewritten
---
 src/proc/engine/buffer-provider.cpp |  4 +-
 src/proc/engine/buffhandle.hpp      | 10 ++--
 src/proc/engine/bufftable.hpp       | 22 ++++++-
 src/proc/engine/nodeinvocation.hpp  |  4 +-
 src/proc/engine/stateproxy.cpp      |  2 +-
 src/proc/engine/stateproxy.hpp      |  2 +-
 src/proc/state.hpp                  |  3 +-
 wiki/renderengine.html              | 91 ++++++++++++++++++++---------
 8 files changed, 99 insertions(+), 39 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index a3d0bc51b..57660a572 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -58,9 +58,9 @@ namespace engine {
   /* === BufferDescriptor and BuffHandle === */
   
   bool
-  BufferDescriptor::checkValidity()
+  BufferDescriptor::checkValidity()  const
   {
-    return provider_.checkValidity(*this);
+    return provider_->checkValidity(*this);
   }
   
 
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 60992c058..835d0329c 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -79,7 +79,7 @@ namespace engine {
     public:
       // using standard copy operations
       
-      bool checkValidity();
+      bool checkValidity()  const;
     };
   
   
@@ -88,9 +88,9 @@ namespace engine {
   typedef ProcNode* PNode;
   
   
-  struct ChannelDescriptor  ///////TODO collapse this with BufferDescriptor?
+  struct ChannelDescriptor  ///////TODO really need to define that here? it is needed for node wiring only
     {
-      BufferDescriptor bufferType;
+      const lumiera::StreamType * bufferType;                /////////////////////////////////////////TICKET #828
     };
   
   struct InChanDescriptor : ChannelDescriptor
@@ -120,9 +120,11 @@ namespace engine {
       
       
     public:
+      typedef Buff* PBuff;
+      
       /** @internal a buffer handle may be obtained by "locking"
        *  a buffer from the corresponding BufferProvider */
-      BuffHandle(BufferDescriptor const& typeInfo, Buff* storage = 0)
+      BuffHandle(BufferDescriptor const& typeInfo, PBuff storage = 0)
         : descriptor_(typeInfo)
         , pBuffer_(storage)
         { }
diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp
index 3682b51ff..ac5773e24 100644
--- a/src/proc/engine/bufftable.hpp
+++ b/src/proc/engine/bufftable.hpp
@@ -48,6 +48,10 @@ namespace engine {
      * data buffers. The tables are supposed to be implemented as bare "C" arrays,
      * thus the array of real buffer pointers can be fed directly to the
      * processing function of the respective node.
+     * 
+     * @todo this whole design is a first attempt and rather clumsy. It should be reworked
+     *       to use a single contiguous memory area and just layer the object structure on top
+     *       (by using placement new). Yet the idea of an stack-like organisation should be retained
      */
   struct BuffTable
     {
@@ -64,7 +68,21 @@ namespace engine {
   
   class BuffTableStorage
     {
-      vector        hTab_;
+      /////////////////////////////////////////////////////////////////////////TICKET #826  need to be reworked entirely
+      /** just a placeholder to decouple the existing code
+       *  from the reworked BuffHandle logic. The existing
+       *  code in turn will be reworked rather fundamentally
+       */
+      struct BuffHaXXXX
+        : BuffHandle
+        {
+          BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ }
+          static BufferDescriptor const& 
+          just_satisfy_the_compiler() { }
+        };
+        
+                                           ////////////////////////////////////TICKET #825  should be backed by mpool and integrated with node invocation
+      vector        hTab_;
       vector pTab_;
       size_t level_;
       
@@ -139,7 +157,7 @@ namespace engine {
         {
           const uint nrO(wd.nrO);
           
-          // Setup the public visible table locations
+          // Setup the publicly visible table locations
           this->outHandle = &tab_.first[ 0 ];
           this->inHandle  = &tab_.first[nrO];
           this->outBuff   = &tab_.second[ 0 ];
diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
index 1a7575297..695406cda 100644
--- a/src/proc/engine/nodeinvocation.hpp
+++ b/src/proc/engine/nodeinvocation.hpp
@@ -180,7 +180,7 @@ namespace engine {
           : Invocation(sta, w, outCh) {}
         
         virtual BuffHandle
-        allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); }          ////////////TODO: actually implement the "allocate from parent" logic!
+        allocateBuffer (const lumiera::StreamType* ty) { return parent_.allocateBuffer(ty); }          ////////////TODO: actually implement the "allocate from parent" logic!
       };
     
     struct AllocBufferFromCache   ///< using the global current State, which will delegate to Cache
@@ -190,7 +190,7 @@ namespace engine {
           : Invocation(sta, w, outCh) {}
         
         virtual BuffHandle
-        allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
+        allocateBuffer (const lumiera::StreamType* ty) { return current_.allocateBuffer(ty); }
       };
     
   
diff --git a/src/proc/engine/stateproxy.cpp b/src/proc/engine/stateproxy.cpp
index cb32f3b57..90e36a094 100644
--- a/src/proc/engine/stateproxy.cpp
+++ b/src/proc/engine/stateproxy.cpp
@@ -38,7 +38,7 @@ namespace engine {
   
   /** @internal */
   BuffHandle
-  StateProxy::allocateBuffer (BufferDescriptor const&)
+  StateProxy::allocateBuffer (const lumiera::StreamType*)
   {
     UNIMPLEMENTED ("allocate a suitable buffer to hold a frame of the denoted type");
   }
diff --git a/src/proc/engine/stateproxy.hpp b/src/proc/engine/stateproxy.hpp
index 7125790f7..bca680be3 100644
--- a/src/proc/engine/stateproxy.hpp
+++ b/src/proc/engine/stateproxy.hpp
@@ -38,7 +38,7 @@ namespace engine {
       
     private: /* === top-level implementation of the State interface === */
       
-      BuffHandle allocateBuffer (BufferDescriptor const&);
+      BuffHandle allocateBuffer (const lumiera::StreamType*);              //////////////////////////TICKET #828
       
       void releaseBuffer (BuffHandle& bh);
       
diff --git a/src/proc/state.hpp b/src/proc/state.hpp
index ca0a6a44c..f0e863aaa 100644
--- a/src/proc/state.hpp
+++ b/src/proc/state.hpp
@@ -54,6 +54,7 @@ namespace engine {
    */
   class State
     {
+                                   ////////////////////////////////////////////////TICKET #826  expected to be reworked to quite some extent (9/2011)
     public:
       /** allocate a new writable buffer with type and size according to
        *  the BufferDescriptor. The actual provider of this buffer depends
@@ -62,7 +63,7 @@ namespace engine {
        *  @return a BuffHandle encapsulating the information necessary to get
        *          at the actual buffer address and for releasing the buffer.
        */
-      virtual BuffHandle allocateBuffer (BufferDescriptor const&)  =0;
+      virtual BuffHandle allocateBuffer (const lumiera::StreamType*)  =0;
       
       /** resign control of the buffer denoted by the handle */
       virtual void releaseBuffer (BuffHandle&)  =0;
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 8cae9bde7..6cff8960f 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1099,7 +1099,29 @@ there are only limited sanity checks, and they can be expected to be optimised a
 Basically the client is responsible for sane buffer access.
 
-
+
+
Buffers are used to hold the media data for processing and output. Within the Lumiera RenderEngine and [[Player]] subsystem, we use some common concepts to handle the access and allocation of working buffers. Yet this doesn't imply having only one central authority in charge of every buffer -- such an approach wouldn't be possible (due to collaboration with external systems) and wouldn't be desirable either. Rather, there are some common basic usage //patterns// -- and there are some core interfaces used throughout the organisation of the rendering process.
+
+Mostly, the //client code,// i.e. code in need of using buffers, can access some BufferProvider, thereby delegating the actual buffer management. This binds the client to adhere to kind of a //buffer access protocol,// comprised of the ''announcing'', ''locking'', optionally ''attaching'' and finally the ''releasing'' steps. Here, the actual buffer management within the provider is a question of implementation and will be configured during build-up of the scope in question.
+
+!usage situations
+;rendering
+:any calculations and transformations of media data typically require an input- and output buffer. To a large extent, these operations will be performed by specialised libraries, resulting in a call to some plain-C function receiving pointers to the required working buffers. Our invocation code has the liability to prepare and provide those pointers, relying on a BufferProvider in turn.
+;output
+:most any of the existing libraries for handling external output require the client to adhere to some specific protocol. Often, this involves some kind of callback invoked at the external library's discretion, thus forcing our engine to prepare data within an intermediary buffer. Alternatively, the output system might provide some mechanism to gain limited direct access to the output buffers, and such an access can again be exposed to our internal client code through the BufferProvider abstraction.
+
+!primary implementations
+;memory pool
+:in all those situations, where we just need a working buffer for some time, we can rely on our internal custom memory allocator.
+:{{red{~Not-Yet-Implemented as of 9/11}}} -- as a fallback we just rely on heap allocations through the language runtime
+;frame cache
+:whenever a calculated result may be of further interest beyond the immediate need triggering the calculation, it might be eligible for caching.
+:The Lumiera ''frame cache'' is a special BufferProvider, maintaining a larger pool of buffers which can be pinned and kept around for some time,
+:accomodating limited resources and current demand for fresh result buffers.
+
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
@@ -1118,6 +1140,7 @@ While BufferProvider is an interface meant to be backed by various different kin
 
 __see also__
 &rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
+&rarr; more about BufferManagement within the RenderEngine and [[Player]] subsystem
 &rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
 
@@ -3143,16 +3166,27 @@ While the general approach and reasoning remains valid, a lot of the details loo In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
-
-
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. There is no such thing as an "engine object" calling nodes iteratively or table driven, rather, the nodes themselves issue recursive calls to their predecessor(s). For this to work, we need the nodes to adhere to a specific protocol:
-# Node is pulled, with a StateProxy object as parameter (encapsulating the access to the frames or buffers)
-# Node may now access current parameter values, using the state accessible via the StateProxy
-# using it's //input-output and wiring descriptor,// the Node creates a StateAdapter wrapping the StateProxy for allocating buffers and accessing the required input
-# StateAdapter might first try to get the output frames from the Cache in the Backend. In case of failure, a {{{process()}}} call is prepared by generating {{{pull()}}} call(s) for the input
-# as late as possible, typically on return, these recursive pull-calls have allocated a buffer containing the input data.
-# when input is ready prior to the {{{process()}}} call, output buffers will be allocated, either from the cache, or (if not caching) from the "parent" StateAdapter up the callstack.
-# after all buffers are available, the StateAdapter issues the {{{process()}}} call back to the originating node, which now may dereference the frame pointers and do its calculations
-# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+
+
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. Meaning that there isn't an central entity invoking nodes consecutively. Rather, the nodes themselves contain the detailed knowledg regarding prerequisites, so the calculation plan is worked out recursively. Yet there are some prerequisite resources to be made available for any calculation to happen. Thus the actual calculation is broken down into atomic chunks of work, resulting in a 2-phase invocation whenever "pulling" a node. For this to work, we need the nodes to adhere to a specific protocol:
+;planning phase
+:when a node invocation is foreseeable to be required for getting a specific frame for a specific nominal and actual time, the engine has to find out the actual operations to happen
+:# the planning is initiated by issuing an "get me output" request, finally resulting in a JobTicket
+:# recursively, the node propagates "get me output" requests for its prerequisites
+:# after retrieving the planning information for these prerequisites, the node encodes specifics of the actual invocation situation into a closure called StateAdapter <br/>{{red{TODO: why not just labeling this &raquo;~StateClosure&laquo;?}}}
+:# finally, all this information is packaged into a JobTicket representing the planning results.
+;pull phase
+:now the actual node invocation is embedded within a job, activated through the scheduler to deliver //just in time.//
+:# Node is pulled, with a StateProxy object as parameter (encapsulating BufferProvider for access to the required frames or buffers)
+:# Node may now retrieve current parameter values, using the state accessible via the StateProxy
+:# to prepare for the actual {{{process()}}} call, the node now has to retrieve the input prerequisites
+:#* when the planning phase determined availability from the cache, then just these cached buffer(s) are now retrieved, dereferencing a BuffHandle
+:#* alternatively the planning might have arranged for some other kind of input to be provided through a prerequisite Job. Again, the corresponding BuffHandle can now be dereferenced
+:#* Nodes may be planned to have a nested structure, thus directly invoking {{{pull()}}} call(s) to prerequisite nodes without further scheduling
+:# when input is ready prior to the {{{process()}}} call, output buffers will be allocated by locking the output [[buffer handles|BuffHandle]] prepared during the planning phase
+:# since all buffers and prerequistes are available, the Node may now prepare a frame pointer array and finally invoke the external {{{process()}}} to kick off the actual calculations
+:# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+{{red{WIP as of 9/11  -- many details here are still to be worked out and might change as we go}}}
+
 some points to note:
 * the WiringDescriptor is {{{const}}} and precalculated while building (remember another thread may call in parallel)
 * when a node is "inplace-capable", input and output buffer may actually point to the same location
@@ -3469,7 +3503,7 @@ There is rather strong separation between these two levels, and &mdash; <
 [img[Block Diagram|uml/fig128005.png]]
 
-
+
Render Engine, [[Builder]] and [[Controller]] are closely related Subsystems. Actually, the [[Builder]] //creates// a newly configured Render Engine //for every// RenderProcess. Before doing so, it queries from the Session (or, to be more precise, from the [[Fixture]] within the current Session) all necessary Media Object Placement information. The [[Builder]] then derives from this information the actual assembly of [[Processing Nodes|ProcNode]] comprising the Render Engine. Thus:
  * the source of the build process is a sequence of absolute (explicit) [[Placements|Placement]] called the [[Playlist]]
  * the [[build process|BuildProcess]] is driven, configured and controlled by the [[Controller]] subsystem component. It encompasses the actual playback configuration and State of the System.
@@ -3477,6 +3511,7 @@ There is rather strong separation between these two levels, and &mdash; <
 
 see also: RenderEntities, [[two Examples (Object diagrams)|Examples]] 
 
+{{red{TODO: adjust terminology in this drawing: "Playlist" &rarr; "Fixture" and "Graph" &rarr; "Segment"}}}
 [img[Overview: Components of the Renderengine|uml/fig128261.png]]
 
@@ -4961,23 +4996,26 @@ At first sight the link between asset and clip-MO is a simple logical relation b &rarr; EngineFaçade
-
+
The [[Render Engine|Rendering]] only carries out the low-level and performance critical tasks. All configuration and decision concerns are to be handled by [[Builder]] and [[Controller]]. While the actual connection of the Render Nodes can be highly complex, basically each Segment of the Timeline with uniform characteristics is handled by one Processor, which is a graph of [[Processing Nodes|ProcNode]] discharging into a ExitNode. The Render Engine Components as such are //stateless// themselves; for the actual calculations they are combined with a StateProxy object generated by and connected internally to the [[Controller]], while at the same time holding the Data Buffers (Frames) for the actual calculations.
 
+{{red{Warning: obsolete as of 9/11}}}
+Currently the Render/Playback is beeing targetted for implementation; almost everything in this diagram will be implemented in a slightly differently way....
 [img[Entities comprising the Render Engine|uml/fig128389.png]]
 
-
+
Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !Layered structure of State
 State can be seen as structured like an onion. All the [[StateAdapter]]s in one call stack are supposed to be within one layer: they all know of a "current state", which in turn is a StateProxy (and thus may refer yet to another state, maybe accros the network or in the backend or whatever). The actual {{{process()}}} function "within" the individual nodes just sees a single StateAdapter and thus can be thought to be a layer below.
 
 !Buffer identification
-For the purpose of node operation, Buffers are identified by a //Buffer-handle,// which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the respective StateAdapter provides an array containing just the output and input buffer pointers
+For the purpose of node operation, Buffers are identified by a [[buffer-handle|BuffHandle]], which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the node invocation needs to prepare and provide an array containing just the output and input buffer pointers. Typically, this //frame pointer array//&nbsp; is allocated on the call stack.
 
 !Problem of multi-channel nodes
-Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this nodes {{{process()}}} call, but we just "let go" the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
+Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this node's {{{process()}}} call, but afterwards we'r just "letting go", releasing the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
 
 Closely related to this is the problem how to number and identify nodes and thus to be able to find calculated frames in cache (&rarr; [[here|NodeFrameNumbering]])
 
@@ -4990,23 +5028,24 @@ Every node is actually decomposed into three parts
 Thus, the outer container can be changed polymorphically to support the different kinds of nodes (large-scale view). The actual wiring of the nodes is contained in the WiringDescriptor, including the {{{process()}}} function pointer. Additionally, this WiringDescriptor knows the actual type of the operation Strategy, and this actual type has been chosen by the builder such as to select details of the desired operation of this node, for example caching / no caching or maybe ~OpenGL rendering or the special case of a node pulling directly from a source reader. Most of this configuration is done by selecting the right template specialisation within the builder; thus in the critical path most of the calls can be inlined
 
 !!!! composing the actual operation Strategy
-As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous because of the large number of different execution paths to get right. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
-We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out the allocation of the "buffer table" into a hand-made stack-like buffer allocated from the heap.)
+As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous and difficult to get right because of the large number of different execution paths. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
+We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out further variable parts to be invoked through virtual functions.)
 
 !Rules for buffer allocation and freeing
 * only output buffers are allocated. It is //never necessary//&nbsp; to allocate input buffers!
 * buffers are to be allocated as late as possible, typically just before invoking {{{process()}}}
-* buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility of writing the result to cache.
-* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StateProxy.
-* any other buffers filled with results in the course of the same {{{process()}}} call can be released immediately before returning from the {{{pull()}}}
-* similar, and input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
-* buffers are allways released by calling to the "current state" (which is a StateProxy), providing the buffer-ID to be released
+* buffers are allways allocated by activating a [[buffer handle|BuffHandle]], preconfigured already during the planning phase
+* {{{pull()}}} returns a handle at least for the single output requested by this call, allowing the caller to retrieve the result data
+* any other buffers filled with results during the same {{{process()}}} call can be released immediately before returning from {{{pull()}}}
+* similar, any input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
+* while any handle contains the necessary information for releasing or "committing" this buffer, this has to be triggered explicitly.
 
 @@clear(right):display(block):@@
 
-
+
While the render process, with respect to the dependencies, the builder and the processing function is sufficiently characterized by referring to the ''pull principle'' and by defining a [[protocol|NodeOperationProtocol]] each node has to adhere to &mdash; for actually get it coded we have to care for some important details, especially //how to manage the buffers.// It may well be that the length of the code path necessary to invoke the individual processing functions is finally not so important, compared with the time spent at the inner pixel loop within these functions. But my guess is (as of 5/08), that the overall number of data moving and copying operations //will be//&nbsp; of importance.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !requirements
 * operations should be "in place" as much as possible
@@ -5568,8 +5607,8 @@ if (oldText.indexOf("SplashScreen")==-1)
 }
 //}}}
-
-
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
+
+
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack {{red{Really on the stack? 9/11}}} for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
An Object representing a //Render Process// and containing associated state information.

From 1f13931640fe09f84b798722a6edd2aa95ebe7ea Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Sep 2011 01:50:11 +0200
Subject: [PATCH 061/108] test driven brainstorming: using a BufferProvider

---
 .../engine/diagnostic-buffer-provider.hpp     |  67 ++++++++++
 tests/46engine.tests                          |   5 +
 .../engine/buffer-provider-protocol-test.cpp  | 121 ++++++++++++++++++
 wiki/renderengine.html                        |   4 +-
 4 files changed, 195 insertions(+), 2 deletions(-)
 create mode 100644 src/proc/engine/diagnostic-buffer-provider.hpp
 create mode 100644 tests/components/proc/engine/buffer-provider-protocol-test.cpp

diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
new file mode 100644
index 000000000..42011673a
--- /dev/null
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -0,0 +1,67 @@
+/*
+  DIAGNOSTIC-BUFFER-PROVIDER.hpp  -  helper for testing against the BufferProvider interface
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+/** @file diagnostic-buffer-provider.hpp
+ ** An facility for writing unit-tests targetting the BufferProvider interface.
+ **
+ ** @see buffer-provider-protocol-test.cpp
+ */
+
+#ifndef PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
+#define PROC_ENGINE_DIAGNOSTIC_BUFFR_PROVIDER_H
+
+
+#include "lib/error.hpp"
+#include "proc/engine/buffer-provider.hpp"
+
+#include 
+
+
+namespace engine {
+  
+  
+  /********************************************************************
+   * Helper for unit tests: Buffer provider reference implementation.
+   * 
+   * @todo write type comment
+   */
+  class DiagnosticBufferProvider
+    : public BufferProvider
+    {
+    public:
+      /** build a new Diagnostic Buffer Provider instance,
+       *  discard the existing one. Use the static query API
+       *  for investigating collected data. */
+      static BufferProvider&
+      build()
+        {
+          UNIMPLEMENTED ("Diagnostic Buffer Provider instance");
+        }
+      
+    private:
+      
+    };
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/tests/46engine.tests b/tests/46engine.tests
index 28f68b6a6..bc4097114 100644
--- a/tests/46engine.tests
+++ b/tests/46engine.tests
@@ -2,6 +2,11 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en
 
 
 
+PLANNED "BufferProviderProtocol_test" Buffer provider diagnostics <
+
+  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 "lib/error.hpp"
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+//#include "lib/util.hpp"
+//#include "proc/play/diagnostic-output-slot.hpp"
+#include "proc/engine/diagnostic-buffer-provider.hpp"
+#include "proc/engine/buffhandle.hpp"
+
+//#include 
+//#include 
+
+//using boost::format;
+//using std::string;
+//using std::cout;
+
+
+namespace engine{
+namespace test  {
+  
+//  using lib::AllocationCluster;
+//  using mobject::session::PEffect;
+  using ::engine::BuffHandle;
+  using lumiera::error::LUMIERA_ERROR_LIFECYCLE;
+  
+  
+  namespace { // Test fixture
+    
+  }
+  
+  
+  /*******************************************************************
+   * @test verify the OutputSlot interface and base implementation
+   *       by performing full data exchange cycle. This is a
+   *       kind of "dry run" for documentation purposes,
+   *       both the actual OutputSlot implementation
+   *       as the client using this slot are Mocks.
+   */
+  class BufferProviderProtocol_test : public Test
+    {
+      virtual void
+      run (Arg) 
+        {
+          UNIMPLEMENTED ("build a diagnostic buffer provider and perform a full lifecycle");
+          verifyStandardCase();
+        }
+      
+      
+      void
+      verifySimpleUsage()
+        {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+          // Create Test fixture.
+          // In real usage, a suitable memory/frame/buffer provider
+          // will be preconfigured, depending on the usage context
+          BufferProvider& provider = DiagnosticBufferProvider::build();
+          
+          BuffHandle buff = provider.lockBufferFor();
+          CHECK (buff.isValid());
+          CHECK (sizeof(TestFrame) <= buff.size());
+          buff.create() = testData(0);
+          
+          TestFrame* storage = *buff;
+          CHECK (testData(0) == *storage);
+          
+          buff.release();
+          CHECK (!buff.isValid());
+          VERIFY_ERROR (LIFECYCLE, *buff );
+          
+          DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
+          CHECK (checker.buffer_was_used (0));
+          CHECK (checker.buffer_was_closed (0));
+          CHECK (checker.object_was_attached ());
+          CHECK (checker.object_was_destroyed ());
+          
+          CHECK (testData(0) == checker.accessStorage (0));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+        }
+      
+      
+      void
+      verifyStandardCase()
+        {
+          // Create Test fixture.
+          // In real usage, a suitable memory/frame/buffer provider
+          // will be preconfigured, depending on the usage context
+          BufferProvider& provider = DiagnosticBufferProvider::build();
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (BufferProviderProtocol_test, "unit player");
+  
+  
+  
+}} // namespace engine::test
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 6cff8960f..0d086a79c 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1121,7 +1121,7 @@ Mostly, the //client code,// i.e. code in need of using buffers, can access some
 
 
-
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
@@ -1132,7 +1132,7 @@ While BufferProvider is an interface meant to be backed by various different kin
 ;announcing
 :client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations (or similar mechanisms to ensure the avialability) to happen right away; the BufferProvider will then return the actual number of buffers guraanteed to be available. This announcing step is optional an can happen anytime before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
 ;locking
-:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, the BufferProvider might at that point allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking might fail and raise an exception. To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, at that point the BufferProvider might allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //anounced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
 ;attaching
 :optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be automatically destroyed when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, which will be invoked in a similar manner. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
 ;releasing

From e1248d195af07febbdc1c84a317722f62a087059 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Sep 2011 17:29:16 +0200
Subject: [PATCH 062/108] move asside obsoleted code

...will be removed soon, when reworking ProcNode
---
 src/proc/engine/bufftable-obsolete.hpp        | 186 ++++++++++++++++++
 src/proc/engine/nodeinvocation.hpp            |   2 +-
 src/proc/engine/nodeoperation.hpp             |   2 +-
 src/proc/engine/render-invocation.hpp         |   2 +-
 .../proc/engine/buff-table-test.cpp           |   4 +-
 5 files changed, 191 insertions(+), 5 deletions(-)
 create mode 100644 src/proc/engine/bufftable-obsolete.hpp

diff --git a/src/proc/engine/bufftable-obsolete.hpp b/src/proc/engine/bufftable-obsolete.hpp
new file mode 100644
index 000000000..bd79683ee
--- /dev/null
+++ b/src/proc/engine/bufftable-obsolete.hpp
@@ -0,0 +1,186 @@
+/*
+  BUFFTABLE-OBSOLTE.hpp  -  Old dead code to be removed when rewriting ProcNode!!!!!
+
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+#ifndef ENGINE_BUFFHTABLE_OBSOLETE_H
+#define ENGINE_BUFFHTABLE_OBSOLETE_H
+
+
+#include "lib/error.hpp"
+#include "proc/engine/buffhandle.hpp"
+#include "proc/engine/procnode.hpp"
+
+#include 
+#include 
+#include 
+
+
+////////////////////////////////WARNING: obsolete code
+////////////////////////////////WARNING: ...just left in tree to keep it compiling
+////////////////////////////////TICKET   #826  need to be reworked entirely
+
+namespace engine {
+  
+  using std::pair;
+  using std::vector;
+  
+  
+    /**
+     * Obsolete, to be rewritten  /////TICKET #826
+     *  
+     * Tables of buffer handles and corresponding dereferenced buffer pointers.
+     * Used within the invocation of a processing node to calculate data.
+     * The tables are further differentiated into input data buffers and output
+     * data buffers. The tables are supposed to be implemented as bare "C" arrays,
+     * thus the array of real buffer pointers can be fed directly to the
+     * processing function of the respective node.
+     * 
+     * @todo this whole design is a first attempt and rather clumsy. It should be reworked
+     *       to use a single contiguous memory area and just layer the object structure on top
+     *       (by using placement new). Yet the idea of an stack-like organisation should be retained
+     */
+  struct BuffTable
+    {
+      typedef BuffHandle        * PHa;
+      typedef BuffHandle::PBuff * PBu;
+      typedef pair Chunk;
+      
+      PHa outHandle;
+      PHa inHandle;
+      PBu outBuff;
+      PBu inBuff;
+    };
+  
+  
+    /** Obsolete, to be rewritten  /////TICKET #826 */
+  class BuffTableStorage
+    {
+      /////////////////////////////////////////////////////////////////////////TICKET #826  need to be reworked entirely
+      /** just a placeholder to decouple the existing code
+       *  from the reworked BuffHandle logic. The existing
+       *  code in turn will be reworked rather fundamentally
+       */
+      struct BuffHaXXXX
+        : BuffHandle
+        {
+          BuffHaXXXX() : BuffHandle(just_satisfy_the_compiler()) { /* wont work ever */ }
+          static BufferDescriptor const& 
+          just_satisfy_the_compiler() { }
+        };
+        
+                                           ////////////////////////////////////TICKET #825  should be backed by mpool and integrated with node invocation
+      vector        hTab_;
+      vector pTab_;
+      size_t level_;
+      
+    public:
+      BuffTableStorage (const size_t maxSiz)
+        : hTab_(maxSiz),
+          pTab_(maxSiz),
+          level_(0)
+        { }
+      
+      ~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
+      
+    protected:
+      
+      friend class BuffTableChunk;
+      
+      /** allocate the given number of slots
+       *  starting at current level to be used
+       *  by the newly created BuffTableChunk
+       */
+      BuffTable::Chunk
+      claim (uint slots)
+        {
+          ASSERT (pTab_.size() == hTab_.size());
+          REQUIRE (level_+slots <= hTab_.size());
+          
+          size_t prev_level (level_);
+          level_ += slots;
+          return std::make_pair (&hTab_[prev_level],
+                                 &pTab_[prev_level]);
+        }
+      
+      void
+      release (uint slots)
+        {
+          ASSERT (slots <= level_);
+          REQUIRE (level_ <= hTab_.size());
+          REQUIRE (level_ <= pTab_.size());
+          
+          level_ -= slots;
+        }
+      
+      bool
+      level_check (BuffTable::Chunk& prev_level)
+        {
+          return prev_level.first  == &hTab_[level_]
+              && prev_level.second == &pTab_[level_];
+        }
+    };
+  
+  
+  /** Obsolete, to be rewritten  /////TICKET #826 
+   * to be allocated on the stack while evaluating a ProcNode#pull() call.
+   * The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
+   * which can be used to crate such chunks. The claiming and releasing of
+   * slots in the BuffTableStorage is automatically tied to BuffTableChunk
+   * object's lifecycle.
+   */
+  class BuffTableChunk
+    : public BuffTable,
+      boost::noncopyable
+    {
+      const uint siz_;
+      BuffTable::Chunk tab_;
+      BuffTableStorage& sto_;
+      
+    public:
+      BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
+        : siz_(wd.nrI + wd.nrO),
+          tab_(storage.claim (siz_)),
+          sto_(storage)
+        {
+          const uint nrO(wd.nrO);
+          
+          // Setup the publicly visible table locations
+          this->outHandle = &tab_.first[ 0 ];
+          this->inHandle  = &tab_.first[nrO];
+          this->outBuff   = &tab_.second[ 0 ];
+          this->inBuff    = &tab_.second[nrO];
+        }
+      
+      ~BuffTableChunk ()
+        {
+          sto_.release (siz_);
+          ASSERT ( sto_.level_check (tab_),
+                  "buffer management logic broken.");
+        }
+    };
+  
+  
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
index 695406cda..a67696850 100644
--- a/src/proc/engine/nodeinvocation.hpp
+++ b/src/proc/engine/nodeinvocation.hpp
@@ -56,7 +56,7 @@
 #include "proc/state.hpp"
 #include "proc/engine/procnode.hpp"
 #include "proc/engine/buffhandle.hpp"
-#include "proc/engine/bufftable.hpp"
+#include "proc/engine/bufftable-obsolete.hpp"
 
 
 
diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
index 63753cee6..947fa645f 100644
--- a/src/proc/engine/nodeoperation.hpp
+++ b/src/proc/engine/nodeoperation.hpp
@@ -60,7 +60,7 @@
 #include "proc/state.hpp"
 #include "proc/engine/procnode.hpp"
 #include "proc/engine/buffhandle.hpp"
-#include "proc/engine/bufftable.hpp"
+#include "proc/engine/bufftable-obsolete.hpp"
 #include "proc/engine/nodeinvocation.hpp"
 
 #include "lib/meta/util.hpp"
diff --git a/src/proc/engine/render-invocation.hpp b/src/proc/engine/render-invocation.hpp
index 459eb3f93..481d5e691 100644
--- a/src/proc/engine/render-invocation.hpp
+++ b/src/proc/engine/render-invocation.hpp
@@ -37,7 +37,7 @@
 //#include "proc/state.hpp"
 #include "proc/engine/procnode.hpp"
 #include "proc/engine/buffhandle.hpp"
-//#include "proc/engine/bufftable.hpp"
+//#include "proc/engine/bufftable-obsolete.hpp"
 
 
 
diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp
index 267978d49..5d7bd27d3 100644
--- a/tests/components/proc/engine/buff-table-test.cpp
+++ b/tests/components/proc/engine/buff-table-test.cpp
@@ -25,7 +25,7 @@
 #include "lib/error.hpp"
 
 #include "proc/engine/procnode.hpp"
-#include "proc/engine/bufftable.hpp"
+#include "proc/engine/bufftable-obsolete.hpp"
 #include "lib/ref-array.hpp"
 
 #include 
@@ -137,7 +137,7 @@ namespace test  {
       virtual void run(Arg) 
         {
            counter = 0;
-           std::srand (time (NULL));
+           std::srand (std::time (NULL));
            
            // allocate storage block to be used chunk wise
            pStorage.reset (new BuffTableStorage (TABLE_SIZ));

From 32f71cba6d1cc268e0b4754e6f5ced1044aa9009 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 17 Sep 2011 18:25:45 +0200
Subject: [PATCH 063/108] more test-driven brainstorming

---
 src/proc/engine/buffer-provider.cpp           |  6 +--
 src/proc/engine/buffer-provider.hpp           |  4 +-
 src/proc/engine/buffhandle.hpp                |  4 +-
 .../engine/buffer-provider-protocol-test.cpp  | 38 ++++++++++++++++++-
 4 files changed, 44 insertions(+), 8 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index 57660a572..c9a2311e8 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -40,7 +40,7 @@ namespace engine {
    *          currently locked and usable by client code
    */
   bool
-  BufferProvider::checkValidity (BufferDescriptor const&)
+  BufferProvider::verifyValidity (BufferDescriptor const&)
   {
     UNIMPLEMENTED ("BufferProvider basic and default implementation");
   }
@@ -58,9 +58,9 @@ namespace engine {
   /* === BufferDescriptor and BuffHandle === */
   
   bool
-  BufferDescriptor::checkValidity()  const
+  BufferDescriptor::verifyValidity()  const
   {
-    return provider_->checkValidity(*this);
+    return provider_->verifyValidity(*this);
   }
   
 
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index 7f8f4f99b..02f6ff262 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -79,12 +79,12 @@ namespace engine {
       
       
       /** describe the kind of buffer managed by this provider */
-      BufferDescriptor getDefaultDescriptor();
+      BufferDescriptor getDefaultDescriptor();                //////////////TODO really? there is no sensible "default"
       
       
       /* === API for BuffHandle internal access === */
       
-      bool checkValidity (BufferDescriptor const&);
+      bool verifyValidity (BufferDescriptor const&);
       
     };
   
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 835d0329c..f2e9646fb 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -79,7 +79,7 @@ namespace engine {
     public:
       // using standard copy operations
       
-      bool checkValidity()  const;
+      bool verifyValidity()  const;
     };
   
   
@@ -143,7 +143,7 @@ namespace engine {
       isValid()  const
         {
           return bool(pBuffer_)
-              && descriptor_.checkValidity();
+              && descriptor_.verifyValidity();
         }
       
     };
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index c3adb7f9e..19866ebfc 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -24,10 +24,11 @@
 #include "lib/error.hpp"
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
-//#include "lib/util.hpp"
+#include "lib/util-foreach.hpp"
 //#include "proc/play/diagnostic-output-slot.hpp"
 #include "proc/engine/diagnostic-buffer-provider.hpp"
 #include "proc/engine/buffhandle.hpp"
+#include "proc/engine/bufftable.hpp"
 
 //#include 
 //#include 
@@ -35,6 +36,7 @@
 //using boost::format;
 //using std::string;
 //using std::cout;
+using util::for_each;
 
 
 namespace engine{
@@ -48,6 +50,9 @@ namespace test  {
   
   namespace { // Test fixture
     
+    const uint TEST_SIZE = 1024*1024;
+    const uint TEST_ELMS = 20;
+    
   }
   
   
@@ -64,6 +69,7 @@ namespace test  {
       run (Arg) 
         {
           UNIMPLEMENTED ("build a diagnostic buffer provider and perform a full lifecycle");
+          verifySimpleUsage();
           verifyStandardCase();
         }
       
@@ -108,6 +114,36 @@ namespace test  {
           // will be preconfigured, depending on the usage context
           BufferProvider& provider = DiagnosticBufferProvider::build();
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
+          
+          BufferDescriptor desc1 = provider.getDescriptor();
+          BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE);
+          CHECK (desc1.verifyValidity());
+          CHECK (desc2.verifyValidity());
+          
+          size_t num1 = provider.announce(TEST_ELMS, desc1);
+          size_t num2 = provider.announce(TEST_ELMS, desc2);
+          CHECK (num1 == TEST_ELMS);
+          CHECK (0 < num2 && num2 <=TEST_ELMS);
+          
+          const size_t STORAGE_SIZE = BuffTable::Storage<2*TEST_ELMS>::size; 
+          char storage[STORAGE_SIZE];
+          BuffTable& tab = BuffTable::prepare(storage, STORAGE_SIZE);
+          
+          for (uint i=0; i < num1; ++i )
+            {
+              tab.attachBuffer (provider.lockBufferFor (desc1));
+            }
+          for (uint i=0; i < num1; ++i )
+            {
+              tab.attachBuffer (provider.lockBufferFor (desc2));
+            }
+          
+          for_each (tab.buffers(), do_some_calculations);
+          
+          tab.releaseBuffers();
+          
+          DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
+          CHECK (checker.all_buffers_released());
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
         }
     };

From ed5091d8f5a5c7572ec43fca87c6a750dc5b975c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 18 Sep 2011 01:02:31 +0200
Subject: [PATCH 064/108] considering Buffer handling and BuffTable in more
 detail

---
 .../engine/buffer-provider-protocol-test.cpp  | 17 ++++++---------
 wiki/renderengine.html                        | 21 +++++++++++++++++--
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index 19866ebfc..a50ceb2a5 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -127,19 +127,14 @@ namespace test  {
           
           const size_t STORAGE_SIZE = BuffTable::Storage<2*TEST_ELMS>::size; 
           char storage[STORAGE_SIZE];
-          BuffTable& tab = BuffTable::prepare(storage, STORAGE_SIZE);
-          
-          for (uint i=0; i < num1; ++i )
-            {
-              tab.attachBuffer (provider.lockBufferFor (desc1));
-            }
-          for (uint i=0; i < num1; ++i )
-            {
-              tab.attachBuffer (provider.lockBufferFor (desc2));
-            }
+          BuffTable& tab =
+              BuffTable::prepare(STORAGE_SIZE, storage)
+                        .prepare(num1, desc1)
+                        .prepare(num2, desc2)
+                        .build(); 
           
+          tab.lockBuffers();
           for_each (tab.buffers(), do_some_calculations);
-          
           tab.releaseBuffers();
           
           DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 0d086a79c..5b6ca80dc 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1144,6 +1144,20 @@ __see also__
 &rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
 
+
+
The invocation of individual [[render nodes|ProcNode]] uses an ''buffer table'' internal helper data structure to encapsulate technical details of the allocation, use, re-use and feeing of data buffers for the media calculations. Here, the management of the physical data buffers is delegated through a BufferProvider, which typically is implemented relying on the ''frame cache'' in the backend. Yet some partially quite involved technical details need to be settled for each invocation: We need input buffers, maybe provided as external input, while in other cases to be filled by a recursive call. We need storage to prepare the (possibly automated) parameters, and finally we need a set of output buffers. All of these buffers and parameters need to be rearranged for invoking the (external) processing function, followed by releasing the input buffers and commiting the output buffers to be used as result.
+
+Because there are several flavours of node wiring, the building blocks comprising such a node invocation will be combined depending on the circumstances. Performing all these various steps is indeed the core concern of the render node -- with the help of BufferTable to deal with the repetitive, tedious and technical details.
+
+!requirements
+The layout of the buffer table will be planned beforehand for each invocation, allongside with planning the individual invocation jobs for the scheduler. At that point, a generic JobTicket for the whole timeline segment is available, describing the necessary operations in an abstract way, as determined by the preceeding planning phase. Jobs are prepared chunk wise, some time in advance (but not all jobs of at once). Jobs will be executed concurrently. Thus, buffer tables need to be created repeatedly and placed into a memory block accessed and owned exclusively by the individual job.
+* within the buffer table, we need an working area for the output handles, the input handles and the parameter descriptors
+* actually, these can be seen as pools holding handle objects which might even be re-used, especially for a chain of effects calculated in-place.
+* each of these pools is characterised by a common //buffer type,// represented as buffer descriptor
+* we need some way to integrate with the StateProxy, because some of the buffers need to be marked especially, e.g. as result
+* there should be convenience functions to release all pending buffers, forwarding the release operation to the individual handles
+
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
 {{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
@@ -3166,7 +3180,7 @@ While the general approach and reasoning remains valid, a lot of the details loo
 In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
 
-
+
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. Meaning that there isn't an central entity invoking nodes consecutively. Rather, the nodes themselves contain the detailed knowledg regarding prerequisites, so the calculation plan is worked out recursively. Yet there are some prerequisite resources to be made available for any calculation to happen. Thus the actual calculation is broken down into atomic chunks of work, resulting in a 2-phase invocation whenever "pulling" a node. For this to work, we need the nodes to adhere to a specific protocol:
 ;planning phase
 :when a node invocation is foreseeable to be required for getting a specific frame for a specific nominal and actual time, the engine has to find out the actual operations to happen
@@ -3192,6 +3206,7 @@ some points to note:
 * when a node is "inplace-capable", input and output buffer may actually point to the same location
 * but there is no guarantee for this to happen, because the cache may be involved (and we can't overwrite the contents of a cache frame)
 * generally, a node may have N inputs and M output frames, which are expected to be processed in a single call
+* some of the technical details of buffer management are encapsulated within the BufferTable of each invocation
 
 &rarr; the [["mechanics" of the render process|RenderMechanics]]
 &rarr; more fine grained [[implementation details|RenderImplDetails]]
@@ -5043,7 +5058,7 @@ We //do employ//&nbsp; some virtual calls for the buffer management in order
 @@clear(right):display(block):@@
 
-
+
While the render process, with respect to the dependencies, the builder and the processing function is sufficiently characterized by referring to the ''pull principle'' and by defining a [[protocol|NodeOperationProtocol]] each node has to adhere to &mdash; for actually get it coded we have to care for some important details, especially //how to manage the buffers.// It may well be that the length of the code path necessary to invoke the individual processing functions is finally not so important, compared with the time spent at the inner pixel loop within these functions. But my guess is (as of 5/08), that the overall number of data moving and copying operations //will be//&nbsp; of importance.
 {{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
@@ -5059,6 +5074,8 @@ On the other hand, the processing function within the individual node needs to b
 Not everything can be preconfigured though. The pull principle opens the possibility for the node to decide on a per call base what predecessor(s) to pull (if any). This decision may rely on automation parameters, which thus need to be accessible prior to requesting the buffer(s). Additionally, in a later version we plan to have the node network calculate some control values for adjusting the cache and backend timings &mdash; and of course at some point we'll want to utilize the GPU, resulting in the need to feed data from our processing buffers into some texture representation.
 
 !buffer management
+{{red{NOTE 9/11: the following is partially obsolete and needs to be rewritten}}} &rarr; see the BufferTable for details regarding new buffer management...
+
 Besides the StateProxy representing the actual render process and holding a couple of buffer (refs), we employ a lightweight adapter object in between. It is used //for a single {{{pull()}}}-call// &mdash; mapping the actual buffers to the input and output port numbers of the processing node and for dealing with the cache calls. While the StateProxy manages a pool of frame buffers, this interspersed adapter allows us to either use a buffer retrieved from the cache as an input, possibly use a new buffer located within the cache as output, or (in case no caching happens) to just use the same buffer as input and output for "in-place"-processing. The idea is that most of the configuration of this adapter object is prepared in the wiring step while building the node network.
 
 The usage patern of the buffers can be stack-like when processing nodes require multiple input buffers. In the standard case, which also is the simplest case, a pair of buffers (or a single buffer for "in-place" capable nodes) suffices to calculate a whole chain of nodes. But &mdash; as the recursive descent means depth-first processing &mdash; in case multiple input buffers are needed, we may encounter a situation where some of these input buffers already contain processed data, while we have to descend into yet another predecessor node chain to pull the data for the remaining buffers. Care has to be taken //to allocate the buffers as late as possible,// otherwise we could end up holding onto a buffer almost for each node in the network. Effectively this translates into the rule to allocate output buffers only after all input buffers are ready and filled with data; thus we shouldn't allocate buffers when //entering// the recursive call to the predecessor(s), rather we have to wait until we are about to return from the downcall chain.

From c473784fe3167c9addc6e7a7f2cb6a61ef56c65c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 18 Sep 2011 15:05:18 +0200
Subject: [PATCH 065/108] disable obsolte parts of BufferTable_test

to be rewritten
---
 tests/components/proc/engine/buff-table-test.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/components/proc/engine/buff-table-test.cpp b/tests/components/proc/engine/buff-table-test.cpp
index 5d7bd27d3..06ff34e54 100644
--- a/tests/components/proc/engine/buff-table-test.cpp
+++ b/tests/components/proc/engine/buff-table-test.cpp
@@ -81,6 +81,7 @@ namespace test  {
     
     
     
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
     void*
     detect_start_level (BuffTableStorage& sto)
     {
@@ -116,6 +117,7 @@ namespace test  {
           && (not_within(b.inHandle,  b.outBuff, &b.inBuff[num.nrO]))
            ;
     }
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
   } // (End) internal defs
   
   
@@ -142,7 +144,9 @@ namespace test  {
            // allocate storage block to be used chunk wise
            pStorage.reset (new BuffTableStorage (TABLE_SIZ));
            
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
            invocation (0, detect_start_level(*pStorage));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
            
            pStorage.reset(0);  // dtor throws assertion error if corrupted
            
@@ -161,12 +165,14 @@ namespace test  {
             return; // end recursion
           
           ++counter;
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
           BuffTableChunk thisChunk (numbers, *pStorage);
           CHECK (consistencyCheck (thisChunk, numbers, lastLevel));
           
           uint nrBranches ( 1 + (rand() % WIDTH_MAX));
           while (nrBranches--)
             invocation (consumed, first_behind (thisChunk,numbers.getNrI()));
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #833
         }
     };
   

From ca615b9933234ad71ddd4a684ebfa284388e47bf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 18 Sep 2011 15:28:58 +0200
Subject: [PATCH 066/108] start stubbing some of the functionality planned thus
 far

---
 src/proc/engine/buffer-provider.hpp           | 21 +++++
 src/proc/engine/buffhandle.hpp                | 25 ++++++
 .../engine/buffer-provider-protocol-test.cpp  |  3 +-
 tests/components/proc/engine/testframe.hpp    | 81 +++++++++++++++++++
 4 files changed, 129 insertions(+), 1 deletion(-)
 create mode 100644 tests/components/proc/engine/testframe.hpp

diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index 02f6ff262..bbb7766ef 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -77,6 +77,9 @@ namespace engine {
       virtual BuffHandle lockBufferFor (BufferDescriptor const&)  =0;
       virtual void releaseBuffer (BuffHandle const&)              =0;
       
+      template
+      BuffHandle lockBufferFor ();
+      
       
       /** describe the kind of buffer managed by this provider */
       BufferDescriptor getDefaultDescriptor();                //////////////TODO really? there is no sensible "default"
@@ -87,7 +90,25 @@ namespace engine {
       bool verifyValidity (BufferDescriptor const&);
       
     };
+    
+    
+    
+    
+  /* === Implementation === */
   
+  /** convenience shortcut:
+   *  prepare and claim ("lock") a buffer suitable
+   *  to hold an object of the given type.
+   * @return a handle embedding a suitably configured
+   *         buffer descriptor. The corresponding buffer
+   *         has been allocated and marked for exclusive use
+   */
+  template
+  BuffHandle
+  BufferProvider::lockBufferFor()
+  {
+    UNIMPLEMENTED ("convenience shortcut to announce and lock for a specific object type");
+  }
   
   
 } // namespace engine
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index f2e9646fb..889638724 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -132,6 +132,10 @@ namespace engine {
       // using standard copy operations
       
       
+      template
+      BU& create();
+      
+      
       Buff&
       operator* ()  const
         {
@@ -146,9 +150,30 @@ namespace engine {
               && descriptor_.verifyValidity();
         }
       
+      size_t
+      size()  const
+        {
+          UNIMPLEMENTED ("forward to the buffer provider for storage size diagnostics");
+        }
+      
     };
   
   
+  /* === Implementation details === */
+  
+  /** convenience shortcut: place and maintain an object within the buffer.
+   *  This operation performs the necessary steps to attach an object;
+   *  if the buffer isn't locked yet, it will do so. Moreover, the created
+   *  object will be owned by the buffer management facilities, i.e. the
+   *  destructor is registered as cleanup function.   
+   */
+  template
+  BU&
+  BuffHandle::create()
+  {
+    UNIMPLEMENTED ("convenience shortcut to attach/place an object in one sway");
+  }
+  
   
   
 } // namespace engine
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index a50ceb2a5..38f65568d 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -26,6 +26,7 @@
 #include "lib/test/test-helper.hpp"
 #include "lib/util-foreach.hpp"
 //#include "proc/play/diagnostic-output-slot.hpp"
+#include "proc/engine/testframe.hpp"
 #include "proc/engine/diagnostic-buffer-provider.hpp"
 #include "proc/engine/buffhandle.hpp"
 #include "proc/engine/bufftable.hpp"
@@ -77,7 +78,6 @@ namespace test  {
       void
       verifySimpleUsage()
         {
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           // Create Test fixture.
           // In real usage, a suitable memory/frame/buffer provider
           // will be preconfigured, depending on the usage context
@@ -88,6 +88,7 @@ namespace test  {
           CHECK (sizeof(TestFrame) <= buff.size());
           buff.create() = testData(0);
           
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           TestFrame* storage = *buff;
           CHECK (testData(0) == *storage);
           
diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp
new file mode 100644
index 000000000..0ea6a8d75
--- /dev/null
+++ b/tests/components/proc/engine/testframe.hpp
@@ -0,0 +1,81 @@
+/*
+  TESTFRAME.hpp  -  test data frame (stub) for checking Render engine functionality
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+#ifndef PROC_ENGINE_TESTFRAME_H
+#define PROC_ENGINE_TESTFRAME_H
+
+
+//#include "lib/time/timevalue.hpp"
+
+//#include 
+
+
+//using std::tr1::shared_ptr;
+//using std::string;
+
+
+namespace engine {
+namespace test   {
+  
+  
+//class TestPlacement;
+  
+  /**
+   * Mock data frame for simulated rendering.
+   * A test frame can be created and placed instead of a real data frame.
+   * It doesn't depend on any external libraries and will be self-maintaining.
+   * Placeholder functions are provided for assignment (simulating the actual
+   * calculations); additional diagnostic functions allow to verify the
+   * performed operations after-the fact
+   *  
+   * @todo WIP-WIP-WIP 9/11
+   * 
+   */
+  class TestFrame
+    {
+      
+    public:
+    };
+  
+  
+  
+  TestFrame
+  testData (uint seqNr)
+  {
+    UNIMPLEMENTED ("build, memorise and expose test data frames on demand");
+  }
+  
+  TestFrame
+  testData (uint chanNr, uint seqNr)
+  {
+    UNIMPLEMENTED ("build, memorise and expose test data frames on demand (multi-channel)");
+  }
+  
+  
+  
+  /* == some test data to check == */
+  // extern const lib::time::Duration LENGTH_TestClip;
+  
+  
+}} // namespace engine::test
+#endif

From cafe271830c136d613de19e91f6ce8a804c0e4f5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 19 Sep 2011 01:42:37 +0200
Subject: [PATCH 067/108] stubbing of basic buffer provider functionality

---
 src/proc/engine/buffer-provider.cpp           |  7 ++
 src/proc/engine/buffhandle.hpp                | 19 +++++
 .../engine/diagnostic-buffer-provider.hpp     | 71 +++++++++++++++++++
 .../engine/buffer-provider-protocol-test.cpp  | 14 ++--
 tests/components/proc/engine/testframe.hpp    | 18 +++++
 5 files changed, 121 insertions(+), 8 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index c9a2311e8..0ca617577 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -63,6 +63,13 @@ namespace engine {
     return provider_->verifyValidity(*this);
   }
   
+  
+  void
+  BuffHandle::release()
+  {
+    UNIMPLEMENTED ("forward buffer release call to buffer provider");
+  }
+  
 
 
 } // namespace engine
diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
index 889638724..8401bdb17 100644
--- a/src/proc/engine/buffhandle.hpp
+++ b/src/proc/engine/buffhandle.hpp
@@ -132,9 +132,16 @@ namespace engine {
       // using standard copy operations
       
       
+      
+      void release();
+      
+      
       template
       BU& create();
       
+      template
+      BU& accessAs();
+      
       
       Buff&
       operator* ()  const
@@ -175,6 +182,18 @@ namespace engine {
   }
   
   
+  /** convenience shortcuts: access the buffer contents in a typesafe fashion.
+   *  This is equivalent to a plain dereferentiation with additional metadata check
+   * @throw error::Logic in case of type mismatch \c LUMIERA_ERROR_WRONG_TYPE
+   */
+  template
+  BU&
+  BuffHandle::accessAs()
+  {
+    UNIMPLEMENTED ("convenience shortcut to access buffer contents typesafe");
+  }
+  
+  
   
 } // namespace engine
 #endif
diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
index 42011673a..c8836b617 100644
--- a/src/proc/engine/diagnostic-buffer-provider.hpp
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -47,6 +47,23 @@ namespace engine {
   class DiagnosticBufferProvider
     : public BufferProvider
     {
+      
+      
+      /* === BufferProvider Implementation === */
+      
+      virtual BuffHandle
+      lockBufferFor (BufferDescriptor const& descriptor)
+        {
+          UNIMPLEMENTED ("lock buffer for exclusive use");
+        }
+      
+      virtual void
+      releaseBuffer (BuffHandle const& handle)
+        {
+          UNIMPLEMENTED ("release a buffer and invalidate the handle");
+        }
+      
+      
     public:
       /** build a new Diagnostic Buffer Provider instance,
        *  discard the existing one. Use the static query API
@@ -57,6 +74,60 @@ namespace engine {
           UNIMPLEMENTED ("Diagnostic Buffer Provider instance");
         }
       
+      
+      /** access the diagnostic API of the buffer provider
+       * @throw error::Invalid if the given provider doesn't allow
+       *        for diagnostic access or wasn't registered beforehand.
+       */
+      static DiagnosticBufferProvider&
+      access (BufferProvider const& provider)
+        {
+          UNIMPLEMENTED ("access existing instance linked to the given provider");
+        }
+      
+      
+      
+      
+      /* === diagnostic API === */
+      
+      bool
+      buffer_was_used (uint bufferID)
+        {
+          UNIMPLEMENTED ("check usage flag of a specific buffer");
+        }
+      
+      
+      bool
+      buffer_was_closed (uint bufferID)
+        {
+          UNIMPLEMENTED ("check closed-flag of a specific buffer");
+        }
+      
+      
+      template
+      bool
+      object_was_attached (uint bufferID)
+        {
+          UNIMPLEMENTED ("verify object attachment status of a specific buffer");
+        }
+      
+      
+      template
+      bool
+      object_was_destroyed (uint bufferID)
+        {
+          UNIMPLEMENTED ("verify object attachment status of a specific buffer");
+        }
+      
+      
+      void*
+      accessStorage (uint bufferID)
+        {
+          
+        }
+      
+      
+      
     private:
       
     };
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index 38f65568d..7846dec5a 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -88,22 +88,20 @@ namespace test  {
           CHECK (sizeof(TestFrame) <= buff.size());
           buff.create() = testData(0);
           
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
-          TestFrame* storage = *buff;
-          CHECK (testData(0) == *storage);
+          TestFrame& storage = buff.accessAs();
+          CHECK (testData(0) == storage);
           
           buff.release();
           CHECK (!buff.isValid());
-          VERIFY_ERROR (LIFECYCLE, *buff );
+          VERIFY_ERROR (LIFECYCLE, buff.accessAs() );
           
-          DiagnosticBufferProvider checker = DiagnosticBufferProvider::access(provider);
+          DiagnosticBufferProvider& checker = DiagnosticBufferProvider::access(provider);
           CHECK (checker.buffer_was_used (0));
           CHECK (checker.buffer_was_closed (0));
-          CHECK (checker.object_was_attached ());
-          CHECK (checker.object_was_destroyed ());
+          CHECK (checker.object_was_attached (0));
+          CHECK (checker.object_was_destroyed (0));
           
           CHECK (testData(0) == checker.accessStorage (0));
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
         }
       
       
diff --git a/tests/components/proc/engine/testframe.hpp b/tests/components/proc/engine/testframe.hpp
index 0ea6a8d75..5678bf147 100644
--- a/tests/components/proc/engine/testframe.hpp
+++ b/tests/components/proc/engine/testframe.hpp
@@ -55,6 +55,24 @@ namespace test   {
     {
       
     public:
+      
+      bool
+      operator== (void* memLocation)
+        {
+          UNIMPLEMENTED ("verify contents of an arbitrary memory location");
+        }
+      
+      friend bool
+      operator== (TestFrame const& f1, TestFrame const& f2)
+        {
+          UNIMPLEMENTED ("equality of test data frames");
+        }
+      
+      friend bool
+      operator!= (TestFrame const& f1, TestFrame const& f2)
+        {
+          return !(f1 == f2);
+        }
     };
   
   

From 5b6ecbab1c4b87d22f187676238c83a7de17a606 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 20 Sep 2011 00:54:01 +0200
Subject: [PATCH 068/108] start diagnostic buffer provider implementation

---
 .../engine/diagnostic-buffer-provider.cpp     | 202 ++++++++++++++++++
 .../engine/diagnostic-buffer-provider.hpp     |  65 ++----
 2 files changed, 225 insertions(+), 42 deletions(-)
 create mode 100644 src/proc/engine/diagnostic-buffer-provider.cpp

diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp
new file mode 100644
index 000000000..69df7c35a
--- /dev/null
+++ b/src/proc/engine/diagnostic-buffer-provider.cpp
@@ -0,0 +1,202 @@
+/*
+  DiagnosticBufferProvider  -  helper for testing against the BufferProvider interface
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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 "lib/error.hpp"
+#include "include/logging.h"
+#include "lib/meta/function.hpp"
+#include "lib/scoped-ptrvect.hpp"
+
+#include "proc/engine/diagnostic-buffer-provider.hpp"
+
+#include 
+//#include 
+
+using lib::ScopedPtrVect;
+using boost::scoped_array;
+
+
+
+namespace engine {
+  
+  
+  /** Storage for the diagnostics frontend */
+  lib::Singleton DiagnosticBufferProvider::diagnostics;
+  
+  
+  class Block
+    : boost::noncopyable
+    {
+      size_t size_;
+      scoped_array storage_;
+      
+      bool was_locked_;
+      
+    public:
+      Block()
+        : size_(0)
+        , storage_()
+        , was_locked_(false)
+        { }
+      
+      bool
+      was_used()  const
+        {
+          return was_locked_;
+        }
+    };
+  
+    
+    
+  namespace { // Details of allocation and accounting
+
+    using lumiera::typelist::FunctionSignature;
+    using std::tr1::placeholders::_1;
+    using std::tr1::function;
+    using std::tr1::bind;
+    
+    /**
+     * type rebinding template for issuing flexible queries
+     * and forwarding them to the individual block entries
+     */
+    template
+    struct _Query
+      {
+        typedef typename FunctionSignature::Ret Result;
+      };
+    
+    function QUERY_was_used = bind (&Block::was_used, _1 );
+  
+  } // (END) Details of allocation and accounting
+  
+  
+  
+  /**
+   * @internal DiagnosticBufferProvider's PImpl.
+   * Uses a linearly growing table of heap allocated buffer blocks,
+   * which will never be discarded, unless the PImpl is discarded as a whole.
+   * This way, the tracked usage information remains available after the fact.
+   */
+  class DiagnosticBufferProvider::HeapMemProvider
+    : public BufferProvider
+    , public ScopedPtrVect
+    {
+      virtual BuffHandle
+      lockBufferFor (BufferDescriptor const& descriptor)
+        {
+          UNIMPLEMENTED ("lock buffer for exclusive use");
+        }
+      
+      virtual void
+      releaseBuffer (BuffHandle const& handle)
+        {
+          UNIMPLEMENTED ("release a buffer and invalidate the handle");
+        }
+      
+    public:
+      HeapMemProvider()
+        {
+        }
+      
+      virtual ~HeapMemProvider()
+        {
+          INFO (proc_mem, "discarding %uz diagnostic buffer entries", HeapMemProvider::size());
+        }
+      
+      template
+      typename _Query::Result
+      accessStats (uint bufferID, FUN queryFunction)
+        {
+          queryFunction (access_or_create (bufferID));
+        }
+      
+    private:
+      Block&
+      access_or_create (uint bufferID)
+        {
+          UNIMPLEMENTED ("either get existing block or create new entries to fit");
+        }
+    };
+  
+  
+  
+  BufferProvider&
+  DiagnosticBufferProvider::build()
+  {
+    return diagnostics().reset();
+  }
+  
+  
+  DiagnosticBufferProvider&
+  DiagnosticBufferProvider::access (BufferProvider const& provider)
+  {
+    if (!diagnostics().isCurrent (provider))
+      throw error::Invalid("given Provider doesn't match (current) diagnostic data record."
+                           "This might be an lifecycle error. Did you build() this instance beforehand?");
+    
+    return diagnostics();
+  }
+  
+
+    
+    
+  DiagnosticBufferProvider::HeapMemProvider&
+  DiagnosticBufferProvider::reset()
+  {
+    pImpl_.reset(new HeapMemProvider());
+    return *pImpl_;
+  }
+  
+  bool
+  DiagnosticBufferProvider::isCurrent (BufferProvider const& implInstance)
+  {
+    return &implInstance == pImpl_.get();
+  }
+
+  
+
+  
+  
+  /* === diagnostic API === */
+  
+  bool
+  DiagnosticBufferProvider::buffer_was_used (uint bufferID)  const
+    {
+      pImpl_->accessStats (bufferID, QUERY_was_used);
+    }
+  
+  
+  bool
+  DiagnosticBufferProvider::buffer_was_closed (uint bufferID)  const
+    {
+      UNIMPLEMENTED ("check closed-flag of a specific buffer");
+    }
+  
+  
+  void*
+  DiagnosticBufferProvider::accessStorage (uint bufferID)  const
+    {
+      
+    }
+  
+
+} // namespace engine
diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
index c8836b617..399f7d782 100644
--- a/src/proc/engine/diagnostic-buffer-provider.hpp
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -31,13 +31,18 @@
 
 
 #include "lib/error.hpp"
+#include "lib/singleton.hpp"
+#include "lib/util.hpp"
 #include "proc/engine/buffer-provider.hpp"
 
+#include 
 #include 
 
 
 namespace engine {
   
+  namespace error = lumiera::error;
+  
   
   /********************************************************************
    * Helper for unit tests: Buffer provider reference implementation.
@@ -45,34 +50,29 @@ namespace engine {
    * @todo write type comment
    */
   class DiagnosticBufferProvider
-    : public BufferProvider
+    : boost::noncopyable
     {
       
+      /**
+       * simple BufferProvider implementation
+       * with additional allocation tracking
+       */
+      class HeapMemProvider;
       
-      /* === BufferProvider Implementation === */
       
-      virtual BuffHandle
-      lockBufferFor (BufferDescriptor const& descriptor)
-        {
-          UNIMPLEMENTED ("lock buffer for exclusive use");
-        }
+      boost::scoped_ptr              pImpl_;
+      static lib::Singleton diagnostics;
       
-      virtual void
-      releaseBuffer (BuffHandle const& handle)
-        {
-          UNIMPLEMENTED ("release a buffer and invalidate the handle");
-        }
+      
+      HeapMemProvider& reset();
+      bool isCurrent (BufferProvider const&);
       
       
     public:
       /** build a new Diagnostic Buffer Provider instance,
        *  discard the existing one. Use the static query API
        *  for investigating collected data. */
-      static BufferProvider&
-      build()
-        {
-          UNIMPLEMENTED ("Diagnostic Buffer Provider instance");
-        }
+      static BufferProvider& build();
       
       
       /** access the diagnostic API of the buffer provider
@@ -80,33 +80,21 @@ namespace engine {
        *        for diagnostic access or wasn't registered beforehand.
        */
       static DiagnosticBufferProvider&
-      access (BufferProvider const& provider)
-        {
-          UNIMPLEMENTED ("access existing instance linked to the given provider");
-        }
+      access (BufferProvider const&);
       
       
       
       
       /* === diagnostic API === */
       
-      bool
-      buffer_was_used (uint bufferID)
-        {
-          UNIMPLEMENTED ("check usage flag of a specific buffer");
-        }
-      
-      
-      bool
-      buffer_was_closed (uint bufferID)
-        {
-          UNIMPLEMENTED ("check closed-flag of a specific buffer");
-        }
+      bool buffer_was_used (uint bufferID)  const;
+      bool buffer_was_closed (uint bufferID)  const;
+      void* accessStorage (uint bufferID)  const;
       
       
       template
       bool
-      object_was_attached (uint bufferID)
+      object_was_attached (uint bufferID)  const
         {
           UNIMPLEMENTED ("verify object attachment status of a specific buffer");
         }
@@ -114,19 +102,12 @@ namespace engine {
       
       template
       bool
-      object_was_destroyed (uint bufferID)
+      object_was_destroyed (uint bufferID)  const
         {
           UNIMPLEMENTED ("verify object attachment status of a specific buffer");
         }
       
       
-      void*
-      accessStorage (uint bufferID)
-        {
-          
-        }
-      
-      
       
     private:
       

From 7ef85065ba8d61f8c08e7e52135a1f864cb02cd2 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 21 Sep 2011 02:23:12 +0200
Subject: [PATCH 069/108] impl: buffer provider metadata...

---
 src/proc/engine/buffer-provider.cpp           | 44 ++++++++++++-
 src/proc/engine/buffer-provider.hpp           | 21 ++++++-
 .../engine/diagnostic-buffer-provider.cpp     | 63 ++++++++++---------
 .../engine/diagnostic-buffer-provider.hpp     |  4 +-
 .../engine/buffer-provider-protocol-test.cpp  |  4 +-
 5 files changed, 97 insertions(+), 39 deletions(-)

diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index 0ca617577..78cded609 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -29,6 +29,46 @@ namespace engine {
   namespace { // impl. details and definitions
     
     const uint DEFAULT_DESCRIPTOR = 0;
+    
+    typedef uint64_t LocalKey;
+    typedef size_t HashVal;
+    
+    const LocalKey UNSPECIFIC = 0;
+    
+    struct TypeHandler
+      {
+        typedef void (*Ctor) (void*);
+        typedef void (*Dtor) (void*);
+        
+        Ctor createAttached;
+        Dtor destroyAttached;
+        
+        TypeHandler()
+          : createAttached (0)
+          , destroyAttached (0)
+          { }
+        
+        template
+        TypeHandler()
+          : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
+          , destroyAttached (0)
+          { }
+      };
+    
+    const TypeHandler RAW_BUFFER;
+    
+    
+    class Metadata
+      {
+      public:
+        static HashVal
+        key ( size_t storageSize
+            , TypeHandler instanceFunc =RAW_BUFFER
+            , LocalKey specifics =UNSPECIFIC)
+        {
+          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
+        }
+      };
   }
 
 
@@ -47,9 +87,9 @@ namespace engine {
   
   
   BufferDescriptor
-  BufferProvider::getDefaultDescriptor()
+  BufferProvider::getDescriptorFor (size_t storageSize)
   {
-    return BufferDescriptor (*this, DEFAULT_DESCRIPTOR);
+    return BufferDescriptor (*this, Metadata::key (storageSize));
   }
       
       
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index bbb7766ef..cb66710d3 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -61,8 +61,11 @@ namespace engine {
   
   
   /**
-   * Handle for a buffer for processing data, abstracting away the actual implementation.
-   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
+   * Interface: a facility providing and managing working buffers for media calculations.
+   * The pointer to actual buffer storage can be retrieved by
+   * - optionally announcing the required buffer(s) beforehand
+   * - "locking" a buffer to yield a buffer handle
+   * - dereferencing this smart-handle class
    * 
    * @todo as of 6/2011 buffer management within the engine is still a bit vague
    */
@@ -82,7 +85,11 @@ namespace engine {
       
       
       /** describe the kind of buffer managed by this provider */
-      BufferDescriptor getDefaultDescriptor();                //////////////TODO really? there is no sensible "default"
+      BufferDescriptor getDescriptorFor(size_t storageSize=0);
+      
+      template
+      BufferDescriptor getDescriptor();
+
       
       
       /* === API for BuffHandle internal access === */
@@ -111,5 +118,13 @@ namespace engine {
   }
   
   
+  template
+  BufferDescriptor
+  BufferProvider::getDescriptor()
+  {
+    UNIMPLEMENTED ("build descriptor for automatically placing an object instance into the buffer");
+  }
+  
+  
 } // namespace engine
 #endif
diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp
index 69df7c35a..b8f527d41 100644
--- a/src/proc/engine/diagnostic-buffer-provider.cpp
+++ b/src/proc/engine/diagnostic-buffer-provider.cpp
@@ -63,28 +63,25 @@ namespace engine {
         {
           return was_locked_;
         }
+      
+      bool
+      was_closed()  const
+        {
+          return was_locked_;
+        }
+      
+      void*
+      accessMemory()  const
+        {
+          return storage_.get();
+        }
     };
   
     
     
   namespace { // Details of allocation and accounting
-
-    using lumiera::typelist::FunctionSignature;
-    using std::tr1::placeholders::_1;
-    using std::tr1::function;
-    using std::tr1::bind;
     
-    /**
-     * type rebinding template for issuing flexible queries
-     * and forwarding them to the individual block entries
-     */
-    template
-    struct _Query
-      {
-        typedef typename FunctionSignature::Ret Result;
-      };
-    
-    function QUERY_was_used = bind (&Block::was_used, _1 );
+    const uint MAX_BUFFERS = 50;
   
   } // (END) Details of allocation and accounting
   
@@ -119,21 +116,27 @@ namespace engine {
       
       virtual ~HeapMemProvider()
         {
-          INFO (proc_mem, "discarding %uz diagnostic buffer entries", HeapMemProvider::size());
+          INFO (proc_mem, "discarding %zu diagnostic buffer entries", HeapMemProvider::size());
         }
       
-      template
-      typename _Query::Result
-      accessStats (uint bufferID, FUN queryFunction)
-        {
-          queryFunction (access_or_create (bufferID));
-        }
-      
-    private:
       Block&
       access_or_create (uint bufferID)
         {
-          UNIMPLEMENTED ("either get existing block or create new entries to fit");
+          while (!withinStorageSize (bufferID))
+            manage (new Block);
+          
+          ENSURE (withinStorageSize (bufferID));
+          return (*this)[bufferID];
+        }
+      
+    private:
+      bool
+      withinStorageSize (uint bufferID)  const
+        {
+          if (bufferID >= MAX_BUFFERS)
+            throw error::Fatal ("hardwired internal limit for test buffers exceeded");
+          
+          return bufferID < size();
         }
     };
   
@@ -181,21 +184,21 @@ namespace engine {
   bool
   DiagnosticBufferProvider::buffer_was_used (uint bufferID)  const
     {
-      pImpl_->accessStats (bufferID, QUERY_was_used);
+      return pImpl_->access_or_create(bufferID).was_used();
     }
   
   
   bool
   DiagnosticBufferProvider::buffer_was_closed (uint bufferID)  const
     {
-      UNIMPLEMENTED ("check closed-flag of a specific buffer");
+      return pImpl_->access_or_create(bufferID).was_closed();
     }
   
   
   void*
-  DiagnosticBufferProvider::accessStorage (uint bufferID)  const
+  DiagnosticBufferProvider::accessMemory (uint bufferID)  const
     {
-      
+      return pImpl_->access_or_create(bufferID).accessMemory();
     }
   
 
diff --git a/src/proc/engine/diagnostic-buffer-provider.hpp b/src/proc/engine/diagnostic-buffer-provider.hpp
index 399f7d782..f79ca49d2 100644
--- a/src/proc/engine/diagnostic-buffer-provider.hpp
+++ b/src/proc/engine/diagnostic-buffer-provider.hpp
@@ -80,7 +80,7 @@ namespace engine {
        *        for diagnostic access or wasn't registered beforehand.
        */
       static DiagnosticBufferProvider&
-      access (BufferProvider const&);
+      access (BufferProvider const& );
       
       
       
@@ -89,7 +89,7 @@ namespace engine {
       
       bool buffer_was_used (uint bufferID)  const;
       bool buffer_was_closed (uint bufferID)  const;
-      void* accessStorage (uint bufferID)  const;
+      void* accessMemory (uint bufferID)  const;
       
       
       template
diff --git a/tests/components/proc/engine/buffer-provider-protocol-test.cpp b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
index 7846dec5a..553f93190 100644
--- a/tests/components/proc/engine/buffer-provider-protocol-test.cpp
+++ b/tests/components/proc/engine/buffer-provider-protocol-test.cpp
@@ -101,7 +101,7 @@ namespace test  {
           CHECK (checker.object_was_attached (0));
           CHECK (checker.object_was_destroyed (0));
           
-          CHECK (testData(0) == checker.accessStorage (0));
+          CHECK (testData(0) == checker.accessMemory (0));
         }
       
       
@@ -112,10 +112,10 @@ namespace test  {
           // In real usage, a suitable memory/frame/buffer provider
           // will be preconfigured, depending on the usage context
           BufferProvider& provider = DiagnosticBufferProvider::build();
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           
           BufferDescriptor desc1 = provider.getDescriptor();
           BufferDescriptor desc2 = provider.getDescriptorFor(TEST_SIZE);
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #829
           CHECK (desc1.verifyValidity());
           CHECK (desc2.verifyValidity());
           

From 8a2c94014cfafb2b17999e59f09d1a1eeaf1f619 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 22 Sep 2011 18:57:06 +0200
Subject: [PATCH 070/108] extract buffer metadata handling into separate entity

---
 src/proc/engine/buffer-metadata.hpp           | 135 ++++++++++++++++
 src/proc/engine/buffer-provider.cpp           |  41 +----
 tests/46engine.tests                          |   6 +-
 .../proc/engine/buffer-metadata-test.cpp      | 146 ++++++++++++++++++
 4 files changed, 288 insertions(+), 40 deletions(-)
 create mode 100644 src/proc/engine/buffer-metadata.hpp
 create mode 100644 tests/components/proc/engine/buffer-metadata-test.cpp

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
new file mode 100644
index 000000000..ca9d5b734
--- /dev/null
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -0,0 +1,135 @@
+/*
+  BUFFER-METADATA.hpp  -  internal metadata for data buffer providers
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+/** @file buffer-metadate.hpp
+ ** Metadata for managing and accessing buffers. The Lumiera Engine uses the
+ ** Abstraction of an BufferProvider to handle various kinds of buffer organisation
+ ** and access in a uniform way. Actually, buffers can be exposed and provided by several
+ ** facilities, which might even be implemented through an external library. Thus the engine
+ ** and the abstraction placed in between needs a common set of control data, to be able to
+ ** expose the correct buffer for each request. Typically -- and independent of the actual
+ ** implementation -- the following properties need to be tracked
+ ** - that overall storage size available within the buffer
+ ** - a pair of custom \em creator and \em destructor functions to use together with this buffer
+ ** - an additional client key to distinguish otherwise otherwise identical client requests
+ ** These three distinctions are applied in sequence, thus forming a tree with 3 levels.
+ ** Only the first distinguishing level (the size) is mandatory. The others are provided,
+ ** because some of the foreseeable buffer providers allow to re-access the data placed
+ ** into the buffer, by assigning an internally managed ID to the buffer. The most
+ ** prominent example is the frame cache, which obviously needs to keep track of
+ ** the buffers after the render engine is finished, while the engine code
+ ** just accesses yet another buffer to place the results of calculations.
+ ** 
+ ** These additional distinctions and properties are associated with the help of the
+ ** BufferDescriptor, embedded into each BuffHandle. While the engine just uses these
+ ** handles in the way of a pointer, the buffer descriptor acts as an additional tag
+ ** attached to the buffer access, allowing to re-access a context within the
+ ** buffer provider implementation.
+ ** 
+ ** @see buffer-provider.hpp
+ ** @see BufferMetadata_test
+ ** @see BufferProviderProtocol_test
+ */
+
+#ifndef PROC_ENGINE_BUFFR_METADATA_H
+#define PROC_ENGINE_BUFFR_METADATA_H
+
+
+#include "lib/error.hpp"
+
+//#include 
+
+
+namespace engine {
+  
+  
+  typedef uint64_t LocalKey;
+  typedef size_t HashVal;
+  
+  const LocalKey UNSPECIFIC = 0;
+  
+  struct TypeHandler
+    {
+      typedef void (*Ctor) (void*);
+      typedef void (*Dtor) (void*);
+      
+      Ctor createAttached;
+      Dtor destroyAttached;
+      
+      TypeHandler()
+        : createAttached (0)
+        , destroyAttached (0)
+        { }
+      
+      template
+      TypeHandler()
+        : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
+        , destroyAttached (0)
+        { }
+    };
+  
+  const TypeHandler RAW_BUFFER;
+  
+  
+  class Metadata
+    {
+    public:
+      static HashVal
+      key ( size_t storageSize
+          , TypeHandler instanceFunc =RAW_BUFFER
+          , LocalKey specifics =UNSPECIFIC)
+        {
+          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
+        }
+      
+      static Metadata&
+      get (HashVal key)
+        {
+          UNIMPLEMENTED ("access, possibly create metadata records");
+        }
+      
+      BufferState
+      state ()  const
+        {
+          UNIMPLEMENTED ("buffer state accounting");
+        }
+      
+      Metadata&
+      mark (BufferState newState)
+        {
+          UNIMPLEMENTED ("buffer state transitions");
+          return *this;
+        }
+    };
+    
+    
+    
+    
+  /* === Implementation === */
+  
+  /** */
+  
+  
+  
+  
+} // namespace engine
+#endif
diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index 78cded609..d2820d211 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -22,6 +22,8 @@
 
 
 #include "proc/engine/buffer-provider.hpp"
+#include "proc/engine/buffer-metadata.hpp"
+
 
 namespace engine {
   
@@ -30,45 +32,6 @@ namespace engine {
     
     const uint DEFAULT_DESCRIPTOR = 0;
     
-    typedef uint64_t LocalKey;
-    typedef size_t HashVal;
-    
-    const LocalKey UNSPECIFIC = 0;
-    
-    struct TypeHandler
-      {
-        typedef void (*Ctor) (void*);
-        typedef void (*Dtor) (void*);
-        
-        Ctor createAttached;
-        Dtor destroyAttached;
-        
-        TypeHandler()
-          : createAttached (0)
-          , destroyAttached (0)
-          { }
-        
-        template
-        TypeHandler()
-          : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
-          , destroyAttached (0)
-          { }
-      };
-    
-    const TypeHandler RAW_BUFFER;
-    
-    
-    class Metadata
-      {
-      public:
-        static HashVal
-        key ( size_t storageSize
-            , TypeHandler instanceFunc =RAW_BUFFER
-            , LocalKey specifics =UNSPECIFIC)
-        {
-          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
-        }
-      };
   }
 
 
diff --git a/tests/46engine.tests b/tests/46engine.tests
index bc4097114..2c9df05db 100644
--- a/tests/46engine.tests
+++ b/tests/46engine.tests
@@ -7,7 +7,11 @@ return: 0
 END
 
 
-PLANNED "BuffTable_test" BuffTable_test <
+
+  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 "lib/error.hpp"
+#include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+//#include "lib/util-foreach.hpp"
+#include "lib/util.hpp"
+//#include "proc/play/diagnostic-output-slot.hpp"
+//#include "proc/engine/testframe.hpp"
+//#include "proc/engine/diagnostic-buffer-provider.hpp"
+#include "proc/engine/buffer-metadata.hpp"
+//#include "proc/engine/buffhandle.hpp"
+//#include "proc/engine/bufftable.hpp"
+
+//#include 
+#include 
+//#include 
+
+//using boost::format;
+//using std::string;
+//using std::cout;
+//using util::for_each;
+using util::isnil;
+using util::isSameObject;
+
+
+namespace engine{
+namespace test  {
+  
+//  using lib::AllocationCluster;
+//  using mobject::session::PEffect;
+//  using ::engine::BuffHandle;
+  using lumiera::error::LUMIERA_ERROR_INVALID;
+  using lumiera::error::LUMIERA_ERROR_LIFECYCLE;
+  
+  
+  namespace { // Test fixture
+    
+    const size_t TEST_MAX_SIZE = 1024 * 1024;
+    
+    const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE 
+    const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE 
+    
+    const HashVal JUST_SOMETHING = 123;
+//  const uint TEST_SIZE = 1024*1024;
+//  const uint TEST_ELMS = 20;
+      
+    bool
+    ensure_proper_fixture() 
+    {
+      return (SIZE_A != SIZE_B)
+          && (JUST_SOMETHING != Metadata::key(SIZE_A))
+          && (JUST_SOMETHING != Metadata::key(SIZE_B))
+          ;
+    }
+    
+  }
+  
+  
+  /*******************************************************************
+   * @test verify the properties of the BufferMetadata records used
+   *       internally within BufferProvider to attach additional
+   *       organisational data to the exposed buffers.
+   */
+  class BufferMetadata_test : public Test
+    {
+      virtual void
+      run (Arg) 
+        {
+          UNIMPLEMENTED ("cover all metadata properties");
+          CHECK (ensure_proper_fixture());
+          verifyBasicProperties();
+          verifyStandardCase();
+        }
+      
+      
+      void
+      verifyBasicProperties()
+        {
+          HashVal key = Metadata::key(SIZE_A);
+          CHECK (key);
+          
+          HashVal key1 = Metadata::key(SIZE_A);
+          HashVal key2 = Metadata::key(SIZE_B);
+          CHECK (key1);
+          CHECK (key2);
+          CHECK (key == key1);
+          CHECK (key != key2);
+          
+          VERIFY_ERROR (INVALID, Metadata::get(0))
+          VERIFY_ERROR (INVALID, Metadata::get(JUST_SOMETHING));
+          CHECK ( & Metadata::get(key));
+          CHECK ( & Metadata::get(key1));
+          CHECK ( & Metadata::get(key2));
+          
+          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key)));
+          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key1)));
+          CHECK (!isSameObject (Metadata::get(key), Metadata::get(key2)));
+          
+          Metadata& m1 = Metadata::get(key);
+          CHECK (NIL == m1.state());
+          
+          VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
+          
+          m1.mark (LOCKED);
+          CHECK (LOCKED == m1.state());
+          CHECK (LOCKED == Metadata::get(key1).state());
+        }
+      
+      
+      void
+      verifyStandardCase()
+        {
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (BufferMetadata_test, "unit player");
+  
+  
+  
+}} // namespace engine::test

From 1ea3cba2f5237e68de28788f35375b8460165663 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 23 Sep 2011 15:31:20 +0200
Subject: [PATCH 071/108] remold the buffer metadata into a PImple used by
 BufferProvider

---
 src/proc/engine/buffer-metadata.hpp           | 51 ++++++++++++-----
 src/proc/engine/buffer-provider.cpp           |  8 ++-
 src/proc/engine/buffer-provider.hpp           | 17 +++---
 .../engine/diagnostic-buffer-provider.cpp     |  4 +-
 .../proc/engine/buffer-metadata-test.cpp      | 57 +++++++++++--------
 5 files changed, 89 insertions(+), 48 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index ca9d5b734..13cc9432b 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -55,16 +55,27 @@
 
 
 #include "lib/error.hpp"
+#include "lib/symbol.hpp"
 
 //#include 
 
 
 namespace engine {
   
+  using lib::Literal;
+  
   
   typedef uint64_t LocalKey;
   typedef size_t HashVal;
   
+  enum BufferState
+    { NIL,
+      FREE,
+      LOCKED,
+      EMITTED,
+      BLOCKED
+    };
+  
   const LocalKey UNSPECIFIC = 0;
   
   struct TypeHandler
@@ -93,7 +104,17 @@ namespace engine {
   class Metadata
     {
     public:
-      static HashVal
+      struct Entry
+        {
+          BufferState state ()  const;
+          Entry& mark (BufferState newState);
+        };
+      
+      
+      Metadata (Literal implementationID)
+        { }
+      
+      HashVal
       key ( size_t storageSize
           , TypeHandler instanceFunc =RAW_BUFFER
           , LocalKey specifics =UNSPECIFIC)
@@ -101,24 +122,14 @@ namespace engine {
           UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
         }
       
-      static Metadata&
+      Entry&
       get (HashVal key)
         {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
-      BufferState
-      state ()  const
-        {
-          UNIMPLEMENTED ("buffer state accounting");
-        }
-      
-      Metadata&
-      mark (BufferState newState)
-        {
-          UNIMPLEMENTED ("buffer state transitions");
-          return *this;
-        }
+#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
     };
     
     
@@ -127,6 +138,18 @@ namespace engine {
   /* === Implementation === */
   
   /** */
+  BufferState
+  Metadata::Entry::state ()  const
+    {
+      UNIMPLEMENTED ("buffer state accounting");
+    }
+  
+  Metadata::Entry&
+  Metadata::Entry::mark (BufferState newState)
+    {
+      UNIMPLEMENTED ("buffer state transitions");
+      return *this;
+    }
   
   
   
diff --git a/src/proc/engine/buffer-provider.cpp b/src/proc/engine/buffer-provider.cpp
index d2820d211..e0670cd73 100644
--- a/src/proc/engine/buffer-provider.cpp
+++ b/src/proc/engine/buffer-provider.cpp
@@ -34,7 +34,11 @@ namespace engine {
     
   }
 
-
+  
+  BufferProvider::BufferProvider (Literal implementationID)
+    : meta_(new Metadata (implementationID))
+    { }
+  
   BufferProvider::~BufferProvider() { }
   
   
@@ -52,7 +56,7 @@ namespace engine {
   BufferDescriptor
   BufferProvider::getDescriptorFor (size_t storageSize)
   {
-    return BufferDescriptor (*this, Metadata::key (storageSize));
+    return BufferDescriptor (*this, meta_->key (storageSize));
   }
       
       
diff --git a/src/proc/engine/buffer-provider.hpp b/src/proc/engine/buffer-provider.hpp
index cb66710d3..6f025a1c8 100644
--- a/src/proc/engine/buffer-provider.hpp
+++ b/src/proc/engine/buffer-provider.hpp
@@ -43,21 +43,20 @@
 
 
 #include "lib/error.hpp"
+#include "lib/symbol.hpp"
 #include "proc/engine/buffhandle.hpp"
 
 #include 
+#include 
 
 
 namespace engine {
   
+  using boost::scoped_ptr;
+  using lib::Literal;
   
-  enum BufferState
-    { NIL,
-      FREE,
-      LOCKED,
-      EMITTED,
-      BLOCKED
-    };
+  
+  class Metadata;
   
   
   /**
@@ -72,6 +71,10 @@ namespace engine {
   class BufferProvider
     : boost::noncopyable
     {
+      scoped_ptr meta_;
+      
+    protected:
+      BufferProvider (Literal implementationID);
       
     public:
       virtual ~BufferProvider();  ///< this is an interface
diff --git a/src/proc/engine/diagnostic-buffer-provider.cpp b/src/proc/engine/diagnostic-buffer-provider.cpp
index b8f527d41..1711e1a6b 100644
--- a/src/proc/engine/diagnostic-buffer-provider.cpp
+++ b/src/proc/engine/diagnostic-buffer-provider.cpp
@@ -111,8 +111,8 @@ namespace engine {
       
     public:
       HeapMemProvider()
-        {
-        }
+        : BufferProvider ("Diagnostic_HeapAllocated")
+        { }
       
       virtual ~HeapMemProvider()
         {
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index a8573bfd0..839307aad 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -34,6 +34,7 @@
 //#include "proc/engine/bufftable.hpp"
 
 //#include 
+#include 
 #include 
 //#include 
 
@@ -41,6 +42,7 @@
 //using std::string;
 //using std::cout;
 //using util::for_each;
+using boost::scoped_ptr;
 using util::isnil;
 using util::isSameObject;
 
@@ -48,6 +50,7 @@ using util::isSameObject;
 namespace engine{
 namespace test  {
   
+
 //  using lib::AllocationCluster;
 //  using mobject::session::PEffect;
 //  using ::engine::BuffHandle;
@@ -59,21 +62,13 @@ namespace test  {
     
     const size_t TEST_MAX_SIZE = 1024 * 1024;
     
-    const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE 
-    const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE 
+    const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
+    const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
     
     const HashVal JUST_SOMETHING = 123;
 //  const uint TEST_SIZE = 1024*1024;
 //  const uint TEST_ELMS = 20;
       
-    bool
-    ensure_proper_fixture() 
-    {
-      return (SIZE_A != SIZE_B)
-          && (JUST_SOMETHING != Metadata::key(SIZE_A))
-          && (JUST_SOMETHING != Metadata::key(SIZE_B))
-          ;
-    }
     
   }
   
@@ -85,6 +80,9 @@ namespace test  {
    */
   class BufferMetadata_test : public Test
     {
+      /** common Metadata table to be tested */
+      scoped_ptr meta_;
+    
       virtual void
       run (Arg) 
         {
@@ -95,37 +93,50 @@ namespace test  {
         }
       
       
+      bool
+      ensure_proper_fixture() 
+      {
+        if (!meta_)
+          meta_.reset(new Metadata("BufferMetadata_test"));
+        
+        return (SIZE_A != SIZE_B)
+            && (JUST_SOMETHING != meta_->key(SIZE_A))
+            && (JUST_SOMETHING != meta_->key(SIZE_B))
+            ;
+      }
+      
+      
       void
       verifyBasicProperties()
         {
-          HashVal key = Metadata::key(SIZE_A);
+          HashVal key = meta_->key(SIZE_A);
           CHECK (key);
           
-          HashVal key1 = Metadata::key(SIZE_A);
-          HashVal key2 = Metadata::key(SIZE_B);
+          HashVal key1 = meta_->key(SIZE_A);
+          HashVal key2 = meta_->key(SIZE_B);
           CHECK (key1);
           CHECK (key2);
           CHECK (key == key1);
           CHECK (key != key2);
           
-          VERIFY_ERROR (INVALID, Metadata::get(0))
-          VERIFY_ERROR (INVALID, Metadata::get(JUST_SOMETHING));
-          CHECK ( & Metadata::get(key));
-          CHECK ( & Metadata::get(key1));
-          CHECK ( & Metadata::get(key2));
+          VERIFY_ERROR (INVALID, meta_->get(0))
+          VERIFY_ERROR (INVALID, meta_->get(JUST_SOMETHING));
+          CHECK ( & meta_->get(key));
+          CHECK ( & meta_->get(key1));
+          CHECK ( & meta_->get(key2));
           
-          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key)));
-          CHECK ( isSameObject (Metadata::get(key), Metadata::get(key1)));
-          CHECK (!isSameObject (Metadata::get(key), Metadata::get(key2)));
+          CHECK ( isSameObject (meta_->get(key), meta_->get(key)));
+          CHECK ( isSameObject (meta_->get(key), meta_->get(key1)));
+          CHECK (!isSameObject (meta_->get(key), meta_->get(key2)));
           
-          Metadata& m1 = Metadata::get(key);
+          Metadata::Entry& m1 = meta_->get(key);
           CHECK (NIL == m1.state());
           
           VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
           
           m1.mark (LOCKED);
           CHECK (LOCKED == m1.state());
-          CHECK (LOCKED == Metadata::get(key1).state());
+          CHECK (LOCKED == meta_->get(key1).state());
         }
       
       

From 5188982e7118edbb13929a21cea20252fed00b51 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 24 Sep 2011 02:27:12 +0200
Subject: [PATCH 072/108] considering details regarding the usage of buffer
 metadata

---
 wiki/renderengine.html | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 5b6ca80dc..42a7cb353 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -1077,8 +1077,8 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
-
-
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
+
+
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these operations, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
 
 Obviously, overloading a pointer with semantic meaning isn't exactly a brilliant idea -- and the usual answer is to embed this pointer into a smart handle, which also yields the nice side-effect of explaining this design to the reader. Thus a buffer handle
 * can only be obtained from a BufferProvider
@@ -1091,7 +1091,7 @@ To perform anything useful with such a buffer handle, the client code needs some
 
 Just linking this type information to the context is certainly the most elegant solution, but also by far the most difficult to achieve -- not to mention the implicit dependency on a very specific invocation situation. So for now (9/2011) it seems best to stick to the simple and explicit implementation, just keeping that structural optimisation in mind. And the link to this buffer type information should be made explicit within the definition anyway, even if we choose to employ another design tradeoff later.
 * thus the conclusion is: we introduce a ''descriptor object'', which will be stored within the handle
-* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to organise implementation details
+* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to [[organise implementation details|BufferMetadata]]
 
 
 !sanity checks
@@ -1099,7 +1099,7 @@ there are only limited sanity checks, and they can be expected to be optimised a
 Basically the client is responsible for sane buffer access.
 
-
+
Buffers are used to hold the media data for processing and output. Within the Lumiera RenderEngine and [[Player]] subsystem, we use some common concepts to handle the access and allocation of working buffers. Yet this doesn't imply having only one central authority in charge of every buffer -- such an approach wouldn't be possible (due to collaboration with external systems) and wouldn't be desirable either. Rather, there are some common basic usage //patterns// -- and there are some core interfaces used throughout the organisation of the rendering process.
 
 Mostly, the //client code,// i.e. code in need of using buffers, can access some BufferProvider, thereby delegating the actual buffer management. This binds the client to adhere to kind of a //buffer access protocol,// comprised of the ''announcing'', ''locking'', optionally ''attaching'' and finally the ''releasing'' steps. Here, the actual buffer management within the provider is a question of implementation and will be configured during build-up of the scope in question.
@@ -1115,13 +1115,25 @@ Mostly, the //client code,// i.e. code in need of using buffers, can access some
 :in all those situations, where we just need a working buffer for some time, we can rely on our internal custom memory allocator.
 :{{red{~Not-Yet-Implemented as of 9/11}}} -- as a fallback we just rely on heap allocations through the language runtime
 ;frame cache
-:whenever a calculated result may be of further interest beyond the immediate need triggering the calculation, it might be eligible for caching.
+:whenever a calculated result may be of further interest, beyond the immediate need triggering the calculation, it might be eligible for caching.
 :The Lumiera ''frame cache'' is a special BufferProvider, maintaining a larger pool of buffers which can be pinned and kept around for some time,
 :accomodating limited resources and current demand for fresh result buffers.
 
 
-
+
+
the generic BufferProvider implementation exposes a service to attach and maintain additional metadata with individual buffers. Using this service is not mandatory -- a concrete buffer provider implementation may chose to maintain more specific metadata right on the implementation level, especially if more elaborate management is necessary within the implementation anyway (e.g. the frame index). We can expect most buffer provider implementations to utilise at least the generic buffer type id service though.
+
+!buffer types and descriptors
+Client code accesses buffer through [[smart buffer handles|BuffHandle]], including some kind of buffer type information, encoded into a type ID within the ''buffer descriptor''. These descriptors are used like prototypes, relating the type-~IDs hierarchically. Obviously, the most fundamental distinction is the BufferProvider in charge for that specific buffer. Below that, the next mandatory level of distinction is the ''buffer size''. In some cases, additional distinctions can be necessary. Each BufferProvider exposes a service to yield unique type ~IDs governed by such a hierarchical scheme.
+
+!state and metadata for individual buffers
+Beyond that, it can be necessary to associate at least a state flag with //individual buffers.// Doing so requires the buffer to be in //locked state,// otherwise it wouldn't be distinguishable as an separate entity (a client is able to access the buffer memory address only after "locking" this buffer). Especially when using a buffer provider in conjunction with an OutputSlot, these states and transitions are crucial for performing an orderly handover of generated data from the producer (render engine) to the consumer (external output sink).
+
+__Note__: while the API to access this service is uniform, conceptually there is a difference between just using the (shared) type information and associating individual metadata, like the buffer state. Type-~IDs, once allocated, will never be discarded (within the lifetime of an Lumiera application instance -- buffer associations aren't persistent). To the contrary, individual metadata //will be discarded,// when releasing the corresponding buffer. According to the ''prototype pattern'', individual metadata is treated as a one-way-off specialisation.
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
 * that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
 * that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
@@ -1130,14 +1142,18 @@ Mostly, the //client code,// i.e. code in need of using buffers, can access some
 !operations
 While BufferProvider is an interface meant to be backed by various different kinds of buffer and memory management approaches, there is a common set of operations to be supported by any of them
 ;announcing
-:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations (or similar mechanisms to ensure the avialability) to happen right away; the BufferProvider will then return the actual number of buffers guraanteed to be available. This announcing step is optional an can happen anytime before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
+:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations to happen right away, or it might trigger similar mechanisms to ensure availability; the BufferProvider will then return the actual number of buffers guaranteed to be available. This announcing step is optional an can happen any time before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. (Currently 9/2011 this is meant to be global for the whole BufferProvider, but it might happen that we need to break that down to individual clients)
 ;locking
-:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again until released. If necessary, at that point the BufferProvider might allocate memory to accomodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //anounced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again unless released. If necessary, at that point the BufferProvider might allocate memory to accommodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //announced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
 ;attaching
-:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be automatically destroyed when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, which will be invoked in a similar manner. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
+:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be destroyed automatically when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, to be invoked in a similar way. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
 ;releasing
 :buffers need to be released explicitly by the client code. This renders the corresponding BuffHandle invalid, (optionally) invokes a destructor function of an attached object and maybe reclaims the buffer memory
 
+!!type metadata service
+In addition to the basic operations, clients may associate BufferMetadata with individual buffers;
+in the basic form, this means just maintaining a type tag describing the kind of buffer, while optionally this service might be extended to e.g. associating a state flag.
+
 __see also__
 &rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
 &rarr; more about BufferManagement within the RenderEngine and [[Player]] subsystem
@@ -3480,8 +3496,8 @@ Thus there are two serious problem situations
 &rarr; SchedulerRequirements
 &rarr; OutputSlotImpl
-
-
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot is outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot will be outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
 
 !operation steps
 [>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.

From bc756e42d93007d23154db37ed9e0957ca597039 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 24 Sep 2011 15:27:58 +0200
Subject: [PATCH 073/108] rewrite (planned) test to reflect the new metadata
 design

---
 src/proc/engine/buffer-metadata.hpp           | 47 ++++++++++++++++++-
 tests/46engine.tests                          |  6 +--
 .../proc/engine/buffer-metadata-test.cpp      | 26 ++++++++--
 3 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index 13cc9432b..c71359313 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -106,8 +106,10 @@ namespace engine {
     public:
       struct Entry
         {
-          BufferState state ()  const;
+          BufferState state()  const;
+          HashVal parentKey()  const;
           Entry& mark (BufferState newState);
+          Entry& markLocked (const void* buffer);
         };
       
       
@@ -122,12 +124,42 @@ namespace engine {
           UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
         }
       
+      HashVal
+      key (HashVal parentKey, TypeHandler instanceFunc)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      HashVal
+      key (HashVal parentKey, LocalKey specifics)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      HashVal
+      key (HashVal parentKey, const void* concreteBuffer)
+        {
+          UNIMPLEMENTED ("create sub-object key for concrete buffer");
+        }
+      
       Entry&
       get (HashVal key)
         {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
+      bool
+      isKnown (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: known record?");
+        }
+      
+      bool
+      isLocked (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
+        }
+      
 #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
 #endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
     };
@@ -144,6 +176,13 @@ namespace engine {
       UNIMPLEMENTED ("buffer state accounting");
     }
   
+  /** */
+  HashVal
+  Metadata::Entry::parentKey ()  const
+    {
+      UNIMPLEMENTED ("retrieve the sparent (object or type) key");
+    }
+  
   Metadata::Entry&
   Metadata::Entry::mark (BufferState newState)
     {
@@ -151,6 +190,12 @@ namespace engine {
       return *this;
     }
   
+  Metadata::Entry&
+  Metadata::Entry::markLocked (const void* buffer)
+    {
+      UNIMPLEMENTED ("transition to locked state");
+      return *this;
+    }
   
   
   
diff --git a/tests/46engine.tests b/tests/46engine.tests
index 2c9df05db..e1a3f9704 100644
--- a/tests/46engine.tests
+++ b/tests/46engine.tests
@@ -2,16 +2,16 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en
 
 
 
-PLANNED "BufferProviderProtocol_test" Buffer provider diagnostics <get(key);
           CHECK (NIL == m1.state());
+          CHECK (!meta_->isLocked(key));
           
           VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
+          VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED)  );
           
-          m1.mark (LOCKED);
-          CHECK (LOCKED == m1.state());
-          CHECK (LOCKED == meta_->get(key1).state());
+          Metadata::Entry& m2 = m1.markLocked (SOME_POINTER);
+          CHECK (!isSameObject (m1,m2));
+          CHECK (NIL    == m1.state());
+          CHECK (LOCKED == m2.state());
+          
+          HashVal keyX = meta_->key(key1, SOME_POINTER);
+          CHECK (meta_->isLocked(keyX));
+          CHECK (keyX != key1);
+          CHECK (keyX);
+          
+          CHECK ( isSameObject (m1, meta_->get(key)));
+          CHECK ( isSameObject (m1, meta_->get(key1))); 
+          CHECK ( isSameObject (m2, meta_->get(keyX)));
+          CHECK ( key1 == m2.parentKey());
+          
+          m2.mark(FREE);              // Warning: don't use the m2 reference anymore! 
+          CHECK (!meta_->isLocked(keyX));
+          CHECK (!meta_->isKnown(keyX));
+          CHECK ( meta_->isKnown(key1));
+          VERIFY_ERROR (INVALID, meta_->get(keyX));
         }
       
       

From db2b02f0c51c46e0f141ee94727bda6d806cac37 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 04:07:30 +0200
Subject: [PATCH 074/108] define a front-end for explicit allocations

...currently just defined by forwarding
to std::allocator
---
 src/lib/result.hpp                            |   6 +-
 src/lib/scoped-holder-transfer.hpp            |   4 +-
 src/lib/simple-allocator.hpp                  | 262 ++++++++++++++++++
 src/lib/typed-allocation-manager.hpp          |   4 +-
 src/proc/control/command-argument-holder.hpp  |   1 -
 src/proc/control/command-impl.hpp             |   1 -
 src/proc/engine/buffer-metadata.hpp           |   5 +-
 tests/40components.tests                      |   7 +-
 .../proc/engine/buffer-metadata-test.cpp      |   2 +-
 tests/lib/simple-allocator-test.cpp           | 160 +++++++++++
 10 files changed, 440 insertions(+), 12 deletions(-)
 create mode 100644 src/lib/simple-allocator.hpp
 create mode 100644 tests/lib/simple-allocator-test.cpp

diff --git a/src/lib/result.hpp b/src/lib/result.hpp
index daf153a49..2f42c7f64 100644
--- a/src/lib/result.hpp
+++ b/src/lib/result.hpp
@@ -24,9 +24,9 @@
 /** @file result.hpp
  ** Intermediary value object to represent the result of an operation.
  ** This operation might have produced a value result or failed with an exception.
- ** Typically, the Result token used \em inline -- immediately either invoking one
- ** of the member function or employing the built-in result type conversion. It
- ** will be copyable iff the result value is copyable. There is an implicit
+ ** Typically, the Result token is used \em inline -- immediately either invoking
+ ** one of the member function or employing the built-in result type conversion.
+ ** It will be copyable iff the result value is copyable. There is an implicit
  ** valid or failure state, which can be tested. Any attempt to get the value
  ** of an invalid result token will cause in an exception to be thrown.
  ** 
diff --git a/src/lib/scoped-holder-transfer.hpp b/src/lib/scoped-holder-transfer.hpp
index e1ee6d5c9..58508f393 100644
--- a/src/lib/scoped-holder-transfer.hpp
+++ b/src/lib/scoped-holder-transfer.hpp
@@ -105,8 +105,8 @@ namespace lib {
       pointer       address(reference r)            const { return par_.address(r); }
       const_pointer address(const_reference cr)     const { return par_.address(cr); }
       pointer       allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
-      void          deallocate(pointer p, size_type n)    { return par_.deallocate(p,n); }
-      void          destroy(pointer p)                    { return par_.destroy(p); }
+      void          deallocate(pointer p, size_type n)    {        par_.deallocate(p,n); }
+      void          destroy(pointer p)                    {        par_.destroy(p); }
       
       
       void 
diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp
new file mode 100644
index 000000000..71b0438fa
--- /dev/null
+++ b/src/lib/simple-allocator.hpp
@@ -0,0 +1,262 @@
+/*
+  SIMPLE-ALLOCATOR.hpp  -  frontend for plain explicit allocations 
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+/** @file simple-allocator.hpp
+ ** Frontend and marker interface for allocating small objects explicitly.
+ ** Contrary to the TypedAllocationManager, the SimpleAllocator doesn't provide any
+ ** ref-counting or tracking facilities, nor does he support bulk de-allocation.
+ ** Each object needs to be allocated and released by explicit call.
+ ** The advantage over using std::allocator  directly is the shortcut for (placement) construction,
+ ** and -- of course -- the ability to exchange the memory model at one central location.
+ ** 
+ ** SimpleAllocator instances will be defined for a specific collection of types; for each of those
+ ** types, there will be an embedded dedicated custom allocator (currently as of 9/2011, just
+ ** implemented as std::allocator). Objects of these preconfigured types can be constructed
+ ** and destroyed through this allocator instance. Each call needs to be done explicitly, with
+ ** the precise, concrete type to be created or destroyed. This is especially important for
+ ** the releasing of objects: there is \em no support for any kind of virtual destruction.
+ ** 
+ ** @see engine::BufferMetadata
+ ** @see TypedAllocationManager (another more elaborate custom allocation scheme)
+ **
+ */
+
+
+
+#ifndef LIB_SIMPLE_ALLOCATOR_H
+#define LIB_SIMPLE_ALLOCATOR_H
+
+//#include "pre.hpp"
+#include "lib/error.hpp"
+#include "lib/meta/generator.hpp"
+#include "lib/format.hpp"
+//#include "lib/typed-counter.hpp"
+#include "include/logging.h"
+
+
+//#include 
+#include 
+#include 
+
+
+
+namespace lib {
+  
+//  using std::tr1::shared_ptr;
+  using lumiera::typelist::InstantiateForEach;
+  using lumiera::typelist::Types;
+  
+  namespace {
+    using lumiera::typelist::Node;
+    using lumiera::typelist::NullType;
+    
+    template
+    struct IsInList
+      {
+        enum{ value = false };
+      };
+    
+    template
+    struct IsInList, TY>
+      {
+        enum{ value = true };
+      };
+    
+    template
+    struct IsInList, TY>
+      {
+        enum{ value = IsInList::value };
+      };
+    
+  }
+  
+  
+  template
+  class CustomAllocator
+    : public std::allocator
+    {
+      
+    };
+  
+    
+  
+  /**
+   * Frontend for explicit allocations, using a custom allocator.
+   * This template is to be instantiated for the collection of types
+   * later to be allocated through this custom memory manager/model.
+   * It provides convenience shortcuts for placement-construction
+   * and releasing of target objects.
+   * 
+   * @todo currently (as of 8/09) the low-level pooled allocator
+   *       isn't implemented; instead we do just heap allocations.
+   *       see Ticket #835
+   */
+  template
+  class SimpleAllocator
+    : InstantiateForEach< typename TYPES::List     // for each of those types...         
+                        , CustomAllocator         //  ...mix in the custom allocator 
+                        >
+    {
+      
+      /** forward plain memory allocation */
+      template
+      XX *
+      allocateSlot ()
+        {
+          TRACE (memory, "allocate %s", util::tyStr().c_str());
+          return CustomAllocator::allocate (1);
+        }
+      
+      template
+      void
+      releaseSlot (XX* entry)
+        {
+          TRACE (memory, "release %s", util::tyStr().c_str());
+          CustomAllocator::deallocate (entry, 1);
+        }
+      
+      
+      template
+      void
+      ___assertSupportedType()
+        {
+          typedef typename TYPES::List PreconfiguredTypes;
+          typedef IsInList IsPreconfiguredType;
+          
+          BOOST_STATIC_ASSERT (IsPreconfiguredType::value);
+        }
+     
+        
+      
+      
+      public: /* ==== build objects with managed allocation ==== */
+      
+#define _EXCEPTION_SAFE_INVOKE(_CTOR_)                      \
+                                                             \
+          ___assertSupportedType();                       \
+          XX* storage = allocateSlot();                    \
+          try                                                   \
+            {                                                    \
+              return (new(storage) _CTOR_ );                     \
+            }                                                    \
+          catch(...)                                             \
+            {                                                    \
+              releaseSlot(storage);                          \
+              throw;                                             \
+            }
+      
+      template< class XX>
+      XX*                                                                              //_____________________
+      create ()                                                                       ///< invoke default ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX() )
+        }
+      
+      
+      template< class XX, typename P1>
+      XX*                                                                              //___________________
+      create (P1& p1)                                                                 ///< invoke 1-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2)                                                         ///< invoke 2-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              , typename P3
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2, P3& p3)                                                 ///< invoke 3-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              , typename P3
+              , typename P4
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2, P3& p3, P4& p4)                                         ///< invoke 4-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4) )
+        }
+      
+      
+      template< class XX
+              , typename P1
+              , typename P2
+              , typename P3
+              , typename P4
+              , typename P5
+              >
+      XX*                                                                              //___________________
+      create (P1& p1, P2& p2, P3& p3, P4& p4, P5& p5)                                 ///< invoke 5-arg ctor
+        {
+          _EXCEPTION_SAFE_INVOKE ( XX (p1,p2,p3,p4,p5) )
+        }
+      
+#undef _EXCEPTION_SAFE_INVOKE
+      
+      
+      
+      template
+      void
+      destroy (XX* entry)
+        {
+          if (!entry) return;
+          ___assertSupportedType();
+          try
+            {
+              entry->~XX();
+            }
+          catch(...)
+            {
+              lumiera_err errorID = lumiera_error();
+              WARN (common_dbg, "dtor of %s failed: %s", util::tyStr(entry).c_str()
+                                                       , errorID );
+            }
+          releaseSlot (entry);
+        }
+      
+    };
+  
+  
+  
+
+} // namespace lib
+#endif
diff --git a/src/lib/typed-allocation-manager.hpp b/src/lib/typed-allocation-manager.hpp
index 188f120dc..d1a04b19e 100644
--- a/src/lib/typed-allocation-manager.hpp
+++ b/src/lib/typed-allocation-manager.hpp
@@ -63,8 +63,8 @@
 
 
 
-#ifndef CONTROL_TYPED_ALLOCATION_MANAGER_H
-#define CONTROL_TYPED_ALLOCATION_MANAGER_H
+#ifndef LIB_TYPED_ALLOCATION_MANAGER_H
+#define LIB_TYPED_ALLOCATION_MANAGER_H
 
 //#include "pre.hpp"
 #include "lib/error.hpp"
diff --git a/src/proc/control/command-argument-holder.hpp b/src/proc/control/command-argument-holder.hpp
index e71a60a10..d40fd0433 100644
--- a/src/proc/control/command-argument-holder.hpp
+++ b/src/proc/control/command-argument-holder.hpp
@@ -54,7 +54,6 @@
 
 namespace control {
   
-  using lib::TypedAllocationManager;
   using lib::InPlaceBuffer;
   using std::string;
   
diff --git a/src/proc/control/command-impl.hpp b/src/proc/control/command-impl.hpp
index 7d3c18536..a25d92fad 100644
--- a/src/proc/control/command-impl.hpp
+++ b/src/proc/control/command-impl.hpp
@@ -57,7 +57,6 @@
 
 namespace control {
   
-  using lib::TypedAllocationManager;
   using std::tr1::function;
   using std::tr1::shared_ptr;
   
diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index c71359313..ae59f93f6 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -104,8 +104,11 @@ namespace engine {
   class Metadata
     {
     public:
-      struct Entry
+      class Entry
         {
+          
+        
+        public:
           BufferState state()  const;
           HashVal parentKey()  const;
           Entry& mark (BufferState newState);
diff --git a/tests/40components.tests b/tests/40components.tests
index a81b77d3b..9dfaffd74 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -1136,7 +1136,12 @@ return: 0
 END
 
 
-TEST "TypedAllocationManager" TypedAllocationManager_test <
+
+  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 "lib/test/run.hpp"
+#include "lib/simple-allocator.hpp"
+#include "lib/util.hpp"
+
+
+//#include 
+#include 
+#include 
+
+
+namespace lib {
+namespace test{
+  
+  
+  using util::isSameObject;
+//  using std::tr1::shared_ptr;
+  using std::string;
+  using std::rand;
+  
+  
+  
+  namespace { // test data and helpers...
+  
+    long checksum_ = 0;
+    
+    /**
+     * Yet-another ctor/dtor-tracking test dummy object....
+     */
+    template
+    class DummyObj
+      {
+        char crap_[siz];
+        
+      public:
+        DummyObj()
+          {
+            REQUIRE (siz);
+            for (uint i=0; i,DummyObj<23>,string> > TestAllocator;
+  }
+  
+  
+  
+  /***************************************************************************************
+   * @test cover the basic operations of a custom allocator, delegating to mpool.
+   *       The SimpleAllocator doesn't provide any ref-counting or tracking facilities,
+   *       nor does he support bulk de-allocation. The advantage over using std::allocator
+   *       directly is the shortcut for (placement) construction, and -- of course -- the
+   *       ability to exchange the memory model at one central location.
+   * 
+   * @todo as of 9/11 we do heap allocation, but we should use mpool --   see also Ticket #835
+   *        
+   * @see engine::BufferMetadata
+   * @see TypedAllocationManager_test
+   */
+  class SimpleAllocator_test : public Test
+    {
+      
+      virtual void
+      run (Arg) 
+        {
+          CHECK (0 == checksum_);
+          
+          TestAllocator allocator;
+          
+          typedef string *       PS;
+          typedef DummyObj<1> *  PD1;
+          typedef DummyObj<23> * PD23;
+          CHECK (sizeof(DummyObj<1>) != sizeof(DummyObj<23>));
+          
+          PD1  pD11 = allocator.create >();
+          PD1  pD12 = allocator.create >();
+          PD23 pD21 = allocator.create >();
+          PD23 pD22 = allocator.create >();
+          PS   pS11 = allocator.create ("Lumiera");
+          PS   pS12 = allocator.create ("the paradox");
+          
+          CHECK (pD11);
+          CHECK (pD12);
+          CHECK (pD21);
+          CHECK (pD22);
+          CHECK (pS11);
+          CHECK (pS12);
+          CHECK (!isSameObject (*pD11, *pD12));
+          CHECK (!isSameObject (*pD11, *pD21));
+          CHECK (!isSameObject (*pD11, *pD22));
+          CHECK (!isSameObject (*pD11, *pS11));
+          CHECK (!isSameObject (*pD11, *pS12));
+          CHECK (!isSameObject (*pD12, *pD21));
+          CHECK (!isSameObject (*pD12, *pD22));
+          CHECK (!isSameObject (*pD12, *pS11));
+          CHECK (!isSameObject (*pD12, *pS12));
+          CHECK (!isSameObject (*pD21, *pD22));
+          CHECK (!isSameObject (*pD21, *pS11));
+          CHECK (!isSameObject (*pD21, *pS12));
+          CHECK (!isSameObject (*pD22, *pS11));
+          CHECK (!isSameObject (*pD22, *pS12));
+          CHECK (!isSameObject (*pS11, *pS12));
+          
+          CHECK (*pS11 == "Lumiera");
+          CHECK (*pS12 == "the paradox");
+          
+          PD23 pDxx = allocator.create > (*pD21);
+          PS   pSxx = allocator.create        (*pS12);
+          
+          CHECK (*pS12 == *pSxx);
+          CHECK (!isSameObject (*pS12, *pSxx));
+          
+          allocator.destroy (pD11);
+          allocator.destroy (pD12);
+          allocator.destroy (pD21);
+          allocator.destroy (pD22);
+          allocator.destroy (pS11);
+          allocator.destroy (pS12);
+          allocator.destroy (pDxx);
+          allocator.destroy (pSxx);
+          
+          CHECK (0 == checksum_);
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (SimpleAllocator_test, "unit common");
+  
+  
+}} // namespace lib::test

From 057f32e15b67951dd7f41374af3e1b1a771857a9 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 15:40:16 +0200
Subject: [PATCH 075/108] rename the typelist-manipulation header

---
 src/lib/meta/generator-combinations.hpp  |   2 +-
 src/lib/meta/tuple.hpp                   |   2 +-
 src/lib/meta/typelist-manip.hpp          | 366 +++++++++++++++++++++++
 src/lib/meta/typeseq-util.hpp            |   2 +-
 src/lib/variant.hpp                      |   2 +-
 src/proc/control/command-def.hpp         |   2 +-
 src/proc/control/command-signature.hpp   |   5 +-
 src/proc/engine/nodewiring.cpp           |   2 +-
 src/proc/mobject/output-designation.hpp  |   2 +-
 tests/lib/meta/config-flags-test.cpp     |   2 +-
 tests/lib/meta/function-closure-test.cpp |   2 +-
 tests/lib/meta/typelist-manip-test.cpp   |   2 +-
 tests/lib/meta/typeseq-manip-test.cpp    |   2 +-
 13 files changed, 378 insertions(+), 15 deletions(-)
 create mode 100644 src/lib/meta/typelist-manip.hpp

diff --git a/src/lib/meta/generator-combinations.hpp b/src/lib/meta/generator-combinations.hpp
index 28790ec01..3cc3a0946 100644
--- a/src/lib/meta/generator-combinations.hpp
+++ b/src/lib/meta/generator-combinations.hpp
@@ -37,7 +37,7 @@
 #define LUMIERA_META_GENERATOR_COMBINATIONS_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/generator.hpp"
 
 
diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp
index 38d5def29..84559216a 100644
--- a/src/lib/meta/tuple.hpp
+++ b/src/lib/meta/tuple.hpp
@@ -48,7 +48,7 @@
 #define LUMIERA_META_TUPLE_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/typeseq-util.hpp"
 #include "lib/meta/util.hpp"
 
diff --git a/src/lib/meta/typelist-manip.hpp b/src/lib/meta/typelist-manip.hpp
new file mode 100644
index 000000000..428c23f97
--- /dev/null
+++ b/src/lib/meta/typelist-manip.hpp
@@ -0,0 +1,366 @@
+/*
+  TYPELIST-UTIL.hpp  -  Utils for working with lists-of-types
+
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+/** @file typelist-util.hpp
+ ** Metaprogramming: Helpers for manipulating lists-of-types. 
+ ** Sometimes, we use metaprogramming to generate a variation of concrete
+ ** implementations by combining some basic building blocks. Typically, there
+ ** is a number of similar, but not suitably related types involved. We want to
+ ** process those types using a common scheme, without being forced to squeeze
+ ** all those types into a artificial inheritance relationship. Now, generating
+ ** some kind of common factory or adapter, while mixing in pieces of code tailored
+ ** specifically to the individual types, allows still to build a common processing
+ ** in such situations.
+ ** 
+ ** The facilities in this header provide the basics of simple functional list
+ ** processing (mostly with tail recursion). Usually, there is one template parameter
+ ** TYPES, which accepts a \em Type-list. The result of the processing step is then
+ ** accessible as an embedded typedef named \c List . Here, all of the 'processing'
+ ** to calculate this result is performed by the compiler, as a side-effect of
+ ** figuring out the resulting concrete type. At run time, in the generated
+ ** code, typically the resulting classes are empty, maybe just
+ ** exposing a specifically built-up function. 
+ ** 
+ ** @see generator.hpp
+ ** @see typelist-manip-test.cpp
+ ** @see TimeControl_test usage example
+ ** @see typelist.hpp
+ ** 
+ */
+
+
+#ifndef LUMIERA_META_TYPELIST_UTIL_H
+#define LUMIERA_META_TYPELIST_UTIL_H
+
+
+  
+#include "lib/meta/typelist.hpp"
+
+namespace lumiera {
+namespace typelist{
+    
+    
+    /**
+     * Metafunction counting the number of Types in the collection
+     * @return an embedded constant \c value holding the result
+     */
+    template
+    struct count;
+    template<>
+    struct count
+      {
+        enum{ value = 0 };
+      };
+    template
+    struct count >
+      {
+        enum{ value = 1 + count::value };
+      };
+    
+    /**
+     * Metafunction " max( sizeof(T) ) for T in TYPES "
+     */
+    template
+    struct maxSize;
+    template<>
+    struct maxSize
+      {
+        enum{ value = 0 };
+      };
+    template
+    struct maxSize >
+      {
+        enum{ thisval = sizeof(TY)
+            , nextval = maxSize::value
+            , value   = nextval > thisval?  nextval:thisval
+            };
+      };
+    
+    
+    /** pick the n-th element from a typelist */
+    template
+    struct Pick
+      {
+        typedef NullType Type;  
+      };
+    template
+    struct Pick, 0>
+      {
+        typedef TY Type;  
+      };
+    template
+    struct Pick, i>
+      {
+        typedef typename Pick::Type Type;  
+      };
+    
+    
+    
+    
+    /** apply a transformation (template) to each type in the list */
+    template class _TRANS_>
+    struct Apply                           { typedef TY List; };
+    
+    template< class TY, class TYPES
+            , template class _TRANS_
+            >
+    struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type
+                                                         , typename Apply::List
+                                                         > List; };
+    
+    
+    /** conditional node: skip an element based on evaluating a predicate */
+    template
+    struct CondNode                        { typedef TAIL  Next; };
+    
+    template
+    struct CondNode         { typedef Node  Next; };
+    
+    /** filter away those types which don't fulfil a predicate metafunction */
+    template< class TYPES 
+            , template class _P_    ///< a template providing a boolean member \c ::value
+            >
+    struct Filter;
+    
+    template class _P_>
+    struct Filter            { typedef NullType  List; };
+    
+    template< class TY, class TYPES
+            , template class _P_
+            >
+    struct Filter,_P_>      { typedef typename CondNode< _P_::value
+                                                                      , TY
+                                                                      , typename Filter::List
+                                                                      >::Next      
+                                                                      List; };
+    
+    
+    /** append lists-of-types */
+    template
+    struct Append                          { typedef Node::List>  List; };
+    
+    template< class TY, class TYPES
+            , class TAIL
+            >
+    struct Append, TAIL>    { typedef Node::List>  List; };
+    
+    template
+    struct Append >   { typedef Node   List; };
+    
+    template
+    struct Append, NullType>    { typedef Node   List; };
+    
+    template
+    struct Append            { typedef Node   List; };
+    
+    template
+    struct Append            { typedef Node   List; };
+    
+    template<>
+    struct Append       { typedef NullType             List; };
+    
+    
+    
+    
+    /** access the last list element */
+    template
+    struct SplitLast;
+    
+    template<>
+    struct SplitLast             { typedef NullType Type;
+                                             typedef NullType List; };
+    template
+    struct SplitLast >   { typedef TY       Type;
+                                             typedef NullType List; };
+    
+    template
+    struct SplitLast >      { typedef typename SplitLast::Type Type;
+                                             typedef typename Append< TY,
+                                                                      typename SplitLast::List
+                                                                    >::List 
+                                                                    List; };
+    
+    
+    
+    /** 
+     * splice a typelist like an overlay
+     * into an base typelist, starting at given index.
+     * @return either the combined (spliced) List, or
+     *         the Front/Back part before or after the Overlay
+     * @note using a NullType as OVERLAY allows to extract
+     *         an arbitrary Front/Back part of the list
+     */
+    template
+    struct Splice;
+    
+    template
+    struct Splice, OVERLAY, i>  { typedef Node::List>  List;
+                                             typedef Node::Front> Front;
+                                             typedef         typename Splice::Back   Back; };
+    
+    template
+    struct Splice,Node,0> { typedef Node::List>          List;
+                                             typedef NullType                                          Front;
+                                             typedef         typename Splice::Back           Back; };
+    
+    template
+    struct Splice, NullType, 0> { typedef Node List;
+                                             typedef NullType    Front;
+                                             typedef Node Back; };
+    
+    template
+    struct Splice         { typedef NullType    List;
+                                             typedef NullType    Front;
+                                             typedef NullType    Back; };
+    
+    
+    
+    
+    /**
+     * Allows to access various parts of a given typelist:
+     * Start and End, Prefix and Tail..
+     */
+    template
+    struct Dissect;
+    
+    template
+    struct Dissect >
+      {
+        typedef Node                  List;  ///< the complete list
+        typedef T                              Head;  ///< first element
+        typedef Node               First; ///< a list containing the first element
+        typedef TYPES                          Tail;  ///< remainder of the list starting with the second elm.
+        typedef typename SplitLast::List Prefix;///< all of the list, up to but excluding the last element
+        typedef typename SplitLast::Type End;   ///< the last element
+        typedef Node             Last;  ///< a list containing the last element
+      };
+    
+    template<>
+    struct Dissect
+      {
+        typedef NullType                       List;
+        typedef NullType                       Head;
+        typedef NullType                       First;
+        typedef NullType                       Tail;
+        typedef NullType                       Prefix;
+        typedef NullType                       End;
+        typedef NullType                       Last;
+      };
+    
+    
+    
+    
+    /** 
+     * prefix each of the elements,
+     * yielding a list-of lists-of-types
+     */
+    template
+    struct PrefixAll                       { typedef Node< typename Append::List, NullType>  List; };
+    
+    template
+    struct PrefixAll          { typedef NullType  List; };
+    
+    template
+    struct PrefixAll          { typedef Node< typename Append::List, NullType>  List; };
+    
+    template< class T
+            , class TY, class TYPES
+            >
+    struct PrefixAll >   { typedef Node< typename Append::List
+                                                         , typename PrefixAll::List
+                                                         >     List; };
+    
+    
+    
+    
+    /**
+     * build a list-of lists, where each element of the first arg list
+     * gets in turn prepended to all elements of the second arg list.
+     * Can be used to build all possible combinations from two
+     * sources, i.e. the Cartesian product.
+     */
+    template
+    struct Distribute                      { typedef typename PrefixAll::List  List; };
+    
+    template
+    struct Distribute         { typedef NullType List; };
+    
+    template< class TY, class TYPES
+            , class TAIL
+            >
+    struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List
+                                                                    , typename Distribute::List
+                                                                    >::List                    
+                                                                    List; };
+    
+    
+    
+    /** 
+     * build all possible combinations, based on a enumeration of the basic cases.
+     * For each of the types in the argument list, an "enumeration generator" template is invoked,
+     * yielding a list of the possible base cases. These base cases are then combined with all the
+     * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
+     * means that the base cases of the n-th element will appear in the n-th position of the
+     * resulting lists,
+     * 
+     * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
+     * list-of-lists, featuring all possibilities to combine these distinct toggles. 
+     */
+    template< class X
+            , template class _ENUM_>
+    struct Combine                         { typedef typename Distribute< typename _ENUM_::List
+                                                                        , Node
+                                                                        >::List  List; };
+    template< template class _ENUM_>
+    struct Combine      { typedef NodeNull                    List; };
+    
+    template< class TY, class TYPES
+            , template class _ENUM_>
+    struct Combine,_ENUM_>  { typedef typename Distribute< typename _ENUM_::List
+                                                                        , typename Combine::List
+                                                                        >::List  List; };
+    
+    /** enumeration generator for the Combine metafunction,
+     *  yielding an "on" and "off" case
+     */
+    template
+    struct FlagOnOff
+      { 
+        typedef Node >  List;
+      };
+    
+    
+    /** generate all possible on-off combinations of the given flags */
+    template
+    struct CombineFlags
+      { 
+        typedef typename Combine::List  List; 
+      };
+    
+    
+    
+    
+}} // namespace lumiera::typelist
+#endif
diff --git a/src/lib/meta/typeseq-util.hpp b/src/lib/meta/typeseq-util.hpp
index 398d09d53..07ecbffa6 100644
--- a/src/lib/meta/typeseq-util.hpp
+++ b/src/lib/meta/typeseq-util.hpp
@@ -45,7 +45,7 @@
 #define LUMIERA_META_TYPESEQ_UTIL_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/util.hpp"
 
 
diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp
index b4eef9b76..7b30ba63c 100644
--- a/src/lib/variant.hpp
+++ b/src/lib/variant.hpp
@@ -45,7 +45,7 @@
 #define LIB_VARIANT_H
 
 
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/generator.hpp"
 
 #include 
diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp
index b3e777c4c..07490ce0b 100644
--- a/src/proc/control/command-def.hpp
+++ b/src/proc/control/command-def.hpp
@@ -67,7 +67,7 @@
 #include "lib/bool-checkable.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/tuple.hpp"
 #include "lib/util.hpp"
 
diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp
index 3c9df666f..52e4dcf19 100644
--- a/src/proc/control/command-signature.hpp
+++ b/src/proc/control/command-signature.hpp
@@ -47,14 +47,11 @@
 #define CONTROL_COMMAND_SIGNATURE_H
 
 //#include "pre.hpp"
-//#include "lib/symbol.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/typeseq-util.hpp"
-//#include "lib/meta/tuple.hpp"
 
-//#include 
 #include 
 
 
diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp
index f1e989816..9be5dd541 100644
--- a/src/proc/engine/nodewiring.cpp
+++ b/src/proc/engine/nodewiring.cpp
@@ -26,7 +26,7 @@
 #include "proc/engine/nodeoperation.hpp"
 #include "proc/engine/nodewiring-config.hpp"
 
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 
 
 namespace engine {
diff --git a/src/proc/mobject/output-designation.hpp b/src/proc/mobject/output-designation.hpp
index 93f98f518..bfd47b8a5 100644
--- a/src/proc/mobject/output-designation.hpp
+++ b/src/proc/mobject/output-designation.hpp
@@ -26,7 +26,7 @@
 
 #include "proc/asset/pipe.hpp"
 #include "lib/opaque-holder.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 
 extern "C" {
 #include "lib/luid.h"
diff --git a/tests/lib/meta/config-flags-test.cpp b/tests/lib/meta/config-flags-test.cpp
index 57a5621d1..757f84d39 100644
--- a/tests/lib/meta/config-flags-test.cpp
+++ b/tests/lib/meta/config-flags-test.cpp
@@ -40,7 +40,7 @@
 #include "lib/test/run.hpp"
 #include "lib/meta/util.hpp"
 #include "lib/meta/generator.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/configflags.hpp"
 #include "meta/typelist-diagnostics.hpp"
 #include "proc/engine/nodewiring-config.hpp"
diff --git a/tests/lib/meta/function-closure-test.cpp b/tests/lib/meta/function-closure-test.cpp
index e024ff808..4d2eb2a06 100644
--- a/tests/lib/meta/function-closure-test.cpp
+++ b/tests/lib/meta/function-closure-test.cpp
@@ -38,7 +38,7 @@
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "lib/meta/function.hpp"
 #include "lib/meta/function-closure.hpp"
 #include "meta/typelist-diagnostics.hpp"
diff --git a/tests/lib/meta/typelist-manip-test.cpp b/tests/lib/meta/typelist-manip-test.cpp
index a14daef90..57f88d1dd 100644
--- a/tests/lib/meta/typelist-manip-test.cpp
+++ b/tests/lib/meta/typelist-manip-test.cpp
@@ -40,7 +40,7 @@
 
 #include "lib/test/run.hpp"
 #include "lib/meta/generator.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "meta/typelist-diagnostics.hpp"
 
 #include 
diff --git a/tests/lib/meta/typeseq-manip-test.cpp b/tests/lib/meta/typeseq-manip-test.cpp
index 6efffd272..3445d2217 100644
--- a/tests/lib/meta/typeseq-manip-test.cpp
+++ b/tests/lib/meta/typeseq-manip-test.cpp
@@ -39,7 +39,7 @@
 
 #include "lib/test/run.hpp"
 #include "lib/meta/typeseq-util.hpp"
-#include "lib/meta/typelist-util.hpp"
+#include "lib/meta/typelist-manip.hpp"
 #include "meta/typelist-diagnostics.hpp"
 
 #include 

From 5350ef6dbe7512ca85473a9fac2181d0b8b373c5 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 17:06:48 +0200
Subject: [PATCH 076/108] split off and test-cover simple typelist utils

these simple utils (length of typelist, maximum size
containment test) are mostly not used in conjunction
with the more elaborate typelist manipulatino utils.
Moreover, we lacked a dedicated unit test
---
 src/lib/meta/tuple.hpp                 |   2 +-
 src/lib/meta/typelist-manip.hpp        |  55 +----
 src/lib/meta/typelist-util.hpp         | 307 +++----------------------
 src/lib/meta/typelist.hpp              |  20 +-
 src/lib/simple-allocator.hpp           |  29 +--
 src/lib/variant.hpp                    |   2 +-
 src/proc/control/command-signature.hpp |  23 +-
 tests/40components.tests               |   5 +
 tests/lib/meta/typelist-util-test.cpp  |  83 +++++++
 9 files changed, 166 insertions(+), 360 deletions(-)
 create mode 100644 tests/lib/meta/typelist-util-test.cpp

diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp
index 84559216a..38d5def29 100644
--- a/src/lib/meta/tuple.hpp
+++ b/src/lib/meta/tuple.hpp
@@ -48,7 +48,7 @@
 #define LUMIERA_META_TUPLE_H
 
 #include "lib/meta/typelist.hpp"
-#include "lib/meta/typelist-manip.hpp"
+#include "lib/meta/typelist-util.hpp"
 #include "lib/meta/typeseq-util.hpp"
 #include "lib/meta/util.hpp"
 
diff --git a/src/lib/meta/typelist-manip.hpp b/src/lib/meta/typelist-manip.hpp
index 428c23f97..b0d789a76 100644
--- a/src/lib/meta/typelist-manip.hpp
+++ b/src/lib/meta/typelist-manip.hpp
@@ -1,5 +1,5 @@
 /*
-  TYPELIST-UTIL.hpp  -  Utils for working with lists-of-types
+  TYPELIST-MANIP.hpp  -  Utils for working with lists-of-types
 
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -21,8 +21,8 @@
 */
 
 
-/** @file typelist-util.hpp
- ** Metaprogramming: Helpers for manipulating lists-of-types. 
+/** @file typelist-manip.hpp
+ ** Metaprogramming: Helpers for manipulating lists-of-types.
  ** Sometimes, we use metaprogramming to generate a variation of concrete
  ** implementations by combining some basic building blocks. Typically, there
  ** is a number of similar, but not suitably related types involved. We want to
@@ -49,54 +49,17 @@
  */
 
 
-#ifndef LUMIERA_META_TYPELIST_UTIL_H
-#define LUMIERA_META_TYPELIST_UTIL_H
+#ifndef LUMIERA_META_TYPELIST_MANIP_H
+#define LUMIERA_META_TYPELIST_MANIP_H
+
 
 
-  
 #include "lib/meta/typelist.hpp"
 
 namespace lumiera {
 namespace typelist{
     
     
-    /**
-     * Metafunction counting the number of Types in the collection
-     * @return an embedded constant \c value holding the result
-     */
-    template
-    struct count;
-    template<>
-    struct count
-      {
-        enum{ value = 0 };
-      };
-    template
-    struct count >
-      {
-        enum{ value = 1 + count::value };
-      };
-    
-    /**
-     * Metafunction " max( sizeof(T) ) for T in TYPES "
-     */
-    template
-    struct maxSize;
-    template<>
-    struct maxSize
-      {
-        enum{ value = 0 };
-      };
-    template
-    struct maxSize >
-      {
-        enum{ thisval = sizeof(TY)
-            , nextval = maxSize::value
-            , value   = nextval > thisval?  nextval:thisval
-            };
-      };
-    
-    
     /** pick the n-th element from a typelist */
     template
     struct Pick
@@ -111,7 +74,7 @@ namespace typelist{
     template
     struct Pick, i>
       {
-        typedef typename Pick::Type Type;  
+        typedef typename Pick::Type Type;
       };
     
     
@@ -240,7 +203,7 @@ namespace typelist{
     
     /**
      * Allows to access various parts of a given typelist:
-     * Start and End, Prefix and Tail..
+     * Start and End, Prefix and Tail...
      */
     template
     struct Dissect;
@@ -356,7 +319,7 @@ namespace typelist{
     template
     struct CombineFlags
       { 
-        typedef typename Combine::List  List; 
+        typedef typename Combine::List  List;
       };
     
     
diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp
index 428c23f97..c699855ad 100644
--- a/src/lib/meta/typelist-util.hpp
+++ b/src/lib/meta/typelist-util.hpp
@@ -1,5 +1,5 @@
 /*
-  TYPELIST-UTIL.hpp  -  Utils for working with lists-of-types
+  TYPELIST-UTIL.hpp  -  simple helpers for working with lists-of-types
 
   Copyright (C)         Lumiera.org
     2008,               Hermann Vosseler 
@@ -22,28 +22,21 @@
 
 
 /** @file typelist-util.hpp
- ** Metaprogramming: Helpers for manipulating lists-of-types. 
- ** Sometimes, we use metaprogramming to generate a variation of concrete
- ** implementations by combining some basic building blocks. Typically, there
- ** is a number of similar, but not suitably related types involved. We want to
- ** process those types using a common scheme, without being forced to squeeze
- ** all those types into a artificial inheritance relationship. Now, generating
- ** some kind of common factory or adapter, while mixing in pieces of code tailored
- ** specifically to the individual types, allows still to build a common processing
- ** in such situations.
+ ** Metaprogramming: simple helpers for working with lists-of-types. 
+ ** This header provides some very basic "meta functions" for extracting
+ ** some informations from a list-of-types. In Lumiera, we use template
+ ** metaprogramming and especially such lists-of-types, whenever we build
+ ** some common implementation backbone, without being able to subsume all
+ ** participating types (classes) into a single inheritance hierarchy.
  ** 
- ** The facilities in this header provide the basics of simple functional list
- ** processing (mostly with tail recursion). Usually, there is one template parameter
- ** TYPES, which accepts a \em Type-list. The result of the processing step is then
- ** accessible as an embedded typedef named \c List . Here, all of the 'processing'
- ** to calculate this result is performed by the compiler, as a side-effect of
- ** figuring out the resulting concrete type. At run time, in the generated
- ** code, typically the resulting classes are empty, maybe just
- ** exposing a specifically built-up function. 
+ ** The "meta functions" defined here are templates; to access the "result" of
+ ** such a meta function, we instantiate the template and then access one of the
+ ** embedded constant definitions (usually the enum constant named \c value)
  ** 
  ** @see generator.hpp
- ** @see typelist-manip-test.cpp
- ** @see TimeControl_test usage example
+ ** @see TypelistUtil_test
+ ** @see lib::SimpleAllocator usage example (for isInList)
+ ** @see TypelistManip_test
  ** @see typelist.hpp
  ** 
  */
@@ -53,7 +46,7 @@
 #define LUMIERA_META_TYPELIST_UTIL_H
 
 
-  
+
 #include "lib/meta/typelist.hpp"
 
 namespace lumiera {
@@ -77,6 +70,7 @@ namespace typelist{
         enum{ value = 1 + count::value };
       };
     
+    
     /**
      * Metafunction " max( sizeof(T) ) for T in TYPES "
      */
@@ -97,268 +91,35 @@ namespace typelist{
       };
     
     
-    /** pick the n-th element from a typelist */
-    template
-    struct Pick
-      {
-        typedef NullType Type;  
-      };
-    template
-    struct Pick, 0>
-      {
-        typedef TY Type;  
-      };
-    template
-    struct Pick, i>
-      {
-        typedef typename Pick::Type Type;  
-      };
-    
-    
-    
-    
-    /** apply a transformation (template) to each type in the list */
-    template class _TRANS_>
-    struct Apply                           { typedef TY List; };
-    
-    template< class TY, class TYPES
-            , template class _TRANS_
-            >
-    struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type
-                                                         , typename Apply::List
-                                                         > List; };
-    
-    
-    /** conditional node: skip an element based on evaluating a predicate */
-    template
-    struct CondNode                        { typedef TAIL  Next; };
-    
-    template
-    struct CondNode         { typedef Node  Next; };
-    
-    /** filter away those types which don't fulfil a predicate metafunction */
-    template< class TYPES 
-            , template class _P_    ///< a template providing a boolean member \c ::value
-            >
-    struct Filter;
-    
-    template class _P_>
-    struct Filter            { typedef NullType  List; };
-    
-    template< class TY, class TYPES
-            , template class _P_
-            >
-    struct Filter,_P_>      { typedef typename CondNode< _P_::value
-                                                                      , TY
-                                                                      , typename Filter::List
-                                                                      >::Next      
-                                                                      List; };
-    
-    
-    /** append lists-of-types */
-    template
-    struct Append                          { typedef Node::List>  List; };
-    
-    template< class TY, class TYPES
-            , class TAIL
-            >
-    struct Append, TAIL>    { typedef Node::List>  List; };
-    
-    template
-    struct Append >   { typedef Node   List; };
-    
-    template
-    struct Append, NullType>    { typedef Node   List; };
-    
-    template
-    struct Append            { typedef Node   List; };
-    
-    template
-    struct Append            { typedef Node   List; };
-    
-    template<>
-    struct Append       { typedef NullType             List; };
-    
-    
-    
-    
-    /** access the last list element */
-    template
-    struct SplitLast;
-    
-    template<>
-    struct SplitLast             { typedef NullType Type;
-                                             typedef NullType List; };
-    template
-    struct SplitLast >   { typedef TY       Type;
-                                             typedef NullType List; };
-    
-    template
-    struct SplitLast >      { typedef typename SplitLast::Type Type;
-                                             typedef typename Append< TY,
-                                                                      typename SplitLast::List
-                                                                    >::List 
-                                                                    List; };
-    
-    
-    
-    /** 
-     * splice a typelist like an overlay
-     * into an base typelist, starting at given index.
-     * @return either the combined (spliced) List, or
-     *         the Front/Back part before or after the Overlay
-     * @note using a NullType as OVERLAY allows to extract
-     *         an arbitrary Front/Back part of the list
-     */
-    template
-    struct Splice;
-    
-    template
-    struct Splice, OVERLAY, i>  { typedef Node::List>  List;
-                                             typedef Node::Front> Front;
-                                             typedef         typename Splice::Back   Back; };
-    
-    template
-    struct Splice,Node,0> { typedef Node::List>          List;
-                                             typedef NullType                                          Front;
-                                             typedef         typename Splice::Back           Back; };
-    
-    template
-    struct Splice, NullType, 0> { typedef Node List;
-                                             typedef NullType    Front;
-                                             typedef Node Back; };
-    
-    template
-    struct Splice         { typedef NullType    List;
-                                             typedef NullType    Front;
-                                             typedef NullType    Back; };
-    
-    
-    
-    
     /**
-     * Allows to access various parts of a given typelist:
-     * Start and End, Prefix and Tail..
+     * Metafunction to check if a specific type is contained
+     * in a given typelist. Only exact match is detected.
      */
-    template
-    struct Dissect;
-    
-    template
-    struct Dissect >
+    template
+    struct IsInList
       {
-        typedef Node                  List;  ///< the complete list
-        typedef T                              Head;  ///< first element
-        typedef Node               First; ///< a list containing the first element
-        typedef TYPES                          Tail;  ///< remainder of the list starting with the second elm.
-        typedef typename SplitLast::List Prefix;///< all of the list, up to but excluding the last element
-        typedef typename SplitLast::Type End;   ///< the last element
-        typedef Node             Last;  ///< a list containing the last element
+        enum{ value = false };
       };
     
-    template<>
-    struct Dissect
+    template
+    struct IsInList >
       {
-        typedef NullType                       List;
-        typedef NullType                       Head;
-        typedef NullType                       First;
-        typedef NullType                       Tail;
-        typedef NullType                       Prefix;
-        typedef NullType                       End;
-        typedef NullType                       Last;
+        enum{ value = true };
       };
     
-    
-    
-    
-    /** 
-     * prefix each of the elements,
-     * yielding a list-of lists-of-types
-     */
-    template
-    struct PrefixAll                       { typedef Node< typename Append::List, NullType>  List; };
-    
-    template
-    struct PrefixAll          { typedef NullType  List; };
-    
-    template
-    struct PrefixAll          { typedef Node< typename Append::List, NullType>  List; };
-    
-    template< class T
-            , class TY, class TYPES
-            >
-    struct PrefixAll >   { typedef Node< typename Append::List
-                                                         , typename PrefixAll::List
-                                                         >     List; };
-    
-    
-    
-    
-    /**
-     * build a list-of lists, where each element of the first arg list
-     * gets in turn prepended to all elements of the second arg list.
-     * Can be used to build all possible combinations from two
-     * sources, i.e. the Cartesian product.
-     */
-    template
-    struct Distribute                      { typedef typename PrefixAll::List  List; };
-    
-    template
-    struct Distribute         { typedef NullType List; };
-    
-    template< class TY, class TYPES
-            , class TAIL
-            >
-    struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List
-                                                                    , typename Distribute::List
-                                                                    >::List                    
-                                                                    List; };
-    
-    
-    
-    /** 
-     * build all possible combinations, based on a enumeration of the basic cases.
-     * For each of the types in the argument list, an "enumeration generator" template is invoked,
-     * yielding a list of the possible base cases. These base cases are then combined with all the
-     * combinations of the rest, yielding all ordered combinations of all cases. Here, "ordered"
-     * means that the base cases of the n-th element will appear in the n-th position of the
-     * resulting lists,
-     * 
-     * For the typical example, the "base cases" are {flag(on), flag(off)}, so we get a
-     * list-of-lists, featuring all possibilities to combine these distinct toggles. 
-     */
-    template< class X
-            , template class _ENUM_>
-    struct Combine                         { typedef typename Distribute< typename _ENUM_::List
-                                                                        , Node
-                                                                        >::List  List; };
-    template< template class _ENUM_>
-    struct Combine      { typedef NodeNull                    List; };
-    
-    template< class TY, class TYPES
-            , template class _ENUM_>
-    struct Combine,_ENUM_>  { typedef typename Distribute< typename _ENUM_::List
-                                                                        , typename Combine::List
-                                                                        >::List  List; };
-    
-    /** enumeration generator for the Combine metafunction,
-     *  yielding an "on" and "off" case
-     */
-    template
-    struct FlagOnOff
-      { 
-        typedef Node >  List;
-      };
-    
-    
-    /** generate all possible on-off combinations of the given flags */
-    template
-    struct CombineFlags
-      { 
-        typedef typename Combine::List  List; 
+    template
+    struct IsInList >
+      {
+        enum{ value = IsInList::value };
       };
     
+    /** convenience shortcut: query function */
+    template
+    bool
+    isInList()
+    {
+      return IsInList::value;
+    }
     
     
     
diff --git a/src/lib/meta/typelist.hpp b/src/lib/meta/typelist.hpp
index 5d0e11bc0..3de24176f 100644
--- a/src/lib/meta/typelist.hpp
+++ b/src/lib/meta/typelist.hpp
@@ -40,15 +40,25 @@ This code is heavily inspired by
 
 /** @file typelist.hpp
  ** A template metaprogramming technique for manipulating collections of types.
- ** Effectively this is a taylored and simplified version of what can be found in the Loki library.
+ ** Effectively this is a tailored and simplified version of what can be found in the Loki library.
  ** We use it in other generic library-style code to generate repetitive code. If you tend to find
  ** template metaprogramming (or functional programming in general) offending, please ignore the 
- ** technical details and just consider the benefit of such an simplification for the user code.
- **
- ** Interface for using this facility is the template Types(.....) for up to 20 Type parameters
+ ** technical details and just consider the benefit of such an simplification for the client code.
  **
+ ** Interface for using this facility is the template Types(.....) for up to 20 Type parameters.
+ ** To start typelist processing, other templates typically pick up the Types<...>::List type.
+ ** This allows for LISP-style list processing, with a pattern match on either Node
+ ** or NullType to terminate recursion. In C++ template metaprogramming, "pattern match"
+ ** is done by partial template specialisations (the compiler will pick up and thus
+ ** match the template parameters). A typedef acts like a declaration in normal
+ ** programming. Because such a "declaration" can't be changed after the fact,
+ ** effectively this is a flavour of functional programming. Just the
+ ** "execution environment" is the compiler, during compilation.
+ ** 
  ** @see lumiera::visitor::Applicable usage example
- ** @see typelisttest.cpp
+ ** @see control::CommandSignature more elaborate usage example (dissecting a functor signature)
+ ** @see TypeList_test
+ ** @see TypeListManip_test
  ** 
  */
 
diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp
index 71b0438fa..0cbf6b5c9 100644
--- a/src/lib/simple-allocator.hpp
+++ b/src/lib/simple-allocator.hpp
@@ -49,6 +49,7 @@
 //#include "pre.hpp"
 #include "lib/error.hpp"
 #include "lib/meta/generator.hpp"
+#include "lib/meta/typelist-util.hpp"
 #include "lib/format.hpp"
 //#include "lib/typed-counter.hpp"
 #include "include/logging.h"
@@ -63,31 +64,11 @@
 namespace lib {
   
 //  using std::tr1::shared_ptr;
-  using lumiera::typelist::InstantiateForEach;
   using lumiera::typelist::Types;
+  using lumiera::typelist::IsInList;
+  using lumiera::typelist::InstantiateForEach;
   
   namespace {
-    using lumiera::typelist::Node;
-    using lumiera::typelist::NullType;
-    
-    template
-    struct IsInList
-      {
-        enum{ value = false };
-      };
-    
-    template
-    struct IsInList, TY>
-      {
-        enum{ value = true };
-      };
-    
-    template
-    struct IsInList, TY>
-      {
-        enum{ value = IsInList::value };
-      };
-    
   }
   
   
@@ -141,9 +122,9 @@ namespace lib {
       ___assertSupportedType()
         {
           typedef typename TYPES::List PreconfiguredTypes;
-          typedef IsInList IsPreconfiguredType;
+          typedef IsInList IsSupportedType;
           
-          BOOST_STATIC_ASSERT (IsPreconfiguredType::value);
+          BOOST_STATIC_ASSERT (IsSupportedType::value);
         }
      
         
diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp
index 7b30ba63c..b4eef9b76 100644
--- a/src/lib/variant.hpp
+++ b/src/lib/variant.hpp
@@ -45,7 +45,7 @@
 #define LIB_VARIANT_H
 
 
-#include "lib/meta/typelist-manip.hpp"
+#include "lib/meta/typelist-util.hpp"
 #include "lib/meta/generator.hpp"
 
 #include 
diff --git a/src/proc/control/command-signature.hpp b/src/proc/control/command-signature.hpp
index 52e4dcf19..44354c300 100644
--- a/src/proc/control/command-signature.hpp
+++ b/src/proc/control/command-signature.hpp
@@ -30,7 +30,7 @@
  ** we need to accept functor objects with a very specific and predetermined
  ** signature, thus allowing for strict type checking by the compiler.
  ** 
- ** \par Relation of function signatures
+ ** \par Relation of function signatures (MEM = type of the "memento" for Undo)
  ** - operation: void(P1,..PN)
  ** - captureUndo: MEM(P1,..PN)
  ** - undoOperation void(P1,..PN,MEM)
@@ -59,24 +59,20 @@
 
 namespace control {
   
-//  using lib::Symbol;
-//  using std::tr1::shared_ptr;
   using std::tr1::function;
   
   using lumiera::typelist::FunctionSignature;
   using lumiera::typelist::FunctionTypedef;
   using lumiera::typelist::Types;
-//using lumiera::typelist::NullType;
-//using lumiera::typelist::Tuple;
   using lumiera::typelist::Append;
   using lumiera::typelist::SplitLast;
   
-
+  
   /** 
-   * Metaprogramming helper for building Command function signatures. 
+   * Metaprogramming helper for building Command function signatures.
    * The complete definition context of any command is templated to the signature
    * of the actual command operation and to the memento type. The typedefs embedded
-   * within CommandSignature allows accepting suitable typed functions
+   * within CommandSignature allows for accepting suitable typed functions
    * to implement the command in question.
    */
   template
@@ -90,7 +86,7 @@ namespace control {
       
     public:
       typedef typename FunctionTypedef::Sig          OperateSig;
-      typedef typename FunctionTypedef::Sig           CaptureSig;
+      typedef typename FunctionTypedef::Sig          CaptureSig;
       typedef typename FunctionTypedef::Sig  UndoOp_Sig;
       typedef Args                                               CmdArgs;
       typedef MEM                                                Memento;
@@ -100,16 +96,23 @@ namespace control {
   
   
   /** 
-   * Type analysis helper template. 
+   * Type analysis helper template.
    * Used for dissecting a given type signature to derive
    * the related basic operation signature, the signature of a possible Undo-function
    * and the signature necessary for capturing undo information. The implementation
    * relies on re-binding an embedded type defining template, based on the actual
    * case, as identified by the structure of the given parameter signature.
+   * 
+   * To use this template, it is instantiated with the signature of a functor object
+   * in question. Depending on the actual situation, the compiler will then either
+   * pick Case1 or Case2 -- thus allowing the client in any case to pick up the
+   * correct signatures for Operation, Capture and Undo-function from the
+   * public typedefs within \c UndoSignature
    */
   template
   class UndoSignature
     {
+      // preparation:  dissect the function signature into arguments and result
       typedef typename FunctionSignature< function >::Args Args;
       typedef typename FunctionSignature< function >::Ret  Ret;
       
diff --git a/tests/40components.tests b/tests/40components.tests
index 9dfaffd74..d0f849145 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -801,6 +801,11 @@ return: 0
 END
 
 
+TEST "simple typelist utils" TypeListUtil_test < >
 out: ctor DoIt >
diff --git a/tests/lib/meta/typelist-util-test.cpp b/tests/lib/meta/typelist-util-test.cpp
new file mode 100644
index 000000000..22a8c62e8
--- /dev/null
+++ b/tests/lib/meta/typelist-util-test.cpp
@@ -0,0 +1,83 @@
+/*
+  TypeListUtil(Test)  -  check the simple typelist metaprogramming helpers 
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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 "lib/test/run.hpp"
+#include "lib/meta/typelist-util.hpp"
+
+
+
+namespace lumiera {
+namespace typelist{
+namespace test {
+  
+  
+  
+  typedef Types< int
+               , uint
+               , int64_t
+               , uint64_t
+               >::List     TheList;
+  
+  typedef Types<  >::List  EmptyList;
+  
+  
+  
+  /*************************************************************************
+   * @test verify the simple helpers for working with lists-of-types.
+   *       These are simple metafunctions to count the number of elements,
+   *       calculate the maximum size or check for inclusion.
+   *       
+   *       Because these metafunctions will be computed during compilation,
+   *       mostly the function is already verified when it passes compilation.
+   *       All we can do here, at runtime, is to verify some of the (expected)
+   *       constant results.
+   */
+  class TypeListUtil_test : public Test
+    {
+      void
+      run (Arg) 
+        {
+          CHECK (4 == count::value);
+          CHECK (0 == count::value);
+          
+          CHECK (sizeof(int64_t) == maxSize::value);
+          CHECK (0               == maxSize::value);
+          
+          CHECK ( bool(IsInList::value));
+          CHECK ( bool(IsInList::value));
+          CHECK ( bool(IsInList::value));
+          CHECK ( bool(IsInList::value));
+          
+          CHECK (!bool(IsInList::value));
+          CHECK (!bool(IsInList::value));
+          CHECK (!bool(IsInList::value));         // Note: not-a-typelist yields false too
+        } 
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (TypeListUtil_test, "unit common");
+  
+  
+  
+}}} // namespace lumiera::typelist::test

From f505c46d1d83c49fa525d97d414a756a578c0f25 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 Sep 2011 19:23:45 +0200
Subject: [PATCH 077/108] finish simple allocator frontend. Unit-test pass

---
 src/lib/simple-allocator.hpp        | 87 ++++++++++++++++++++++++-----
 tests/40components.tests            |  2 +-
 tests/lib/simple-allocator-test.cpp | 16 ++++--
 3 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/src/lib/simple-allocator.hpp b/src/lib/simple-allocator.hpp
index 0cbf6b5c9..b79b2081d 100644
--- a/src/lib/simple-allocator.hpp
+++ b/src/lib/simple-allocator.hpp
@@ -51,11 +51,9 @@
 #include "lib/meta/generator.hpp"
 #include "lib/meta/typelist-util.hpp"
 #include "lib/format.hpp"
-//#include "lib/typed-counter.hpp"
+#include "lib/typed-counter.hpp"
 #include "include/logging.h"
 
-
-//#include 
 #include 
 #include 
 
@@ -63,24 +61,71 @@
 
 namespace lib {
   
-//  using std::tr1::shared_ptr;
   using lumiera::typelist::Types;
   using lumiera::typelist::IsInList;
   using lumiera::typelist::InstantiateForEach;
   
-  namespace {
-  }
   
   
+  /* === Policies for simple custom allocator  === */
+  
+  /**
+   * Policy: use just plain heap allocations
+   */
   template
   class CustomAllocator
     : public std::allocator
+    { };
+  
+  
+  /**
+   * Policy: maintain explicit per type instance count
+   * @note this imposes additional locking
+   */
+  struct UseInstantiationCounting
     {
+      template
+      size_t
+      allocationCount()  const
+        {
+          return allocCnt_.get();
+        }
       
+      template
+      void
+      incrementCount()
+        {
+          allocCnt_.inc();
+        }
+      
+      template
+      void
+      decrementCount()
+        {
+          allocCnt_.dec();
+        }
+      
+    private:
+      lib::TypedCounter allocCnt_;
     };
   
+  /**
+   * Policy: no additional instantiation accounting
+   */
+  struct NoInstantiationCount
+    {
+      template  size_t allocationCount()  const { return 0; }
+      template  void    incrementCount()  { /* NOP */ }
+      template  void    decrementCount()  { /* NOP */ }
+    };
     
   
+  
+  
+  
+  
+  /* === Allocator frontend === */
+  
   /**
    * Frontend for explicit allocations, using a custom allocator.
    * This template is to be instantiated for the collection of types
@@ -90,13 +135,16 @@ namespace lib {
    * 
    * @todo currently (as of 8/09) the low-level pooled allocator
    *       isn't implemented; instead we do just heap allocations.
-   *       see Ticket #835
+   *       ////////////////////////////////////////////////////////////////////////////////////////////Ticket #835
    */
-  template
+  template
   class SimpleAllocator
-    : InstantiateForEach< typename TYPES::List     // for each of those types...         
-                        , CustomAllocator         //  ...mix in the custom allocator 
+    : InstantiateForEach< typename TYPES::List     // for each of those types...
+                        , CustomAllocator         //  ...mix in the custom allocator
                         >
+    , COUNTER
     {
       
       /** forward plain memory allocation */
@@ -105,7 +153,9 @@ namespace lib {
       allocateSlot ()
         {
           TRACE (memory, "allocate %s", util::tyStr().c_str());
-          return CustomAllocator::allocate (1);
+          XX * newStorage = CustomAllocator::allocate (1);
+          COUNTER::template incrementCount();
+          return newStorage;
         }
       
       template
@@ -114,6 +164,7 @@ namespace lib {
         {
           TRACE (memory, "release %s", util::tyStr().c_str());
           CustomAllocator::deallocate (entry, 1);
+          COUNTER::template decrementCount();
         }
       
       
@@ -126,8 +177,8 @@ namespace lib {
           
           BOOST_STATIC_ASSERT (IsSupportedType::value);
         }
-     
-        
+      
+      
       
       
       public: /* ==== build objects with managed allocation ==== */
@@ -234,10 +285,18 @@ namespace lib {
           releaseSlot (entry);
         }
       
+      
+      /** diagnostics */
+      template
+      size_t
+      numSlots()  const
+        {
+          return COUNTER::template allocationCount();
+        }
     };
   
   
   
-
+  
 } // namespace lib
 #endif
diff --git a/tests/40components.tests b/tests/40components.tests
index d0f849145..615fe5bfe 100644
--- a/tests/40components.tests
+++ b/tests/40components.tests
@@ -1141,7 +1141,7 @@ return: 0
 END
 
 
-PLANNED "custom allocator(I)" SimpleAllocator_test <
 #include 
 #include 
 
@@ -36,7 +34,6 @@ namespace test{
   
   
   using util::isSameObject;
-//  using std::tr1::shared_ptr;
   using std::string;
   using std::rand;
   
@@ -62,6 +59,13 @@ namespace test{
               checksum_ += (crap_[i] = rand() % 128);
           }
         
+        DummyObj (DummyObj const& o)
+          {
+            REQUIRE (siz);
+            for (uint i=0; i,DummyObj<23>,string> > TestAllocator;
+    typedef Types,DummyObj<23>,string> SupportedTypes;
+    typedef SimpleAllocator TestAllocator;
   }
   
   
@@ -148,6 +153,9 @@ namespace test{
           allocator.destroy (pDxx);
           allocator.destroy (pSxx);
           
+          CHECK (0 == allocator.numSlots >());
+          CHECK (0 == allocator.numSlots >());
+          CHECK (0 == allocator.numSlots());
           CHECK (0 == checksum_);
         }
     };

From 7459c0a41fe947789a1b558ebdf30245af9ef771 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 26 Sep 2011 02:19:31 +0200
Subject: [PATCH 078/108] (cont) buffer metadate implementation

state transitions etc
---
 src/proc/engine/buffer-metadata.hpp | 119 +++++++++++++++++++++++++---
 1 file changed, 106 insertions(+), 13 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index ae59f93f6..cf3edb02e 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -64,6 +64,9 @@ namespace engine {
   
   using lib::Literal;
   
+  namespace error = lumiera::error;
+//using error::LUMIERA_ERROR_INVALID;
+
   
   typedef uint64_t LocalKey;
   typedef size_t HashVal;
@@ -93,7 +96,7 @@ namespace engine {
       
       template
       TypeHandler()
-        : createAttached (0)    /////////TODO: how to attach the ctor function??? mabye better use a class with virtual functions?
+        : createAttached (0)    /////////TODO: how to attach the ctor function??? Maybe better use a class with virtual functions?
         , destroyAttached (0)
         { }
     };
@@ -106,13 +109,18 @@ namespace engine {
     public:
       class Entry
         {
+          HashVal parent_;
+          
+        protected:
+          Entry (HashVal parent) : parent_(parent) { }
+         ~Entry ()                                 { }
           
-        
         public:
-          BufferState state()  const;
-          HashVal parentKey()  const;
-          Entry& mark (BufferState newState);
-          Entry& markLocked (const void* buffer);
+          HashVal parentKey()  const { return parent_;}
+          
+          virtual BufferState state()  const              =0;
+          virtual Entry& mark (BufferState newState)      =0;
+          virtual Entry& markLocked (const void* buffer)  =0;
         };
       
       
@@ -172,6 +180,98 @@ namespace engine {
     
   /* === Implementation === */
   
+  namespace {
+
+    using error::LUMIERA_ERROR_LIFECYCLE;
+    using error::LUMIERA_ERROR_BOTTOM_VALUE;
+    
+    
+    class TypeEntry
+      : public Metadata::Entry
+      {
+        virtual BufferState
+        state()  const
+          {
+            return NIL;
+          }
+        
+        virtual Entry&
+        mark (BufferState newState)
+          {
+            if (newState != NIL)
+              throw error::Logic ("This metadata entry is still abstract. "
+                                  "The only possible state transition is to markLocked (buffer)."
+                                 , LUMIERA_ERROR_LIFECYCLE
+                                 );
+            return *this;
+          }
+        
+        virtual Entry&
+        markLocked (const void* buffer)
+          {
+            UNIMPLEMENTED ("how to invoke the allocator. We need to allocate a BufferEntry here");
+          }
+      };
+    
+    
+    class BufferEntry
+      : public Metadata::Entry
+      {
+        BufferState state_;
+        const void* buffer_;
+        
+        virtual BufferState
+        state()  const
+          {
+            return state_;
+          }
+        
+        virtual Entry&
+        markLocked (const void* buffer)
+          {
+            if (!buffer)
+              throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
+                                 , LUMIERA_ERROR_BOTTOM_VALUE);
+            if (this->buffer_)
+              throw error::Logic ("Attempt to lock a slot for a new buffer, "
+                                  "while actually the old buffer is still locked."
+                                 , LUMIERA_ERROR_LIFECYCLE );
+            this->buffer_ = buffer;
+            state_ = LOCKED;
+          }
+        
+        virtual Entry&
+        mark (BufferState newState)
+          {
+            switch (this->state_)
+              {
+              case NIL:
+                throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
+                                    "State transition logic broken (programming error)");
+              case FREE:
+                throw error::Logic ("Need a new buffer pointer in order to lock an entry."
+                                   , LUMIERA_ERROR_LIFECYCLE );
+              case LOCKED:
+                if (newState == EMITTED) break; // allow transition
+                
+              case EMITTED:
+                if (newState == BLOCKED) break; // allow transition
+                
+              case BLOCKED:
+                if (newState == FREE)           // note fall through for LOCKED and EMITTED too 
+                  {
+                    buffer_ = 0;
+                    break; // allow transition
+                  }
+              default:
+                throw error::Fatal ("Invalid buffer state encountered.");
+              }
+            state_ = newState;
+            return *this;
+          }
+      };
+  }
+  
   /** */
   BufferState
   Metadata::Entry::state ()  const
@@ -179,13 +279,6 @@ namespace engine {
       UNIMPLEMENTED ("buffer state accounting");
     }
   
-  /** */
-  HashVal
-  Metadata::Entry::parentKey ()  const
-    {
-      UNIMPLEMENTED ("retrieve the sparent (object or type) key");
-    }
-  
   Metadata::Entry&
   Metadata::Entry::mark (BufferState newState)
     {

From 4df45c44e9fb4771df5e40984b5383246bb8daa1 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 6 Oct 2011 21:55:00 +0200
Subject: [PATCH 079/108] BufferMetadata: some musing about how to access the
 Allocator

---
 src/proc/engine/buffer-metadata.hpp           | 32 +++++++++----------
 .../proc/engine/buffer-metadata-test.cpp      |  2 +-
 2 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index cf3edb02e..42c94c208 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -120,7 +120,6 @@ namespace engine {
           
           virtual BufferState state()  const              =0;
           virtual Entry& mark (BufferState newState)      =0;
-          virtual Entry& markLocked (const void* buffer)  =0;
         };
       
       
@@ -159,6 +158,9 @@ namespace engine {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
+      Entry&
+      markLocked (HashVal parentKey, const void* buffer);
+      
       bool
       isKnown (HashVal key)  const
         {
@@ -206,11 +208,6 @@ namespace engine {
             return *this;
           }
         
-        virtual Entry&
-        markLocked (const void* buffer)
-          {
-            UNIMPLEMENTED ("how to invoke the allocator. We need to allocate a BufferEntry here");
-          }
       };
     
     
@@ -229,15 +226,6 @@ namespace engine {
         virtual Entry&
         markLocked (const void* buffer)
           {
-            if (!buffer)
-              throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
-                                 , LUMIERA_ERROR_BOTTOM_VALUE);
-            if (this->buffer_)
-              throw error::Logic ("Attempt to lock a slot for a new buffer, "
-                                  "while actually the old buffer is still locked."
-                                 , LUMIERA_ERROR_LIFECYCLE );
-            this->buffer_ = buffer;
-            state_ = LOCKED;
           }
         
         virtual Entry&
@@ -287,10 +275,20 @@ namespace engine {
     }
   
   Metadata::Entry&
-  Metadata::Entry::markLocked (const void* buffer)
+  Metadata::markLocked (HashVal parentKey, const void* buffer)
     {
       UNIMPLEMENTED ("transition to locked state");
-      return *this;
+      if (!buffer)
+        throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
+                           , LUMIERA_ERROR_BOTTOM_VALUE);
+      
+      HashVal newKey = this->key (parentKey, buffer);
+      if (isLocked(newKey))
+        throw error::Logic ("Attempt to lock a slot for a new buffer, "
+                            "while actually the old buffer is still locked."
+                           , LUMIERA_ERROR_LIFECYCLE );
+      
+      return this->get(newKey);
     }
   
   
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index 132500fb1..a03984e11 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -137,7 +137,7 @@ namespace test  {
           VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED) );
           VERIFY_ERROR (LIFECYCLE, m1.mark(LOCKED)  );
           
-          Metadata::Entry& m2 = m1.markLocked (SOME_POINTER);
+          Metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER);
           CHECK (!isSameObject (m1,m2));
           CHECK (NIL    == m1.state());
           CHECK (LOCKED == m2.state());

From c79b28fe7cb678b99da0b3ce81d2ca118054687e Mon Sep 17 00:00:00 2001
From: "Michael R. Fisher" 
Date: Thu, 6 Oct 2011 11:58:31 -0500
Subject: [PATCH 080/108] Replacing zoomIn and zoomOut buttons in the
 TimelinePanel with a new TimelineZoomScale widget

---
 .../widgets/timeline/timeline-zoom-scale.cpp  | 115 ++++++++++++++++++
 .../widgets/timeline/timeline-zoom-scale.hpp  |  99 +++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 src/gui/widgets/timeline/timeline-zoom-scale.cpp
 create mode 100644 src/gui/widgets/timeline/timeline-zoom-scale.hpp

diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
new file mode 100644
index 000000000..c019d83ed
--- /dev/null
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -0,0 +1,115 @@
+/*
+  timeline-zoom-scale.cpp  -  Implementation of the zoom scale widget
+
+  Copyright (C)         Lumiera.org
+    2011,               Michael R. Fisher 
+
+  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 "gui/widgets/timeline/timeline-zoom-scale.hpp"
+#include "gui/widgets/timeline-widget.hpp"
+
+using namespace Gtk;
+
+namespace gui {
+namespace widgets {
+
+class TimelineWidget;
+
+namespace timeline {
+
+TimelineZoomScale::TimelineZoomScale()
+  : HBox()
+  , adjustment(0.5, 0.0, 1.0, 0.000001)
+  , slider()
+  , zoomIn(Stock::ZOOM_IN)
+  , zoomOut(Stock::ZOOM_OUT)
+  , smoothing_factor(9.0)
+  , button_step_size(0.03)
+{
+  /* Setup the Slider Control */
+  slider.set_adjustment(adjustment);
+  slider.set_size_request(123,10);
+  slider.set_digits(6);
+  slider.set_inverted(true);
+  slider.set_draw_value(false);
+
+  /* Make our connections */
+  zoomIn.signal_clicked().
+      connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_in_clicked));
+
+  zoomOut.signal_clicked().
+      connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom_out_clicked));
+
+  adjustment.signal_value_changed().
+      connect(sigc::mem_fun(this, &TimelineZoomScale::on_zoom));
+
+  /* Add Our Widgets and show them */
+  pack_start(zoomOut,PACK_SHRINK);
+  pack_start(slider,PACK_SHRINK);
+  pack_start(zoomIn,PACK_SHRINK);
+
+  show_all();
+}
+
+void
+TimelineZoomScale::on_zoom_in_clicked()
+{
+  double newValue = adjustment.get_value() - button_step_size;
+  adjustment.set_value(newValue);
+}
+
+void
+TimelineZoomScale::on_zoom_out_clicked()
+{
+  double newValue = adjustment.get_value() + button_step_size;
+  adjustment.set_value(newValue);
+}
+
+void
+TimelineZoomScale::on_zoom()
+{
+  zoomSignal.emit(calculate_zoom_scale()) ;
+}
+
+sigc::signal
+TimelineZoomScale::signal_zoom()
+{
+  return zoomSignal;
+}
+
+int64_t
+TimelineZoomScale::calculate_zoom_scale()
+{
+  int64_t zoom_scale = 0;
+
+  double smoothed = pow(adjustment.get_value(), smoothing_factor);
+  zoom_scale = (int64_t)( smoothed * (double)TimelineWidget::MaxScale);
+
+  /* Prevent Zooming in To Close and Far */
+  if(zoom_scale < 1)
+    zoom_scale = 1;
+
+  if(zoom_scale > TimelineWidget::MaxScale)
+    zoom_scale = TimelineWidget::MaxScale;
+
+  return zoom_scale;
+}
+
+} // namespace gui
+} // namespace widgets
+} // namespace timeline
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
new file mode 100644
index 000000000..ebeaaffca
--- /dev/null
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -0,0 +1,99 @@
+/*
+  timeline-zoom-scale.hpp  -  Declaration of the zoom scale widget
+
+  Copyright (C)         Lumiera.org
+    2011,               Michael R. Fisher 
+
+  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.
+
+*/
+/** @file timeline-zoom-scale.hpp
+ ** This file contains the definition of the zoom scale widget
+ */
+
+#ifndef TIMELINE_ZOOM_SCALE_HPP
+#define TIMELINE_ZOOM_SCALE_HPP
+
+#include "gui/gtk-lumiera.hpp"
+#include "gui/widgets/mini-button.hpp"
+
+using namespace Gtk;
+using namespace gui::widgets;
+
+namespace gui {
+namespace widgets {
+namespace timeline {
+
+class TimelineZoomScale : public Gtk::HBox
+{
+public:
+  /**
+   * Constructor
+   */
+  TimelineZoomScale();
+
+  /**
+   * Accessor method to the zoomSignal
+   * @return the zoomSignal
+   */
+  sigc::signal signal_zoom();
+
+private:
+  /* Event Handlers */
+
+  /**
+   * Event handler for when the zoomIn Button
+   * is clicked
+   */
+  void on_zoom_in_clicked();
+
+  /**
+   * Event handler for when the zoomIn Button
+   * is clicked
+   */
+  void on_zoom_out_clicked();
+
+  /**
+   * Event handler for when the adjustment
+   * value is changed
+   */
+  void on_zoom();
+
+  /**
+   * Calculate a Zoom Scale value based on
+   * the adjustment's current value
+   * @return The Zoom Scale value
+   */
+  int64_t calculate_zoom_scale();
+
+  /* Widgets */
+  Gtk::Adjustment adjustment;
+  Gtk::HScale slider;
+  MiniButton zoomIn;
+  MiniButton zoomOut;
+
+protected:
+  /* Signals */
+  sigc::signal zoomSignal;
+
+  const double smoothing_factor;
+  const double button_step_size;
+};
+
+} // namespace gui
+} // namespace widgets
+} // namespace timeline
+
+#endif /* TIMELINE_ZOOM_SCALE_HPP */

From e0463da20450ec97e4ee65bca64a09f0783faa33 Mon Sep 17 00:00:00 2001
From: "Michael R. Fisher" 
Date: Thu, 6 Oct 2011 12:38:10 -0500
Subject: [PATCH 081/108] Added TimelineZoomScale widget

---
 src/gui/panels/timeline-panel.cpp             | 16 +++++++++++---
 src/gui/panels/timeline-panel.hpp             |  4 ++++
 src/gui/widgets/timeline-widget.cpp           | 10 ++++-----
 src/gui/widgets/timeline-widget.hpp           |  2 +-
 .../widgets/timeline/timeline-view-window.cpp | 22 +++++++++----------
 .../widgets/timeline/timeline-view-window.hpp |  2 +-
 .../widgets/timeline/timeline-zoom-scale.cpp  |  9 ++++++++
 7 files changed, 43 insertions(+), 22 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index 07dce7b1d..a9c967403 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -60,6 +60,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   , iBeamTool(Gtk::StockID("tool_i_beam"))
   , zoomIn(Stock::ZOOM_IN)
   , zoomOut(Stock::ZOOM_OUT)
+  , zoomScale()
   , updatingToolbar(false)
   , currentTool(timeline::Arrow)
 {
@@ -101,9 +102,10 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
     
   toolbar.append(separator2);
   
-  toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in));
-  toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out));
-   
+  toolbar.append(zoomScale);
+  zoomScale.signal_zoom().
+    connect(mem_fun(this,&TimelinePanel::on_zoom));
+
   toolbar.show_all();
   panelBar.pack_start(toolbar, PACK_SHRINK);
 
@@ -183,6 +185,14 @@ TimelinePanel::on_ibeam_tool()
   set_tool(timeline::IBeam);
 }
 
+void
+TimelinePanel::on_zoom(int64_t zoom_scale)
+{
+  REQUIRE(timelineWidget);
+  timelineWidget->zoom_view(zoom_scale);
+  update_zoom_buttons();
+}
+
 void
 TimelinePanel::on_zoom_in()
 {
diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp
index b65469bc9..aac000277 100644
--- a/src/gui/panels/timeline-panel.hpp
+++ b/src/gui/panels/timeline-panel.hpp
@@ -32,6 +32,8 @@
 #include "gui/panels/panel.hpp"
 #include "gui/widgets/timecode-widget.hpp"
 #include "gui/widgets/timeline-widget.hpp"
+#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
+
 #include "lib/time/timevalue.hpp"
 
 #include 
@@ -87,6 +89,7 @@ private:
   void on_arrow_tool();
   void on_ibeam_tool();
   
+  void on_zoom(int64_t zoom_size);
   void on_zoom_in();
   void on_zoom_out();
   
@@ -191,6 +194,7 @@ private:
   
   MiniButton zoomIn;
   MiniButton zoomOut;
+  gui::widgets::timeline::TimelineZoomScale zoomScale;
   
   Gtk::SeparatorToolItem separator2;
     
diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp
index a00882b7e..cd1459cd8 100644
--- a/src/gui/widgets/timeline-widget.cpp
+++ b/src/gui/widgets/timeline-widget.cpp
@@ -129,13 +129,13 @@ TimelineWidget::set_state(shared_ptr new_state)
 }
 
 void
-TimelineWidget::zoom_view(int zoom_size)
+TimelineWidget::zoom_view(int64_t zoom_scale)
 {
   if(state)
-    {
-      const int view_width = body->get_allocation().get_width();
-      state->get_view_window().zoom_view(view_width / 2, zoom_size);
-    }
+  {
+    const int view_width = body->get_allocation().get_width();
+    state->get_view_window().zoom_view(view_width / 2, zoom_scale);
+  }
 }
 
 ToolType
diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp
index ca213c15b..b8371534b 100644
--- a/src/gui/widgets/timeline-widget.hpp
+++ b/src/gui/widgets/timeline-widget.hpp
@@ -98,7 +98,7 @@ public:
    * @param zoom_size The number of steps to zoom by. The scale factor
    * is 1.25^(-zoom_size).
    */
-  void zoom_view(int zoom_size);
+  void zoom_view(int64_t zoom_scale);
   
   /**
    * Gets the type of the tool currently active.
diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp
index d9b86597b..004022235 100644
--- a/src/gui/widgets/timeline/timeline-view-window.cpp
+++ b/src/gui/widgets/timeline/timeline-view-window.cpp
@@ -31,8 +31,6 @@ namespace gui {
 namespace widgets {
 namespace timeline {
 
-
-
 TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
   : timeOffset(offset)
   , timeScale(scale)
@@ -66,21 +64,21 @@ TimelineViewWindow::set_time_scale(int64_t scale)
 }
 
 void
-TimelineViewWindow::zoom_view(int point, int zoom_size)
-{ 
-  int64_t new_time_scale = (double)timeScale * pow(1.25, -zoom_size);
-  
+TimelineViewWindow::zoom_view(int point, int64_t zoom_scale)
+{
+  int64_t new_time_scale = zoom_scale;
+
   // Limit zooming in too close
   if(new_time_scale < 1) new_time_scale = 1;
-  
-  // Nudge zoom problems caused by integer rounding
+
+ /* Not sure if this is still needed. MRF
+  *  // Nudge zoom problems caused by integer rounding
   if(new_time_scale == timeScale && zoom_size < 0)
     new_time_scale++;
-    
-  // Limit zooming out too far
-  if(new_time_scale > TimelineWidget::MaxScale)
-    new_time_scale = TimelineWidget::MaxScale;
+  */
   
+  // Limit to Min and Max scale code moved to timeline-zoom-scale.cpp
+
   // The view must be shifted so that the zoom is centred on the cursor
   TimeVar newStartPoint = get_time_offset();
   newStartPoint += TimeValue(point * (timeScale - new_time_scale));
diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp
index 4f1733af8..3bf67ac89 100644
--- a/src/gui/widgets/timeline/timeline-view-window.hpp
+++ b/src/gui/widgets/timeline/timeline-view-window.hpp
@@ -101,7 +101,7 @@ public:
    * @param zoom_size The number of steps to zoom by. The scale factor
    * is 1.25^(-zoom_size).
    */
-  void zoom_view(int point, int zoom_size);
+  void zoom_view(int point, int64_t zoom_size);
   
   /**
    * Scrolls the view horizontally as a proportion of the view area.
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index c019d83ed..901df2d7f 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -32,6 +32,15 @@ class TimelineWidget;
 
 namespace timeline {
 
+/**
+ * TODO: The initial adjustment value needs to
+ * match what the TimelineViewWindow's actual timeScale
+ * Value is. TimelineViewWindow::get_time_scale() is
+ * currently a public method, but will soon be private.
+ * Maybe TimelineViewWindow can have a zoom_adjustment
+ * that gets passed to this widget's Constructor?
+ */
+
 TimelineZoomScale::TimelineZoomScale()
   : HBox()
   , adjustment(0.5, 0.0, 1.0, 0.000001)

From caace00dd5cd18d764d4575ce0316cbcda8ed0dc Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 7 Oct 2011 03:49:51 +0200
Subject: [PATCH 082/108] Review the new TimelineZoomScale widget code

---
 src/gui/panels/timeline-panel.cpp              |  1 +
 src/gui/widgets/timeline/timeline-body.cpp     |  2 +-
 .../widgets/timeline/timeline-view-window.cpp  | 18 ++++--------------
 .../widgets/timeline/timeline-view-window.hpp  |  6 +++---
 .../widgets/timeline/timeline-zoom-scale.cpp   | 17 ++++++++++++++++-
 5 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index a9c967403..39c4f5b26 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -124,6 +124,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
 
   zoomIn          .set_tooltip_text(_("Zoom in"));
   zoomOut         .set_tooltip_text(_("Zoom out"));
+  zoomScale       .set_tooltip_text(_("Adjust timeline zoom scale"));
 
   // Setup the timeline widget
   shared_ptr sequence          ///////////////////////////////TICKET #796
diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index 7f78d0929..b0818653a 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -264,7 +264,7 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
           {
             TimelineViewWindow &window = view_window();
             
-                                   /////////////////////////////TICKET# 795 : don't reach in from outside and manipulate internals of the timeline view!
+                                   /////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
                                    /////////////////////////////            : either encapsulate this entirely here, or leave it to the timeline view!            
             const int64_t scale = window.get_time_scale();
             window.set_time_offset(beginShiftTimeOffset
diff --git a/src/gui/widgets/timeline/timeline-view-window.cpp b/src/gui/widgets/timeline/timeline-view-window.cpp
index 004022235..84379b17c 100644
--- a/src/gui/widgets/timeline/timeline-view-window.cpp
+++ b/src/gui/widgets/timeline/timeline-view-window.cpp
@@ -38,20 +38,20 @@ TimelineViewWindow::TimelineViewWindow (Offset offset, int64_t scale)
 }
 
 Offset
-TimelineViewWindow::get_time_offset() const      /////////////////////TODO: this function shouldn't be accessible from outside
+TimelineViewWindow::get_time_offset() const      /////////////////////TICKET #795: this function shouldn't be accessible from outside
 {
   return Offset (timeOffset);
 }
 
 void
-TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TODO: this function shouldn't be accessible from outside
+TimelineViewWindow::set_time_offset(TimeValue const& offset) /////////TICKET #795: this function shouldn't be accessible from outside
 {
   timeOffset = offset;
   changedSignal.emit();
 }
 
 int64_t
-TimelineViewWindow::get_time_scale() const       /////////////////////TODO: this function shouldn't be accessible from outside
+TimelineViewWindow::get_time_scale() const       /////////////////////TICKET #795: this function shouldn't be accessible from outside
 {
   return timeScale;
 }
@@ -64,21 +64,11 @@ TimelineViewWindow::set_time_scale(int64_t scale)
 }
 
 void
-TimelineViewWindow::zoom_view(int point, int64_t zoom_scale)
+TimelineViewWindow::zoom_view(int point, int64_t new_time_scale)
 {
-  int64_t new_time_scale = zoom_scale;
-
   // Limit zooming in too close
   if(new_time_scale < 1) new_time_scale = 1;
 
- /* Not sure if this is still needed. MRF
-  *  // Nudge zoom problems caused by integer rounding
-  if(new_time_scale == timeScale && zoom_size < 0)
-    new_time_scale++;
-  */
-  
-  // Limit to Min and Max scale code moved to timeline-zoom-scale.cpp
-
   // The view must be shifted so that the zoom is centred on the cursor
   TimeVar newStartPoint = get_time_offset();
   newStartPoint += TimeValue(point * (timeScale - new_time_scale));
diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp
index 3bf67ac89..496085555 100644
--- a/src/gui/widgets/timeline/timeline-view-window.hpp
+++ b/src/gui/widgets/timeline/timeline-view-window.hpp
@@ -98,10 +98,10 @@ public:
   /**
    * Zooms the view in or out as by a number of steps while keeping a 
    * given point on the timeline still.
-   * @param zoom_size The number of steps to zoom by. The scale factor
-   * is 1.25^(-zoom_size).
+   * @param new_time_scale The number of steps to zoom by. The scale factor
+   * is 1.25^(-new_time_scale).
    */
-  void zoom_view(int point, int64_t zoom_size);
+  void zoom_view(int point, int64_t new_time_scale);
   
   /**
    * Scrolls the view horizontally as a proportion of the view area.
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index 901df2d7f..9129a5ce5 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -33,12 +33,27 @@ class TimelineWidget;
 namespace timeline {
 
 /**
- * TODO: The initial adjustment value needs to
+ * @todo The initial adjustment value needs to
  * match what the TimelineViewWindow's actual timeScale
  * Value is. TimelineViewWindow::get_time_scale() is
  * currently a public method, but will soon be private.
  * Maybe TimelineViewWindow can have a zoom_adjustment
  * that gets passed to this widget's Constructor?
+ * 
+ * @todo actually there is a more involved problem.
+ * The TimelineWidget maintains a TimelineState, which in turn
+ * owns the TimelineViewWindow. Now, the problem is: when we
+ * switch to another Sequence (View), then this TimelineState
+ * gets switched too, causing also a entirely different TimelineViewWindow
+ * to become effective. Thus
+ * - how can we managed to be notified from that switch?
+ * - problem is: TimelineZoomScale widget is owned by the TimelinePannel.
+ *   Likewise, TimelineWidget is owned by the TimelinePannel. But the
+ *   state handling/switching logic is embedded within TimelineWidget
+ * - and finally: how can we translate the actual scale (in time units),
+ *   as maintained within TimelineViewWindow, back into the adjustment
+ *   used here (which uses a relative scale 0...1.0 ) 
+ * 
  */
 
 TimelineZoomScale::TimelineZoomScale()

From 3acf8049888846956cbd8ccd95c78bad46e8a1b2 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 8 Oct 2011 02:21:29 +0200
Subject: [PATCH 083/108] flesh out the buffer metadata API

functionality just stubbed still
---
 src/proc/engine/buffer-metadata.hpp           | 120 +++++++++++-------
 .../proc/engine/buffer-metadata-test.cpp      |  22 +++-
 2 files changed, 95 insertions(+), 47 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index 42c94c208..e3a071259 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -57,7 +57,7 @@
 #include "lib/error.hpp"
 #include "lib/symbol.hpp"
 
-//#include 
+#include 
 
 
 namespace engine {
@@ -65,8 +65,7 @@ namespace engine {
   using lib::Literal;
   
   namespace error = lumiera::error;
-//using error::LUMIERA_ERROR_INVALID;
-
+  
   
   typedef uint64_t LocalKey;
   typedef size_t HashVal;
@@ -105,6 +104,7 @@ namespace engine {
   
   
   class Metadata
+    : boost::noncopyable
     {
     public:
       class Entry
@@ -119,6 +119,7 @@ namespace engine {
           HashVal parentKey()  const { return parent_;}
           
           virtual BufferState state()  const              =0;
+          virtual const void* access() const              =0;
           virtual Entry& mark (BufferState newState)      =0;
         };
       
@@ -158,9 +159,6 @@ namespace engine {
           UNIMPLEMENTED ("access, possibly create metadata records");
         }
       
-      Entry&
-      markLocked (HashVal parentKey, const void* buffer);
-      
       bool
       isKnown (HashVal key)  const
         {
@@ -173,8 +171,12 @@ namespace engine {
           UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
         }
       
-#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
-#endif    /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #834
+      
+      /* == memory management == */
+      
+      Entry& markLocked (HashVal parentKey, const void* buffer);
+      void release (HashVal key);
+      
     };
     
     
@@ -183,7 +185,7 @@ namespace engine {
   /* === Implementation === */
   
   namespace {
-
+    
     using error::LUMIERA_ERROR_LIFECYCLE;
     using error::LUMIERA_ERROR_BOTTOM_VALUE;
     
@@ -197,6 +199,16 @@ namespace engine {
             return NIL;
           }
         
+        virtual const void*
+        access()  const
+          {
+            throw error::Logic ("This metadata entry is still abstract. "
+                                "It can't be associated with a concrete buffer. "
+                                "You need to invoke markLocked (buffer)."
+                               , LUMIERA_ERROR_LIFECYCLE
+                               );
+          }
+        
         virtual Entry&
         mark (BufferState newState)
           {
@@ -220,12 +232,18 @@ namespace engine {
         virtual BufferState
         state()  const
           {
+            __must_not_be_NIL();
             return state_;
           }
         
-        virtual Entry&
-        markLocked (const void* buffer)
+        virtual const void*
+        access()  const
           {
+            __must_not_be_NIL();
+            __must_not_be_FREE();
+            
+            ENSURE (buffer_);
+            return buffer_;
           }
         
         virtual Entry&
@@ -233,12 +251,9 @@ namespace engine {
           {
             switch (this->state_)
               {
-              case NIL:
-                throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
-                                    "State transition logic broken (programming error)");
-              case FREE:
-                throw error::Logic ("Need a new buffer pointer in order to lock an entry."
-                                   , LUMIERA_ERROR_LIFECYCLE );
+              case NIL:  __must_not_be_NIL();
+              case FREE: __must_not_be_FREE();
+                
               case LOCKED:
                 if (newState == EMITTED) break; // allow transition
                 
@@ -246,7 +261,7 @@ namespace engine {
                 if (newState == BLOCKED) break; // allow transition
                 
               case BLOCKED:
-                if (newState == FREE)           // note fall through for LOCKED and EMITTED too 
+                if (newState == FREE)           // note fall through for LOCKED and EMITTED too
                   {
                     buffer_ = 0;
                     break; // allow transition
@@ -257,39 +272,54 @@ namespace engine {
             state_ = newState;
             return *this;
           }
+        
+        
+        void
+        __must_not_be_NIL()  const
+          {
+            if (NIL == state_)
+              throw error::Fatal ("Concrete buffer entry with state==NIL encountered."
+                                  "State transition logic broken (programming error)");
+          }
+        
+        void
+        __must_not_be_FREE()  const
+          {
+            if (FREE == state_)
+                throw error::Logic ("Buffer is inaccessible (marked as free). "
+                                    "Need a new buffer pointer in order to lock an entry. "
+                                    "You should invoke markLocked(buffer) prior to access."
+                                   , LUMIERA_ERROR_LIFECYCLE );
+          }
       };
   }
   
+  
+  
+  
   /** */
-  BufferState
-  Metadata::Entry::state ()  const
-    {
-      UNIMPLEMENTED ("buffer state accounting");
-    }
-  
-  Metadata::Entry&
-  Metadata::Entry::mark (BufferState newState)
-    {
-      UNIMPLEMENTED ("buffer state transitions");
-      return *this;
-    }
-  
   Metadata::Entry&
   Metadata::markLocked (HashVal parentKey, const void* buffer)
-    {
-      UNIMPLEMENTED ("transition to locked state");
-      if (!buffer)
-        throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
-                           , LUMIERA_ERROR_BOTTOM_VALUE);
-      
-      HashVal newKey = this->key (parentKey, buffer);
-      if (isLocked(newKey))
-        throw error::Logic ("Attempt to lock a slot for a new buffer, "
-                            "while actually the old buffer is still locked."
-                           , LUMIERA_ERROR_LIFECYCLE );
-      
-      return this->get(newKey);
-    }
+  {
+    UNIMPLEMENTED ("transition to locked state");
+    if (!buffer)
+      throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
+                         , LUMIERA_ERROR_BOTTOM_VALUE);
+    
+    HashVal newKey = this->key (parentKey, buffer);
+    if (isLocked(newKey))
+      throw error::Logic ("Attempt to lock a slot for a new buffer, "
+                          "while actually the old buffer is still locked."
+                         , LUMIERA_ERROR_LIFECYCLE );
+    
+    return this->get(newKey);
+  }
+  
+  void
+  Metadata::release (HashVal key)
+  {
+    UNIMPLEMENTED ("metadata memory management");
+  }
   
   
   
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index a03984e11..fd55bf4c8 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -141,6 +141,7 @@ namespace test  {
           CHECK (!isSameObject (m1,m2));
           CHECK (NIL    == m1.state());
           CHECK (LOCKED == m2.state());
+          CHECK (SOME_POINTER == m2.access());
           
           HashVal keyX = meta_->key(key1, SOME_POINTER);
           CHECK (meta_->isLocked(keyX));
@@ -152,11 +153,28 @@ namespace test  {
           CHECK ( isSameObject (m2, meta_->get(keyX)));
           CHECK ( key1 == m2.parentKey());
           
-          m2.mark(FREE);              // Warning: don't use the m2 reference anymore! 
+          // now able to do state transitions
+          CHECK (LOCKED == m2.state());
+          m2.mark(EMITTED);
+          CHECK (EMITTED == m2.state());
+          CHECK (SOME_POINTER == m2.access());
+          CHECK ( meta_->isLocked(keyX));
+          CHECK ( meta_->isKnown(keyX));
+          
+          // but the FREE state is a dead end
+          m2.mark(FREE); 
+          CHECK (!meta_->isLocked(keyX));
+          CHECK ( meta_->isKnown(keyX));
+          CHECK ( meta_->isKnown(key1));
+          VERIFY_ERROR (LIFECYCLE, m2.access());
+          VERIFY_ERROR (LIFECYCLE, m2.mark(LOCKED));
+          CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible
+          
+          meta_->release(keyX);
           CHECK (!meta_->isLocked(keyX));
           CHECK (!meta_->isKnown(keyX));
           CHECK ( meta_->isKnown(key1));
-          VERIFY_ERROR (INVALID, meta_->get(keyX));
+          VERIFY_ERROR (INVALID, meta_->get(keyX)); // now unaccessible
         }
       
       

From 7ea9afd1bba6cab967d68c6e9bc9832e2a22850a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 9 Oct 2011 04:20:56 +0200
Subject: [PATCH 084/108] first attempt to implement buffer metadata...

...leading to refactoring
---
 src/proc/engine/buffer-metadata.hpp           | 228 +++++++++---------
 .../proc/engine/buffer-metadata-test.cpp      |   6 +-
 2 files changed, 117 insertions(+), 117 deletions(-)

diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index e3a071259..6785e1006 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -78,6 +78,8 @@ namespace engine {
       BLOCKED
     };
   
+  
+  
   const LocalKey UNSPECIFIC = 0;
   
   struct TypeHandler
@@ -101,134 +103,54 @@ namespace engine {
     };
   
   const TypeHandler RAW_BUFFER;
+
+
+    /* === Implementation === */
   
-  
-  class Metadata
-    : boost::noncopyable
-    {
-    public:
-      class Entry
-        {
-          HashVal parent_;
-          
-        protected:
-          Entry (HashVal parent) : parent_(parent) { }
-         ~Entry ()                                 { }
-          
-        public:
-          HashVal parentKey()  const { return parent_;}
-          
-          virtual BufferState state()  const              =0;
-          virtual const void* access() const              =0;
-          virtual Entry& mark (BufferState newState)      =0;
-        };
-      
-      
-      Metadata (Literal implementationID)
-        { }
-      
-      HashVal
-      key ( size_t storageSize
-          , TypeHandler instanceFunc =RAW_BUFFER
-          , LocalKey specifics =UNSPECIFIC)
-        {
-          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
-        }
-      
-      HashVal
-      key (HashVal parentKey, TypeHandler instanceFunc)
-        {
-          UNIMPLEMENTED ("create sub-type key");
-        }
-      
-      HashVal
-      key (HashVal parentKey, LocalKey specifics)
-        {
-          UNIMPLEMENTED ("create sub-type key");
-        }
-      
-      HashVal
-      key (HashVal parentKey, const void* concreteBuffer)
-        {
-          UNIMPLEMENTED ("create sub-object key for concrete buffer");
-        }
-      
-      Entry&
-      get (HashVal key)
-        {
-          UNIMPLEMENTED ("access, possibly create metadata records");
-        }
-      
-      bool
-      isKnown (HashVal key)  const
-        {
-          UNIMPLEMENTED ("diagnostics: known record?");
-        }
-      
-      bool
-      isLocked (HashVal key)  const
-        {
-          UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
-        }
-      
-      
-      /* == memory management == */
-      
-      Entry& markLocked (HashVal parentKey, const void* buffer);
-      void release (HashVal key);
-      
-    };
-    
-    
-    
-    
-  /* === Implementation === */
-  
-  namespace {
+  namespace metadata {
     
     using error::LUMIERA_ERROR_LIFECYCLE;
     using error::LUMIERA_ERROR_BOTTOM_VALUE;
     
     
-    class TypeEntry
-      : public Metadata::Entry
+    class Key
       {
-        virtual BufferState
-        state()  const
-          {
-            return NIL;
-          }
+        HashVal parent_;
+        HashVal hashID_;
         
-        virtual const void*
-        access()  const
-          {
-            throw error::Logic ("This metadata entry is still abstract. "
-                                "It can't be associated with a concrete buffer. "
-                                "You need to invoke markLocked (buffer)."
-                               , LUMIERA_ERROR_LIFECYCLE
-                               );
-          }
+        size_t storageSize_;
+        TypeHandler instanceFunc_;
+        LocalKey specifics_;
         
-        virtual Entry&
-        mark (BufferState newState)
-          {
-            if (newState != NIL)
-              throw error::Logic ("This metadata entry is still abstract. "
-                                  "The only possible state transition is to markLocked (buffer)."
-                                 , LUMIERA_ERROR_LIFECYCLE
-                                 );
-            return *this;
-          }
+      public:
+        Key (HashVal familyID, size_t storageSize)
+          : parent_(0)
+          , hashID_(familyID)
+          , storageSize_(storageSize)
+          , instanceFunc_(RAW_BUFFER)
+          , specifics_(UNSPECIFIC)
+          { }
         
+        Key (Key const& parent) 
+          : parent_(parent.hashID_)
+          , hashID_(0)
+          , storageSize_(parent.storageSize_)
+          , instanceFunc_(parent.instanceFunc_)
+          , specifics_(parent.specifics_)
+          { }
+        
+        HashVal parentKey()  const { return parent_;}
+        operator HashVal()   const { return hashID_;}
       };
     
     
-    class BufferEntry
-      : public Metadata::Entry
+    class Entry
+      : public Key
       {
         BufferState state_;
         const void* buffer_;
         
+      public:
         virtual BufferState
         state()  const
           {
@@ -297,6 +219,84 @@ namespace engine {
   
   
   
+  
+  class Metadata
+    : boost::noncopyable
+    {
+    public:
+      
+      typedef metadata::Key Key;
+      typedef metadata::Entry Entry;
+      
+      
+      Metadata (Literal implementationID)
+        { }
+      
+      Key
+      key ( size_t storageSize
+          , TypeHandler instanceFunc =RAW_BUFFER
+          , LocalKey specifics =UNSPECIFIC)
+        {
+          UNIMPLEMENTED ("combine the distinguishing properties into a single hash");
+        }
+      
+      Key
+      key (HashVal parentKey, TypeHandler instanceFunc)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      Key
+      key (HashVal parentKey, LocalKey specifics)
+        {
+          UNIMPLEMENTED ("create sub-type key");
+        }
+      
+      Key
+      key (HashVal parentKey, const void* concreteBuffer)
+        {
+          UNIMPLEMENTED ("create sub-object key for concrete buffer");
+        }
+      
+      Key const&
+      get (HashVal hashID)
+        {
+          UNIMPLEMENTED ("access the plain key entry");
+        }
+      
+      Entry&
+      get (Key key)
+        {
+          UNIMPLEMENTED ("access, possibly create metadata records");
+        }
+      
+      bool
+      isKnown (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: known record?");
+        }
+      
+      bool
+      isLocked (HashVal key)  const
+        {
+          UNIMPLEMENTED ("diagnostics: actually locked buffer instance record?");
+        }
+      
+      
+      /* == memory management == */
+      
+      Entry& markLocked (HashVal parentKey, const void* buffer);
+      void release (HashVal key);
+      
+    };
+    
+    
+    
+    
+  
+  
+  
+  
   /** */
   Metadata::Entry&
   Metadata::markLocked (HashVal parentKey, const void* buffer)
@@ -304,13 +304,13 @@ namespace engine {
     UNIMPLEMENTED ("transition to locked state");
     if (!buffer)
       throw error::Fatal ("Attempt to lock for a NULL buffer. Allocation floundered?"
-                         , LUMIERA_ERROR_BOTTOM_VALUE);
+                         , error::LUMIERA_ERROR_BOTTOM_VALUE);
     
-    HashVal newKey = this->key (parentKey, buffer);
+    Key newKey = this->key (parentKey, buffer);
     if (isLocked(newKey))
       throw error::Logic ("Attempt to lock a slot for a new buffer, "
                           "while actually the old buffer is still locked."
-                         , LUMIERA_ERROR_LIFECYCLE );
+                         , error::LUMIERA_ERROR_LIFECYCLE );
     
     return this->get(newKey);
   }
diff --git a/tests/components/proc/engine/buffer-metadata-test.cpp b/tests/components/proc/engine/buffer-metadata-test.cpp
index fd55bf4c8..de62e39a3 100644
--- a/tests/components/proc/engine/buffer-metadata-test.cpp
+++ b/tests/components/proc/engine/buffer-metadata-test.cpp
@@ -110,11 +110,11 @@ namespace test  {
       void
       verifyBasicProperties()
         {
-          HashVal key = meta_->key(SIZE_A);
+          Metadata::Key key = meta_->key(SIZE_A);
           CHECK (key);
           
-          HashVal key1 = meta_->key(SIZE_A);
-          HashVal key2 = meta_->key(SIZE_B);
+          Metadata::Key key1 = meta_->key(SIZE_A);
+          Metadata::Key key2 = meta_->key(SIZE_B);
           CHECK (key1);
           CHECK (key2);
           CHECK (key == key1);

From d9f64c94bf5353033db6bf588ebe23550941b10b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 9 Oct 2011 14:52:58 +0200
Subject: [PATCH 085/108] simple demo using a pointer and a struct

---
 src/tool/try.cpp | 67 +++++++++++++++++++++++++-----------------------
 1 file changed, 35 insertions(+), 32 deletions(-)

diff --git a/src/tool/try.cpp b/src/tool/try.cpp
index bdfca7100..9e68a90a5 100644
--- a/src/tool/try.cpp
+++ b/src/tool/try.cpp
@@ -19,52 +19,55 @@
 // 1/11  - exploring numeric limits
 // 1/11  - integer floor and wrap operation(s)
 // 1/11  - how to fetch the path of the own executable -- at least under Linux?
-
-
-#include 
+// 10/11 - simple demo using a pointer and a struct
 
 
 #include 
-#include 
 
-extern "C" {
-#include 
-}
-#include "lib/error.hpp"
-#include "lib/symbol.hpp"
-
-
-using std::string;
 using std::cout;
-using std::endl;
-using lib::Literal;
-using lib::STRING_MAX_RELEVANT;
 
-namespace error = lumiera::error;
 
-Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe");
 
-string
-catchMyself ()
-{
-  string buff(STRING_MAX_RELEVANT+1, '\0' );
-  ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT);
-  
-  if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT))
-    throw error::Fatal ("unable to discover path of running executable");
-  
-  buff.resize(chars_read);
-  return buff;
-}
+/**
+ * custom datastructure
+ * holding a constant char array with "hey"
+ */
+struct MyStruct
+  {
+    char data_[3];
+    const int length_;
+    
+    MyStruct()
+      : length_(3)
+      { 
+        const char *tmp = "hey";
+        for (int i=0; ilength_; ++i)
+      cout << myPointer->data_[i];
+    
+    cout << "\n";
+  }
+
 
 
 int 
 main (int, char**) //(int argc, char* argv[])
   {
+    printMyStruct (&theStruct);
     
-    NOBUG_INIT;
-    
-    cout << "\n\nwho am I? :" << catchMyself();
     cout <<  "\n.gulp.\n";
     
     return 0;

From 2477f2e682dcb527bb18a9c119f64b4ccf8f920a Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 9 Oct 2011 16:03:51 +0200
Subject: [PATCH 086/108] outline of building a hierarchy of hash keys for
 buffer metadata

---
 src/proc/asset/entry-id.hpp         |  1 +
 src/proc/engine/buffer-metadata.hpp | 85 ++++++++++++++++++++++++++---
 2 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp
index 0d51e7fa2..c8b6849ac 100644
--- a/src/proc/asset/entry-id.hpp
+++ b/src/proc/asset/entry-id.hpp
@@ -48,6 +48,7 @@
 #include "lib/hash-indexed.hpp"
 #include "lib/util.hpp"
 
+#include 
 #include 
 #include 
 #include 
diff --git a/src/proc/engine/buffer-metadata.hpp b/src/proc/engine/buffer-metadata.hpp
index 6785e1006..a257f6a8c 100644
--- a/src/proc/engine/buffer-metadata.hpp
+++ b/src/proc/engine/buffer-metadata.hpp
@@ -57,6 +57,7 @@
 #include "lib/error.hpp"
 #include "lib/symbol.hpp"
 
+#include 
 #include 
 
 
@@ -67,7 +68,6 @@ namespace engine {
   namespace error = lumiera::error;
   
   
-  typedef uint64_t LocalKey;
   typedef size_t HashVal;
   
   enum BufferState
@@ -79,8 +79,27 @@ namespace engine {
     };
   
   
+  /**
+   * an opaque ID to be used by the BufferProvider implementation.
+   * Typically this will be used, to set apart some pre-registered
+   * kinds of buffers. It is treated as being part of the buffer type.
+   * LocalKey objects may be copied but not re-assigned or changed.
+   */
+  class LocalKey
+    {
+      uint64_t privateID_;
+      
+      /** assignment prohibited */
+      LocalKey& operator= (LocalKey const&);
+      
+    public:
+      LocalKey (uint64_t opaqueValue=0)
+        : privateID_(opaqueValue)
+        { }
+      
+      operator uint64_t()  const { return privateID_; }
+    };
   
-  const LocalKey UNSPECIFIC = 0;
   
   struct TypeHandler
     {
@@ -102,7 +121,11 @@ namespace engine {
         { }
     };
   
-  const TypeHandler RAW_BUFFER;
+  namespace { // internal constants to mark the default case
+    
+    const LocalKey UNSPECIFIC;
+    const TypeHandler RAW_BUFFER;
+  }
 
 
     /* === Implementation === */
@@ -112,6 +135,14 @@ namespace engine {
     using error::LUMIERA_ERROR_LIFECYCLE;
     using error::LUMIERA_ERROR_BOTTOM_VALUE;
     
+    namespace { // details of hash calculation
+        template
+        HashVal
+        chainedHash(HashVal accumulatedHash, VAL changedValue)
+        {
+          UNIMPLEMENTED ("calculate a new hash value, based on parent hash");
+        }
+    }
     
     class Key
       {
@@ -122,23 +153,63 @@ namespace engine {
         TypeHandler instanceFunc_;
         LocalKey specifics_;
         
+        
       public:
+        /** build a standard basic key describing a kind of Buffer. 
+         * @param familyID basic hash seed value to distinguish
+         *                 families of buffer types managed by
+         *                 different BufferProvider instances
+         * @param storageSize fundamental info: buffer size
+         */
         Key (HashVal familyID, size_t storageSize)
-          : parent_(0)
+          : parent_(chainedHash (familyID, storageSize))
           , hashID_(familyID)
           , storageSize_(storageSize)
           , instanceFunc_(RAW_BUFFER)
           , specifics_(UNSPECIFIC)
           { }
         
-        Key (Key const& parent) 
+        // standard copy operations permitted
+        
+        /** create a derived buffer type description.
+         *  Using a different storage size than the parent type,
+         *  all else remaining the same
+         */
+        Key (Key const& parent, size_t differingStorageSize) 
           : parent_(parent.hashID_)
-          , hashID_(0)
-          , storageSize_(parent.storageSize_)
+          , hashID_(chainedHash (parent_, differingStorageSize))
+          , storageSize_(differingStorageSize)  // differing from parent
           , instanceFunc_(parent.instanceFunc_)
           , specifics_(parent.specifics_)
           { }
         
+        
+        /** create a derived buffer type description.
+         *  Using different ctor and dtor functions,
+         *  all else remaining the same as with parent
+         */
+        Key (Key const& parent, TypeHandler differingTypeHandlerFunctions) 
+          : parent_(parent.hashID_)
+          , hashID_(chainedHash (parent_, differingTypeHandlerFunctions))
+          , storageSize_(parent.storageSize_)
+          , instanceFunc_(differingTypeHandlerFunctions)  // differing from parent
+          , specifics_(parent.specifics_)
+          { }
+        
+        
+        /** create a derived buffer type description.
+         *  Using a different private ID than the parent type,
+         *  all else remaining the same
+         */
+        Key (Key const& parent, LocalKey anotherTypeSpecificInternalID) 
+          : parent_(parent.hashID_)
+          , hashID_(chainedHash (parent_, anotherTypeSpecificInternalID))
+          , storageSize_(parent.storageSize_)
+          , instanceFunc_(parent.instanceFunc_)
+          , specifics_(anotherTypeSpecificInternalID)  // differing from parent
+          { }
+        
+        
         HashVal parentKey()  const { return parent_;}
         operator HashVal()   const { return hashID_;}
       };

From 357bfaa674443e5cdd957b1521cdb11c2d2d1075 Mon Sep 17 00:00:00 2001
From: "Michael R. Fisher" 
Date: Sun, 9 Oct 2011 12:48:47 -0500
Subject: [PATCH 087/108] Added pointer to TimelineViewWindow in
 TimelineZoomScroll

---
 src/gui/panels/timeline-panel.cpp                | 13 +++++++++++--
 src/gui/widgets/timeline/timeline-zoom-scale.cpp |  5 +++++
 src/gui/widgets/timeline/timeline-zoom-scale.hpp |  5 +++++
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index 39c4f5b26..8979db727 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -23,6 +23,7 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "gui/panels/timeline-panel.hpp"
+#include "gui/widgets/timeline/timeline-zoom-scale.hpp"
 
 #include "gui/workspace/workspace-window.hpp"
 #include "gui/model/project.hpp"
@@ -34,6 +35,7 @@
 using namespace Gtk;
 using namespace sigc;
 using namespace gui::widgets;
+using namespace gui::widgets::timeline;
 using namespace gui::model;
 
 using boost::shared_ptr;  ///////////////////////////////TICKET #796
@@ -131,7 +133,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
     = *get_project().get_sequences().begin();  
   timelineWidget.reset(new TimelineWidget(load_state(sequence)));
   pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
-  
+
+  // TimelineWidget is now initialized, lets set it in the zoomScale
+  // and wire it with the timeline state changed signal
+  zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());
+ /* This doesn't work
+  timelineWidget->signal_state_changed().
+      connect(sigc::mem_fun(zoomScale, &TimelineZoomScale::on_timeline_state_changed));
+  */
+
   // Set the initial UI state
   update_sequence_chooser();
   update_tool_buttons();
@@ -139,7 +149,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   show_time (Time::ZERO);
 }
 
-
 const char*
 TimelinePanel::get_title()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index 9129a5ce5..d71220f7c 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -116,6 +116,11 @@ TimelineZoomScale::signal_zoom()
   return zoomSignal;
 }
 
+void
+TimelineZoomScale::set_view_window(TimelineViewWindow &view_window)
+{
+  timelineViewWindow =& view_window;
+}
 int64_t
 TimelineZoomScale::calculate_zoom_scale()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
index ebeaaffca..512bea9d8 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -28,6 +28,7 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "gui/widgets/mini-button.hpp"
+#include "gui/widgets/timeline/timeline-view-window.hpp"
 
 using namespace Gtk;
 using namespace gui::widgets;
@@ -50,6 +51,8 @@ public:
    */
   sigc::signal signal_zoom();
 
+  void set_view_window(TimelineViewWindow &view_window);
+
 private:
   /* Event Handlers */
 
@@ -90,6 +93,8 @@ protected:
 
   const double smoothing_factor;
   const double button_step_size;
+
+  TimelineViewWindow *timelineViewWindow;
 };
 
 } // namespace gui

From 5905fdf0cb467d6c86773c0f3b2afa0c0a54b138 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 10 Oct 2011 00:08:50 +0200
Subject: [PATCH 088/108] change definition of TimelineStateChangeSignal

the timeline-state-change signal now delivers
the new TimelineState object immediately, instead
of requiring the listeners to pick it up
---
 src/gui/panels/timeline-panel.cpp             |  6 ++---
 src/gui/widgets/timeline-widget.cpp           |  4 ++--
 src/gui/widgets/timeline-widget.hpp           | 13 +++++-----
 src/gui/widgets/timeline/timeline-body.cpp    | 15 +++++++++---
 src/gui/widgets/timeline/timeline-body.hpp    | 10 +++++---
 src/gui/widgets/timeline/timeline-ruler.cpp   | 24 ++++++++++++-------
 src/gui/widgets/timeline/timeline-ruler.hpp   | 19 +++++++++++----
 .../widgets/timeline/timeline-zoom-scale.cpp  | 13 ++++++++++
 .../widgets/timeline/timeline-zoom-scale.hpp  |  9 +++++++
 9 files changed, 83 insertions(+), 30 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index 8979db727..b92554341 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -137,10 +137,8 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   // TimelineWidget is now initialized, lets set it in the zoomScale
   // and wire it with the timeline state changed signal
   zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());
- /* This doesn't work
-  timelineWidget->signal_state_changed().
-      connect(sigc::mem_fun(zoomScale, &TimelineZoomScale::on_timeline_state_changed));
-  */
+  zoomScale.wireTimelineState (timelineWidget->state_changed_signal());
+  
 
   // Set the initial UI state
   update_sequence_chooser();
diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp
index cd1459cd8..038af15b5 100644
--- a/src/gui/widgets/timeline-widget.cpp
+++ b/src/gui/widgets/timeline-widget.cpp
@@ -125,7 +125,7 @@ TimelineWidget::set_state(shared_ptr new_state)
   update_tracks();
   
   // Send the state changed signal
-  stateChangedSignal.emit();
+  stateChangedSignal.emit (state);
 }
 
 void
@@ -178,7 +178,7 @@ TimelineWidget::hovering_track_changed_signal() const
   return hoveringTrackChangedSignal;
 }
 
-sigc::signal
+TimelineWidget::TimelineStateChangeSignal
 TimelineWidget::state_changed_signal() const
 {
   return stateChangedSignal;
diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp
index b8371534b..b3f801f9a 100644
--- a/src/gui/widgets/timeline-widget.hpp
+++ b/src/gui/widgets/timeline-widget.hpp
@@ -115,14 +115,16 @@ public:
   
 public:
   /* ===== Signals ===== */
+  typedef sigc::signal > TimelineStateChangeSignal;
+  typedef sigc::signal > HoveringTrackChangedSignal;
+  
   sigc::signal mouse_hover_signal() const;
   
   sigc::signal playback_period_drag_released_signal() const;
   
-  sigc::signal >
-    hovering_track_changed_signal() const;
+  HoveringTrackChangedSignal hovering_track_changed_signal() const;
     
-  sigc::signal state_changed_signal() const;
+  TimelineStateChangeSignal state_changed_signal() const;
   
   /* ===== Events ===== */
 protected:
@@ -275,9 +277,8 @@ protected:
   // Signals
   sigc::signal mouseHoverSignal;
   sigc::signal playbackPeriodDragReleasedSignal;
-  sigc::signal >
-    hoveringTrackChangedSignal;
-  sigc::signal stateChangedSignal;
+  HoveringTrackChangedSignal hoveringTrackChangedSignal;
+  TimelineStateChangeSignal stateChangedSignal;
     
   bool update_tracks_frozen;
    
diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index b0818653a..74fed06f7 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -39,6 +39,8 @@ using namespace lumiera;
 
 using gui::util::CairoUtil;
 
+using boost::shared_ptr;           ////////////////////TICKET #796
+
 namespace gui {
 namespace widgets {
 namespace timeline {
@@ -61,7 +63,7 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget)
   register_styles();
   
   // Reset the state
-  on_state_changed();
+  propagateStateChange();
 }
 
 TimelineBody::~TimelineBody()
@@ -294,9 +296,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
 }
 
 void
-TimelineBody::on_state_changed()
+TimelineBody::on_state_changed (shared_ptr newState)
 {
-  if(timelineWidget.get_state())
+  REQUIRE (newState);
+  timelineState = newState;
+}
+
+void
+TimelineBody::propagateStateChange()
+{
+  if (timelineState)
     {
       // Connect up some events
       view_window().changed_signal().connect(
diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp
index 74b057738..f2441da9f 100644
--- a/src/gui/widgets/timeline/timeline-body.hpp
+++ b/src/gui/widgets/timeline/timeline-body.hpp
@@ -125,10 +125,9 @@ protected:
   bool on_motion_notify_event(GdkEventMotion *event);
   
   /**
-   * The event handler for when the TimelineWidget's state object is
-   * replaced.
+   * The event handler for when the TimelineWidget's state is switched.
    */
-  void on_state_changed();
+  void on_state_changed (boost::shared_ptr newState);
   
   /* ===== Internals ===== */
 private:
@@ -161,6 +160,9 @@ private:
   
   void set_vertical_offset(int offset);
   
+  /** adjust to the new timeline state */
+  void propagateStateChange();
+  
   /**
    * A helper function to get the view window
    * @remarks This function must not be called unless the TimlineWidget
@@ -202,6 +204,8 @@ private:
   Cairo::RefPtr playbackPointColour;
   
   gui::widgets::TimelineWidget &timelineWidget;
+  boost::shared_ptr timelineState;      ////////////////////TICKET #796 : should use std::tr1::shared_ptr
+  
 
   friend class Tool;
   friend class ArrowTool;
diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp
index afc395710..650c37912 100644
--- a/src/gui/widgets/timeline/timeline-ruler.cpp
+++ b/src/gui/widgets/timeline/timeline-ruler.cpp
@@ -219,19 +219,27 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
 }
 
 void
-TimelineRuler::on_state_changed()
+TimelineRuler::on_state_changed (shared_ptr newState)
 {
-  if(timelineWidget.get_state())
-    {
-      // Connect up some events
-      view_window().changed_signal().connect(
-        sigc::mem_fun(this, &TimelineRuler::on_update_view) );
-    }
-    
+  REQUIRE (newState);
+  timelineState = newState;
+  
+  propagateStateChange();
+}
+
+void
+TimelineRuler::propagateStateChange()
+{
+  // Connect up some events
+  view_window().changed_signal().connect(
+    sigc::mem_fun(this, &TimelineRuler::on_update_view) );
+  
   // Redraw
   on_update_view();
 }
 
+
+
 void
 TimelineRuler::set_leading_x(const int x)
 {
diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp
index a7c9b0533..1c2cfbb74 100644
--- a/src/gui/widgets/timeline/timeline-ruler.hpp
+++ b/src/gui/widgets/timeline/timeline-ruler.hpp
@@ -29,6 +29,7 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "lib/time/timevalue.hpp"
+#include "gui/widgets/timeline/timeline-state.hpp"
 
 namespace gui {
 namespace widgets {
@@ -108,10 +109,9 @@ private:
   void on_size_allocate(Gtk::Allocation& allocation);
   
   /**
-   * The event handler for when the TimelineWidget's state object is
-   * replaced.
+   * The event handler for when the TimelineWidget's state is switched.
    */
-  void on_state_changed();
+  void on_state_changed (boost::shared_ptr newState);         ////////////////////TICKET #796 : should use std::tr1::shared_ptr
   
 private:
   /* ===== Internal Methods ===== */
@@ -161,7 +161,13 @@ private:
    */
   void draw_playback_point(Cairo::RefPtr cr,
     const Gdk::Rectangle ruler_rect);
-
+  
+  /**
+   * After notification of a timeline state switch
+   * do any local adjustments to adapt to the new state 
+   */
+  void propagateStateChange();
+  
   /**
    * Given the current zoom, this function calculates the preiod
    * between major graduations on the ruler scale.
@@ -233,6 +239,11 @@ private:
    */
   gui::widgets::TimelineWidget &timelineWidget;
   
+  /**
+   * the currently active timeline state object 
+   */
+  boost::shared_ptr timelineState;      ////////////////////TICKET #796 : should use std::tr1::shared_ptr
+  
   /**
    * The caches image of the ruler, over which the chevrons etc. will
    * be drawn.
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index d71220f7c..7cb6c171f 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -90,6 +90,19 @@ TimelineZoomScale::TimelineZoomScale()
   show_all();
 }
 
+void
+TimelineZoomScale::wireTimelineState (TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
+{
+  stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
+}
+
+void
+TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr newState)
+{
+  REQUIRE (newState);
+  UNIMPLEMENTED ("react on the timeline state change");
+}
+
 void
 TimelineZoomScale::on_zoom_in_clicked()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
index 512bea9d8..1514e8b77 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -28,8 +28,11 @@
 
 #include "gui/gtk-lumiera.hpp"
 #include "gui/widgets/mini-button.hpp"
+#include "gui/widgets/timeline-widget.hpp"
 #include "gui/widgets/timeline/timeline-view-window.hpp"
 
+#include 
+
 using namespace Gtk;
 using namespace gui::widgets;
 
@@ -52,9 +55,15 @@ public:
   sigc::signal signal_zoom();
 
   void set_view_window(TimelineViewWindow &view_window);
+  void wireTimelineState (TimelineWidget::TimelineStateChangeSignal);
 
 private:
   /* Event Handlers */
+  
+  /**
+   * 
+   */
+  void on_timeline_state_changed (boost::shared_ptr newState);         ////////////////////TICKET #796 : should use std::tr1::shared_ptr
 
   /**
    * Event handler for when the zoomIn Button

From 5fe1debd5beeefb8dfc671bdea9596cf49daec02 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 10 Oct 2011 01:07:10 +0200
Subject: [PATCH 089/108] ZoomScale: rely on the state change signal for the
 view window

---
 src/gui/panels/timeline-panel.cpp               | 12 ++++++------
 src/gui/panels/timeline-panel.hpp               |  2 +-
 .../widgets/timeline/timeline-zoom-scale.cpp    | 17 ++++++++++++-----
 .../widgets/timeline/timeline-zoom-scale.hpp    | 12 ++++++++----
 4 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index b92554341..970aa21d4 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -112,7 +112,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   panelBar.pack_start(toolbar, PACK_SHRINK);
 
   // Setup tooltips
-  sequenceChooser     .set_tooltip_text(_("Change sequence"));
+  sequenceChooser .set_tooltip_text(_("Change sequence"));
 
   previousButton  .set_tooltip_text(_("To beginning"));
   rewindButton    .set_tooltip_text(_("Rewind"));
@@ -129,15 +129,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager,
   zoomScale       .set_tooltip_text(_("Adjust timeline zoom scale"));
 
   // Setup the timeline widget
-  shared_ptr sequence          ///////////////////////////////TICKET #796
+  shared_ptr sequence          ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost
     = *get_project().get_sequences().begin();  
   timelineWidget.reset(new TimelineWidget(load_state(sequence)));
   pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
 
-  // TimelineWidget is now initialized, lets set it in the zoomScale
-  // and wire it with the timeline state changed signal
-  zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());
-  zoomScale.wireTimelineState (timelineWidget->state_changed_signal());
+  // since TimelineWidget is now initialised,
+  // wire the zoom slider to react on timeline state changes
+  zoomScale.wireTimelineState (timelineWidget->get_state(),
+                               timelineWidget->state_changed_signal());
   
 
   // Set the initial UI state
diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp
index aac000277..8b165dd79 100644
--- a/src/gui/panels/timeline-panel.hpp
+++ b/src/gui/panels/timeline-panel.hpp
@@ -174,7 +174,7 @@ private:
   boost::scoped_ptr timelineWidget;
   
   std::map< boost::weak_ptr,
-    boost::shared_ptr >          ///////////////////////////////TICKET #796
+    boost::shared_ptr >          ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr
     timelineStates;
   
   // Toolbar Widgets
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.cpp b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
index 7cb6c171f..8d084b525 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.cpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.cpp
@@ -91,8 +91,10 @@ TimelineZoomScale::TimelineZoomScale()
 }
 
 void
-TimelineZoomScale::wireTimelineState (TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
+TimelineZoomScale::wireTimelineState (boost::shared_ptr currentState,
+                                      TimelineWidget::TimelineStateChangeSignal stateChangeSignal)
 {
+  on_timeline_state_changed (currentState);
   stateChangeSignal.connect (sigc::mem_fun(this, &TimelineZoomScale::on_timeline_state_changed));
 }
 
@@ -100,7 +102,9 @@ void
 TimelineZoomScale::on_timeline_state_changed (boost::shared_ptr newState)
 {
   REQUIRE (newState);
-  UNIMPLEMENTED ("react on the timeline state change");
+  timelineState = newState;
+  
+  UNIMPLEMENTED ("react on the timeline state change");  ///////////////////////////TODO
 }
 
 void
@@ -129,11 +133,14 @@ TimelineZoomScale::signal_zoom()
   return zoomSignal;
 }
 
-void
-TimelineZoomScale::set_view_window(TimelineViewWindow &view_window)
+TimelineViewWindow&
+TimelineZoomScale::getViewWindow()
 {
-  timelineViewWindow =& view_window;
+  REQUIRE (timelineState, "lifecycle error");
+  return timelineState->get_view_window();
 }
+
+
 int64_t
 TimelineZoomScale::calculate_zoom_scale()
 {
diff --git a/src/gui/widgets/timeline/timeline-zoom-scale.hpp b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
index 1514e8b77..9bf5dde9f 100644
--- a/src/gui/widgets/timeline/timeline-zoom-scale.hpp
+++ b/src/gui/widgets/timeline/timeline-zoom-scale.hpp
@@ -29,6 +29,7 @@
 #include "gui/gtk-lumiera.hpp"
 #include "gui/widgets/mini-button.hpp"
 #include "gui/widgets/timeline-widget.hpp"
+#include "gui/widgets/timeline/timeline-state.hpp"
 #include "gui/widgets/timeline/timeline-view-window.hpp"
 
 #include 
@@ -54,8 +55,8 @@ public:
    */
   sigc::signal signal_zoom();
 
-  void set_view_window(TimelineViewWindow &view_window);
-  void wireTimelineState (TimelineWidget::TimelineStateChangeSignal);
+  void wireTimelineState (boost::shared_ptr currentState,
+                          TimelineWidget::TimelineStateChangeSignal);
 
 private:
   /* Event Handlers */
@@ -83,6 +84,9 @@ private:
    */
   void on_zoom();
 
+  /** access current timeline state */
+  TimelineViewWindow& getViewWindow();
+
   /**
    * Calculate a Zoom Scale value based on
    * the adjustment's current value
@@ -96,14 +100,14 @@ private:
   MiniButton zoomIn;
   MiniButton zoomOut;
 
-protected:
+private:
   /* Signals */
   sigc::signal zoomSignal;
 
   const double smoothing_factor;
   const double button_step_size;
 
-  TimelineViewWindow *timelineViewWindow;
+  boost::shared_ptr timelineState;
 };
 
 } // namespace gui

From fb28592082dc8917e19af7f025a3fd734e15f15b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 10 Oct 2011 01:42:03 +0200
Subject: [PATCH 090/108] more cleanup: use the propagated timelineState
 directly

---
 src/gui/widgets/timeline/timeline-body.cpp  | 54 ++++++++---------
 src/gui/widgets/timeline/timeline-body.hpp  | 20 +++---
 src/gui/widgets/timeline/timeline-ruler.cpp | 67 ++++++++++-----------
 src/gui/widgets/timeline/timeline-ruler.hpp |  4 +-
 4 files changed, 65 insertions(+), 80 deletions(-)

diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index 74fed06f7..3bee2110f 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -71,6 +71,13 @@ TimelineBody::~TimelineBody()
   WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here");
 }
 
+TimelineViewWindow&
+TimelineBody::viewWindow() const
+{
+  REQUIRE(timelineState);
+  return timelineState->get_view_window();
+}
+
 TimelineWidget&
 TimelineBody::getTimelineWidget () const
 {
@@ -149,7 +156,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event)
   // Makes sure the widget styles have been loaded
   read_styles();
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       // Prepare to render via cairo
       const Allocation allocation = get_allocation();
@@ -171,9 +178,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event)
 {
   REQUIRE(event != NULL);
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
-      TimelineViewWindow &window = view_window();
+      TimelineViewWindow &window = viewWindow();
       const Allocation allocation = get_allocation();
       
       if(event->state & GDK_CONTROL_MASK)
@@ -257,17 +264,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
 {
   REQUIRE(event != NULL);
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       // Handle a middle-mouse drag if one is occuring
       switch(dragType)
         {
         case Shift:
           {
-            TimelineViewWindow &window = view_window();
-            
                                    /////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view!
                                    /////////////////////////////            : either encapsulate this entirely here, or leave it to the timeline view!            
+            TimelineViewWindow &window = viewWindow();
             const int64_t scale = window.get_time_scale();
             window.set_time_offset(beginShiftTimeOffset
                                  + TimeValue(scale * (mouseDownX - event->x)));
@@ -308,7 +314,7 @@ TimelineBody::propagateStateChange()
   if (timelineState)
     {
       // Connect up some events
-      view_window().changed_signal().connect(
+      viewWindow().changed_signal().connect(
         sigc::mem_fun(this, &TimelineBody::on_update_view) );
     }
     
@@ -382,7 +388,7 @@ TimelineBody::draw_track(Cairo::RefPtr cr,
 
   // Render the track
   cr->save();
-  TimelineViewWindow &window = view_window();
+  TimelineViewWindow &window = viewWindow();
   timeline_track->draw_track(cr, &window);
   cr->restore();
 }
@@ -395,12 +401,11 @@ TimelineBody::draw_selection(Cairo::RefPtr cr)
   // Prepare
   const Allocation allocation = get_allocation();
   
-  shared_ptr state = timelineWidget.get_state();
-  REQUIRE(state);
+  REQUIRE(timelineState);
   
-  TimelineViewWindow const& window = state->get_view_window();
-  const int start_x = window.time_to_x(state->getSelectionStart());
-  const int end_x   = window.time_to_x(state->getSelectionEnd());
+  TimelineViewWindow const& window = timelineState->get_view_window();
+  const int start_x = window.time_to_x(timelineState->getSelectionStart());
+  const int end_x   = window.time_to_x(timelineState->getSelectionEnd());
   
   // Draw the cover
   if(end_x > 0 && start_x < allocation.get_width())
@@ -436,16 +441,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr)
 {   
   REQUIRE(cr);
   
-  // Prepare
-  
-  shared_ptr state = timelineWidget.get_state();
-  if(state)
+  if (timelineState)
     {
-      if (!state->isPlaying()) return;
+      if (!timelineState->isPlaying()) return;
       
       const Allocation allocation = get_allocation();
-      Time point = state->getPlaybackPoint();
-      const int x = view_window().time_to_x(point);
+      Time point = timelineState->getPlaybackPoint();
+      const int x = viewWindow().time_to_x(point);
         
       // Set source
       cr->set_source(playbackPointColour);
@@ -464,10 +466,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr)
 void
 TimelineBody::begin_shift_drag()
 {
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       dragType = Shift;
-      beginShiftTimeOffset = view_window().get_time_offset();
+      beginShiftTimeOffset = viewWindow().get_time_offset();
       beginShiftVerticalOffset = get_vertical_offset();
     }
 }
@@ -484,14 +486,6 @@ TimelineBody::set_vertical_offset(int offset)
   timelineWidget.verticalAdjustment.set_value(offset);
 }
 
-TimelineViewWindow&
-TimelineBody::view_window() const
-{
-  shared_ptr state = timelineWidget.get_state();
-  REQUIRE(state);
-  return state->get_view_window();
-}
-
 void
 TimelineBody::register_styles() const
 {
diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp
index f2441da9f..aaf90cb2e 100644
--- a/src/gui/widgets/timeline/timeline-body.hpp
+++ b/src/gui/widgets/timeline/timeline-body.hpp
@@ -68,10 +68,7 @@ public:
    */
   TimelineBody(gui::widgets::TimelineWidget &timeline_widget);
   
-  /**
-   * Destructor
-   */
-  ~TimelineBody();
+  virtual ~TimelineBody();
   
   TimelineWidget&
   getTimelineWidget () const;
@@ -131,6 +128,12 @@ protected:
   
   /* ===== Internals ===== */
 private:
+  /**
+   * Access the current timeline view window
+   * @warning must not be called unless the TimlineWidget
+   *          has a valid state.
+   */
+  TimelineViewWindow& viewWindow() const;
 
   /**
    * Draws the timeline tracks.
@@ -162,14 +165,7 @@ private:
   
   /** adjust to the new timeline state */
   void propagateStateChange();
-  
-  /**
-   * A helper function to get the view window
-   * @remarks This function must not be called unless the TimlineWidget
-   * has a valid state.
-   */
-  TimelineViewWindow& view_window() const;
-   
+
   /**
    * Registers all the styles that this class will respond to.
    */
diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp
index 650c37912..03a866482 100644
--- a/src/gui/widgets/timeline/timeline-ruler.cpp
+++ b/src/gui/widgets/timeline/timeline-ruler.cpp
@@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget)
   register_styles();
 }
 
+TimelineViewWindow&
+TimelineRuler::viewWindow() const
+{
+  REQUIRE(timelineState);
+  return timelineState->get_view_window();
+}
+
+
 void
 TimelineRuler::set_mouse_chevron_offset(int offset)
 {
@@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
   if(!window)
     return false;
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       // Prepare to render via cairo      
       const Allocation allocation = get_allocation();
@@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event)
 {
   REQUIRE(event != NULL);
   
-  if(timelineWidget.get_state())
+  if (timelineState)
     {
       if(event->button == 1)
       {
-        pinnedDragTime = view_window().x_to_time(event->x);
+        pinnedDragTime = viewWindow().x_to_time(event->x);
         isDragging = true;
       }
     }
@@ -231,7 +239,7 @@ void
 TimelineRuler::propagateStateChange()
 {
   // Connect up some events
-  view_window().changed_signal().connect(
+  viewWindow().changed_signal().connect(
     sigc::mem_fun(this, &TimelineRuler::on_update_view) );
   
   // Redraw
@@ -243,18 +251,16 @@ TimelineRuler::propagateStateChange()
 void
 TimelineRuler::set_leading_x(const int x)
 {
-  shared_ptr state = timelineWidget.get_state();
-  
-  if(state)
+  if (timelineState)
     {
-      TimeVar newStartPoint (view_window().x_to_time(x));
+      TimeVar newStartPoint (viewWindow().x_to_time(x));
       Offset selectionLength (pinnedDragTime, newStartPoint);
       
       if (newStartPoint > pinnedDragTime)
         newStartPoint=pinnedDragTime; // use the smaller one as selection start
       
-      state->setPlaybackPeriod (Mutation::changeTime(newStartPoint)      );
-      state->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
+      timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint)      );
+      timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength));
                                    //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once
                                                                      ////////////////////TODO        : code duplication with timeline-ibeam-tool 205      
    }
@@ -268,7 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cr,
   REQUIRE(ruler_rect.get_width() > 0);
   REQUIRE(ruler_rect.get_height() > 0);
   
-  const TimelineViewWindow &window = view_window();
+  const TimelineViewWindow &window = viewWindow();
   const gavl_time_t left_offset = _raw(window.get_time_offset());
   const int64_t time_scale = window.get_time_scale();
   
@@ -369,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr cr,
   REQUIRE(cr);
   REQUIRE(ruler_rect.get_width() > 0);
   REQUIRE(ruler_rect.get_height() > 0);
+  REQUIRE(timelineState);
   
-  shared_ptr state = timelineWidget.get_state();
-  REQUIRE(state);
-  const TimelineViewWindow &window = state->get_view_window();
+  const TimelineViewWindow &window = timelineState->get_view_window();
 
   Glib::RefPtr