From 04db5655f13fd0dc3cf84b6fa39a3ae7f76c663b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 31 Dec 2010 05:59:53 +0100 Subject: [PATCH] Implementation: building a simple time grid --- src/lib/error.hpp | 3 +- src/lib/exception.cpp | 4 +- src/lib/time/display.hpp | 49 +++++++++++++++++ src/lib/time/lumitime.cpp | 22 ++++++++ src/lib/time/timevalue.hpp | 6 +++ src/proc/asset/meta.cpp | 13 ++--- src/proc/asset/meta.hpp | 45 ++++++++++++++-- src/proc/asset/meta/time-grid.cpp | 85 ++++++++++++++++++++++++++---- src/proc/asset/meta/time-grid.hpp | 58 ++++++++++++++++---- src/proc/assetmanager.cpp | 58 ++++++++++---------- tests/lib/time/time-value-test.cpp | 2 +- 11 files changed, 285 insertions(+), 60 deletions(-) create mode 100644 src/lib/time/display.hpp diff --git a/src/lib/error.hpp b/src/lib/error.hpp index af911ac7a..51fce33e0 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -125,7 +125,8 @@ namespace lumiera { 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 - LUMIERA_ERROR_DECLARE (UNCONNECTED); ///< missing connection + LUMIERA_ERROR_DECLARE (UNCONNECTED); ///< missing connection + LUMIERA_ERROR_DECLARE (UNIMPLEMENTED);///< unimplemented feature diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp index bd19d30f6..82e228687 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -82,7 +82,9 @@ namespace lumiera { 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"); - LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); + LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); + LUMIERA_ERROR_DEFINE (UNIMPLEMENTED, "using a feature not yet implemented...."); + } // namespace error diff --git a/src/lib/time/display.hpp b/src/lib/time/display.hpp new file mode 100644 index 000000000..2c829d9fc --- /dev/null +++ b/src/lib/time/display.hpp @@ -0,0 +1,49 @@ +/* + DISPLAY.hpp - formatting of time values for display + + Copyright (C) Lumiera.org + 2010, 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 LIB_TIME_DISPLAY_H +#define LIB_TIME_DISPLAY_H + +#include "lib/time/timecode.hpp" + +#include +#include + + + +namespace lib { +namespace time { + + + /** writes time value, formatted as HH:MM:SS:mmm + * @see lumiera_tmpbuf_print_time */ + inline std::ostream& + operator<< (std::ostream& os, Time const& t) + { + return os << std::string(t); + } + + + +}} // lib::time +#endif diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index 8d9bba707..072165aa5 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -30,6 +30,7 @@ extern "C" { #include #include +#include using std::string; @@ -37,6 +38,18 @@ using std::string; namespace lib { namespace time { + namespace { + /** implementation detail: convert a rational number denoting fractionalSeconds + * into the internal time scale used by GAVL and Lumiera internal time values. + */ + inline gavl_time_t + rational2time (TimeFract const& fractionalSeconds) + { + return boost::rational_cast (GAVL_TIME_SCALE * fractionalSeconds); + } + } + + const Time Time::MAX ( TimeValue (+std::numeric_limits::max()) ); const Time Time::MIN ( TimeValue (-std::numeric_limits::max()) ); @@ -60,6 +73,15 @@ namespace time { { } + /** convenience constructor to build an Time value + * from a fraction of seconds, given as rational number. + * An example would be to the time unit of a framerate. + */ + Time::Time (TimeFract const& fractionalSeconds) + : TimeValue(rational2time (fractionalSeconds)) + { } + + /** displaying an internal Lumiera Time value * for diagnostic purposes or internal reporting. * @warning internal Lumiera time values refer to an diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 5e0e6f28a..9f2888588 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -25,6 +25,7 @@ #define LIB_TIME_TIMEVALUE_H #include +#include #include #include @@ -182,6 +183,9 @@ namespace time { /* ======= specific Time entities ==================== */ + /** rational representation of fractional seconds */ + typedef boost::rational TimeFract; + /** * Lumiera's internal time value datatype. * This is a TimeValue, but now more specifically denoting @@ -219,6 +223,8 @@ namespace time { : TimeValue(calcResult) { } + Time (TimeFract const& fractionalSeconds); + Time ( long millis , uint secs , uint mins =0 diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp index 9ac77cac1..09a771068 100644 --- a/src/proc/asset/meta.cpp +++ b/src/proc/asset/meta.cpp @@ -38,7 +38,7 @@ namespace asset { namespace meta { - Descriptor::~Descriptor() { } // emit vtable here... + Descriptor::~Descriptor() { } // emit VTable here... } @@ -54,8 +54,8 @@ namespace asset { * created as a side effect of calling the concrete Meta subclass ctor. */ template - P - MetaFactory::operator() (EntryID elementIdentity) + meta::Builder + MetaFactory::operator () (EntryID elementIdentity) { UNIMPLEMENTED ("Meta-Factory"); } @@ -70,7 +70,7 @@ namespace asset { * created as a side effect of calling the concrete Meta subclass ctor. */ template - P + meta::Builder MetaFactory::operator() (Descriptor const& prototype, EntryID elementIdentity) { UNIMPLEMENTED ("Meta-Factory"); @@ -96,10 +96,11 @@ namespace asset { namespace asset { using meta::Descriptor; + using meta::Builder; using meta::TimeGrid; - template P MetaFactory::operator() (EntryID); + template Builder MetaFactory::operator() (EntryID); - template P MetaFactory::operator() (Descriptor const&, EntryID); + template Builder MetaFactory::operator() (Descriptor const&, EntryID); } // namespace asset diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index cc0f569a2..21f029c14 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -22,14 +22,34 @@ /** @file meta.hpp - ** Internal and organisational metadata. Some internally created data elements, - ** like automation data sets, inventory of session contents, can be exposed and - ** treated as specific Kind of Asset. + ** Internal and organisational metadata. Some internally created data elements + ** rather serve the purpose of controlling the way the application behaves, as + ** opposed to organising the \link struct.hpp structure \endlink of the data the + ** user works with. Lumiera exposes this self-referential control and customisation + ** aspects as a special kind of Asset. Examples being types, scales and quantisation + ** grids, decision rules, control data stores (automation data), annotations attached + ** to labels, inventory entities etc. ** ** For the different Kinds of Assets, we use sub-interfaces inheriting ** from the general Asset interface. To be able to get asset::Meta instances ** directly from the AssetManager, we define a specialisation of the Asset ID. ** + ** \par using meta assets + ** The usage pattern of asset::Meta entities differs from the other assets, + ** insofar they aren't created as individual entries, rather added as part + ** of a larger scale configuration activity, or they are derived from category. + ** The latter fits in with a prototype-like approach; initially, the individual + ** entry just serves to keep track of a categorisation, while at some point, + ** such a link into a describing category may evolve into a local + ** differentiation of some settings (copy on modification). + ** + ** To cope with this special usage, the meta assets are defined to be immutable. + ** They are created from a descriptor, which stands for a category or sub-category + ** and can be another already existing asset::Meta (inheriting from meta::Descriptor) + ** Generally this yields a Builder object, which can be used for outfitting the new + ** or changed metadata entry, finally \em committing this builder to yield a new + ** asset::Meta (which, in case of a mutation, might superseede an existin one). + ** ** @see asset.hpp explanation of assets in general ** @see MetaFactory creating concrete asset::Meta instances ** @@ -71,6 +91,21 @@ namespace asset { public: virtual ~Descriptor(); ///< this is an ABC }; + + /** + * Building and configuring a meta asset. + * The finished elements are defined to be immutable, + * thus, on creation or when changing / superseding a + * meta asset, the client gets a special builder instance, + * which is a value object for configuring the specific details + * to set. When done, the client invokes a \c commit() function, + * which yields a smart-ptr to the new meta asset. + * Individual meta asset subclasses are bound to define a + * specialisation of this Builder template, which will then be + * instantiated and provided by the MetaFactory. + */ + template + struct Builder; } @@ -116,10 +151,10 @@ namespace asset { typedef P PType; template - P operator() (EntryID elementIdentity); + meta::Builder operator() (EntryID elementIdentity); template - P operator() (meta::Descriptor const& prototype, EntryID elementIdentity); + meta::Builder operator() (meta::Descriptor const& prototype, EntryID elementIdentity); }; diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index e969bf645..f7e1e6d5e 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -23,29 +23,94 @@ #include "proc/asset/meta/time-grid.hpp" #include "proc/asset/entry-id.hpp" +#include "proc/assetmanager.hpp" +//#include "lib/time/quantiser.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/display.hpp" //#include "lib/util.hpp" //#include "include/logging.h" +#include +#include + +using boost::format; +using boost::str; +using std::string; + namespace asset { namespace meta { + namespace error = lumiera::error; + namespace { - typedef TimeGrid::Builder Builder; - - Asset::Ident - deriveAssetIdentity (Builder const& setup) - { - EntryID wtf("wtf**ck"); //////////////////////TODO some real meat here - return wtf.getIdent(); - } + // Implementation details (still required???) } /** */ - TimeGrid::TimeGrid (Builder const& setup) - : Meta (deriveAssetIdentity (setup)) + TimeGrid::TimeGrid (EntryID const& nameID) + : Meta (nameID.getIdent()) { } + + using lib::time::Time; + using lib::time::TimeValue; + using lib::time::TimeSpan; + using lib::time::Duration; + using lib::time::Offset; + + /** + * TimeGrid implementation: a trivial time grid, + * starting at a given point in time and using a + * constant grid spacing + */ + class SimpleTimeGrid + : public TimeGrid + { + TimeSpan timeBase_; + + // TODO implement the TimeGrid API here + + public: + SimpleTimeGrid (Time start, Duration gridSpacing, EntryID const& name) + : TimeGrid (name) + , timeBase_(start,gridSpacing) + { } + }; + + + + /** Setup of a TimeGrid: validate the settings configured into this builder instance, + * then decide on the implementation strategy for the time grid. Convert the given + * frames per second into an appropriate gridSpacing time and build a suitable + * name-ID to denote the TimeGrid-meta-Asset to be built. + * @return shared_ptr holding onto the new asset::Meta, which has already been + * registered with the AssetManager. + * @throw error::Config in case of invalid frames-per-second. The AssetManager + * might raise further exception when asset registration fails. + * @todo currently (12/2010) the AssetManager is unable to detect duplicate assets. + * Later on the intention is that in such cases, instead of creating a new grid + * we'll silently return the already registered exisiting and equivalent grid. + */ + P + Builder::commit() + { + if (predecessor_) + throw error::Invalid("compound and variable time grids are a planned feature" + , error::LUMIERA_ERROR_UNIMPLEMENTED); + if (!fps_) + throw error::Config ("attempt to build a TimeGrid with 0 frames per second"); + + Time spacing = 1 / fps_; // seconds per frame + + format gridIdFormat("grid_%f_%s"); + string name = str(gridIdFormat % fps_ % origin_); + EntryID nameID (name); + TimeGrid& newGrid (*new SimpleTimeGrid(origin_, Offset(spacing), nameID)); + + return AssetManager::instance().wrap (newGrid); + } + }} // namespace asset::meta diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 50e6d382f..3dcf8d6aa 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -34,6 +34,11 @@ ** Time grid specifications are integrated into Lumiera's framework ** for meta assets, automation, configuration and similar metadata. ** + ** \par using time grids + ** TimeGrid is an interface (ABC), but provides some actual factory + ** functions, which can be used as a convenience shortcut to fabricate + ** the kind of simple time grid used most often. + ** ** @see MetaFactory creating concrete asset::Meta instances ** @see time::Quantiser ** @@ -43,14 +48,14 @@ #define ASSET_META_TIME_GRID_H #include "proc/asset/meta.hpp" -#include "lib/time/quantiser.hpp" +#include "lib/time/timevalue.hpp" namespace asset { namespace meta { - + /** * Interface: a grid and scale for time quantisation. * This meta-Asset describes a coordinate system or @@ -61,17 +66,52 @@ namespace meta { { public: - struct Builder - { - - }; + + // TODO define the TimeGrid API here + protected: - friend class Builder; - - TimeGrid (Builder const& setup); //////////////TODO + TimeGrid (EntryID const&); }; + + + + using lib::time::Time; + using lib::time::TimeValue; + using lib::time::TimeFract; + + + template<> + struct Builder + { + TimeFract fps_; + Time origin_; + + /** when building a compound or variable grid, + * the predecessor is the grid active \em before + * the origin of this (local) grid. + * @todo currently not supported (as of 12/2010) + */ + P predecessor_; + + /** + * initialise to blank (zero). + * You need at least to set the framerate, + * in order to create a usable TimeGrid + */ + Builder() + : fps_(0) + , origin_(TimeValue(0)) + , predecessor_() + { } + + /** create a time grid + * based on settings within this builder + */ + P commit(); + + }; }} // namespace asset::meta #endif diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 0cf1a989c..6677b0cca 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -52,7 +52,7 @@ namespace asset { IDErr (const char* eID, format fmt) : lumiera::error::Invalid(fmt.str(),eID) {} }; - + // ------pre-defined-common-error-cases--------------- // @@ -88,8 +88,8 @@ namespace asset { AssetManager::AssetManager () : registry (Singleton() ()) { } - - + + /** provide the unique ID for given Asset::Ident tuple */ ID @@ -117,7 +117,7 @@ namespace asset { DB::Lock guard(®istry); TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); - + registry.put (asset_id, smart_ptr); return asset_id; } @@ -155,9 +155,9 @@ namespace asset { return static_pointer_cast (instance().registry.get (asset.id)); } - - - + + + /** * @return true if the given id is registered in the internal asset DB */ @@ -167,10 +167,10 @@ namespace asset { return ( registry.get (ID(id)) ); } // query most general Asset ID-kind and use implicit // conversion from smart-ptr to bool (test if empty) - - + + /** - * @return true if the given id is registered with the given Category + * @return true if the given id is registered with the given Category */ bool AssetManager::known (IDA id, const Category& cat) @@ -178,23 +178,23 @@ namespace asset { PAsset pA = registry.get (id); return ( pA && pA->ident.category.isWithin(cat)); } - + namespace { // details implementing AssetManager::remove - + void recursive_call (AssetManager* instance, PAsset& pA) { instance->remove (pA->getID()); } - + function - detach_child_recursively () ///< @return a functor recursively invoking remove(child) + detach_child_recursively () ///< @return a functor recursively invoking remove(child) { return bind( &recursive_call, &AssetManager::instance(), _1 ); } } - + /** * remove the given asset from the internal DB * together with all its dependents @@ -203,11 +203,11 @@ namespace asset { AssetManager::remove (IDA id) { PAsset asset = getAsset (id); - for_each (asset->dependants, detach_child_recursively()); ///// + for_each (asset->dependants, detach_child_recursively()); asset->unlink(); registry.del(id); } - + list @@ -218,7 +218,7 @@ namespace asset { res.sort(); return res; } - + } // namespace asset @@ -229,15 +229,16 @@ namespace asset { /* explicit template instantiations for various Asset Kinds */ /************************************************************/ -#include "proc/asset/media.hpp" -#include "proc/asset/clip.hpp" -#include "proc/asset/proc.hpp" -#include "proc/asset/struct.hpp" -#include "proc/asset/pipe.hpp" -#include "proc/asset/meta.hpp" -#include "proc/asset/procpatt.hpp" -#include "proc/asset/timeline.hpp" -#include "proc/asset/sequence.hpp" +#include "proc/asset/media.hpp" +#include "proc/asset/clip.hpp" +#include "proc/asset/proc.hpp" +#include "proc/asset/struct.hpp" +#include "proc/asset/pipe.hpp" +#include "proc/asset/meta.hpp" +#include "proc/asset/procpatt.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/sequence.hpp" +#include "proc/asset/meta/time-grid.hpp" namespace asset { @@ -260,5 +261,8 @@ namespace asset { template P AssetManager::wrap (const Timeline& asset); template P AssetManager::wrap (const Sequence& asset); + using meta::TimeGrid; + template P AssetManager::wrap (const TimeGrid& asset); + } // namespace asset diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index a56d0cd93..ee7e207f7 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -201,7 +201,7 @@ namespace test{ TimeValue zero; TimeValue five(5); - TimeSpan interval (Time(org), Duration(Offset (org,five))); + TimeSpan interval (Time(org), Duration(Offset (org,five))); /////////////TODO need more convenient constructors // the time span behaves like a time CHECK (org == interval);