diff options
author | David Robillard <d@drobilla.net> | 2016-10-01 05:46:29 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2016-10-02 12:24:57 -0400 |
commit | 0f50e9239bbda77ce38f297870f8cf4158025acc (patch) | |
tree | 89e255ade0257ca3f1adf9d1e9d8a81195b45d15 /src/server | |
parent | 9b8bce71893ef450992f82a28a6a0287c479baaf (diff) | |
download | ingen-0f50e9239bbda77ce38f297870f8cf4158025acc.tar.gz ingen-0f50e9239bbda77ce38f297870f8cf4158025acc.tar.bz2 ingen-0f50e9239bbda77ce38f297870f8cf4158025acc.zip |
Show audio and load information in status line
Diffstat (limited to 'src/server')
-rw-r--r-- | src/server/Engine.cpp | 63 | ||||
-rw-r--r-- | src/server/Engine.hpp | 55 | ||||
-rw-r--r-- | src/server/GraphImpl.cpp | 3 | ||||
-rw-r--r-- | src/server/RunContext.hpp | 15 | ||||
-rw-r--r-- | src/server/events/Get.cpp | 12 |
5 files changed, 137 insertions, 11 deletions
diff --git a/src/server/Engine.cpp b/src/server/Engine.cpp index 1a0ab0a0..893f6f92 100644 --- a/src/server/Engine.cpp +++ b/src/server/Engine.cpp @@ -83,9 +83,11 @@ Engine::Engine(Ingen::World* world) , _worker(new Worker(world->log(), event_queue_size())) , _sync_worker(new Worker(world->log(), event_queue_size(), true)) , _listener(NULL) + , _cycle_start_time(0) , _rand_engine(0) , _uniform_dist(0.0f, 1.0f) , _quit_flag(false) + , _reset_load_flag(false) , _direct_driver(true) , _atomic_bundles(world->conf().option("atomic-bundles").get<int32_t>()) { @@ -264,8 +266,29 @@ Engine::quit() bool Engine::main_iteration() { + const Ingen::URIs& uris = world()->uris(); + _post_processor->process(); _maid->cleanup(); + + if (_event_load.changed) { + _broadcaster->set_property(Raul::URI("ingen:/engine"), + uris.ingen_maxEventLoad, + uris.forge.make(_event_load.max / 100.0f)); + _event_load.changed = false; + } + + if (_run_load.changed) { + _broadcaster->put(Raul::URI("ingen:/engine"), + { { uris.ingen_meanRunLoad, + uris.forge.make(floorf(_run_load.mean) / 100.0f) }, + { uris.ingen_minRunLoad, + uris.forge.make(_run_load.min / 100.0f) }, + { uris.ingen_maxRunLoad, + uris.forge.make(_run_load.max / 100.0f) } }); + _run_load.changed = false; + } + return !_quit_flag; } @@ -275,6 +298,7 @@ Engine::set_driver(SPtr<Driver> driver) _driver = driver; for (RunContext* ctx : _run_contexts) { ctx->set_priority(driver->real_time_priority()); + ctx->set_rate(driver->sample_rate()); } } @@ -285,14 +309,31 @@ Engine::event_time() return 0; } - const SampleCount start = _direct_driver + // FIXME: Jitter with direct driver + const SampleCount now = _direct_driver ? run_context().start() : _driver->frame_time(); - /* Exactly one cycle latency (some could run ASAP if we get lucky, but not - always, and a slight constant latency is far better than jittery lower - (average) latency */ - return start + _driver->block_length(); + return now + _driver->block_length(); +} + +uint64_t +Engine::current_time(const RunContext& context) const +{ + struct timespec time; +#ifdef CLOCK_MONOTONIC_RAW + clock_gettime(CLOCK_MONOTONIC_RAW, &time); +#else + clock_gettime(CLOCK_MONOTONIC, &time); +#endif + + return (uint64_t)time.tv_sec * 1e6 + (uint64_t)time.tv_nsec / 1e3; +} + +void +Engine::reset_load() +{ + _reset_load_flag = true; } void @@ -367,6 +408,7 @@ unsigned Engine::run(uint32_t sample_count) { RunContext& ctx = run_context(); + _cycle_start_time = current_time(ctx); // Apply control bindings to input control_bindings()->pre_process( @@ -377,6 +419,7 @@ Engine::run(uint32_t sample_count) // Process events that came in during the last cycle // (Aiming for jitter-free 1 block event latency, ideally) const unsigned n_processed_events = process_events(); + const uint64_t t_events = current_time(ctx); // Run root graph if (_root_graph) { @@ -387,6 +430,16 @@ Engine::run(uint32_t sample_count) ctx, _root_graph->port_impl(1)->buffer(0).get()); } + // Update load for this cycle + if (ctx.duration() > 0) { + _event_load.update(t_events - _cycle_start_time, ctx.duration()); + _run_load.update(current_time(ctx) - t_events, ctx.duration()); + if (_reset_load_flag) { + _run_load = Load(); + _reset_load_flag = false; + } + } + return n_processed_events; } diff --git a/src/server/Engine.hpp b/src/server/Engine.hpp index 2d5e053e..fc80d64b 100644 --- a/src/server/Engine.hpp +++ b/src/server/Engine.hpp @@ -89,8 +89,27 @@ public: void set_driver(SPtr<Driver> driver); + /** Return the frame time to execute an event that arrived now. + * + * This aims to return a time one cycle from "now", so that events ideally + * have 1 cycle of latency with no jitter. + */ SampleCount event_time(); + /** Return the time this cycle began processing in microseconds. + * + * This value is comparable to the value returned by current_time(). + */ + inline uint64_t cycle_start_time(const RunContext& context) const { + return _cycle_start_time; + } + + /** Return the current time in microseconds. */ + uint64_t current_time(const RunContext& context) const; + + /** Reset the load statistics (when the expected DSP load changes). */ + void reset_load(); + /** Enqueue an event to be processed (non-realtime threads only). */ void enqueue_event(Event* ev, Event::Mode mode=Event::Mode::NORMAL); @@ -128,11 +147,41 @@ public: SPtr<Store> store() const; size_t event_queue_size() const; - bool atomic_bundles() const { return _atomic_bundles; } + size_t n_threads() const { return _run_contexts.size(); } + bool atomic_bundles() const { return _atomic_bundles; } private: Ingen::World* _world; + struct Load { + void update(uint64_t time, uint64_t available) { + const uint64_t load = time * 100 / available; + if (load < min) { + min = load; + changed = true; + } + if (load > max) { + max = load; + changed = true; + } + if (++n == 1) { + mean = load; + } else { + const float a = mean + ((float)load - mean) / (float)++n; + if (a != mean) { + changed = floorf(a) != floorf(mean); + mean = a; + } + } + } + + uint64_t min = std::numeric_limits<uint64_t>::max(); + uint64_t max = 0; + float mean = 0.0f; + uint64_t n = 0; + bool changed = false; + }; + BlockFactory* _block_factory; Broadcaster* _broadcaster; BufferFactory* _buffer_factory; @@ -153,6 +202,9 @@ private: SocketListener* _listener; std::vector<RunContext*> _run_contexts; + uint64_t _cycle_start_time; + Load _event_load; + Load _run_load; std::mt19937 _rand_engine; std::uniform_real_distribution<float> _uniform_dist; @@ -161,6 +213,7 @@ private: std::mutex _tasks_mutex; bool _quit_flag; + bool _reset_load_flag; bool _direct_driver; bool _atomic_bundles; }; diff --git a/src/server/GraphImpl.cpp b/src/server/GraphImpl.cpp index 22276a34..bdd0a69a 100644 --- a/src/server/GraphImpl.cpp +++ b/src/server/GraphImpl.cpp @@ -1,6 +1,6 @@ /* This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> + Copyright 2007-2016 David Robillard <http://drobilla.net/> Ingen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free @@ -302,6 +302,7 @@ GraphImpl::set_compiled_graph(CompiledGraph* cg) { if (_compiled_graph && _compiled_graph != cg) { _engine.maid()->dispose(_compiled_graph); + _engine.reset_load(); } _compiled_graph = cg; } diff --git a/src/server/RunContext.hpp b/src/server/RunContext.hpp index 803e0c3b..eba30545 100644 --- a/src/server/RunContext.hpp +++ b/src/server/RunContext.hpp @@ -87,6 +87,18 @@ public: /** Return true iff any notifications are pending. */ bool pending_notifications() const { return _event_sink->read_space(); } + /** Return the duration of this cycle in microseconds. + * + * This is the cycle length in frames (nframes) converted to microseconds, + * that is, the amount of real time that this cycle's audio represents. + * Note that this is unrelated to the amount of time available to execute a + * cycle (other than the fact that it must be processed in significantly + * less time to avoid a dropout when running in real time). + */ + inline uint64_t duration() const { + return (uint64_t)_nframes * 1e6 / _rate; + } + inline void locate(FrameTime s, SampleCount nframes) { _start = s; _end = s + nframes; @@ -103,6 +115,7 @@ public: } void set_priority(int priority); + void set_rate(SampleCount rate) { _rate = rate; } inline Engine& engine() const { return _engine; } inline Task* task() const { return _task; } @@ -112,6 +125,7 @@ public: inline FrameTime end() const { return _end; } inline SampleCount offset() const { return _offset; } inline SampleCount nframes() const { return _nframes; } + inline SampleCount rate() const { return _rate; } inline bool realtime() const { return _realtime; } protected: @@ -129,6 +143,7 @@ protected: FrameTime _end; ///< End frame of this cycle, timeline relative SampleCount _offset; ///< Offset into data buffers SampleCount _nframes; ///< Number of frames past offset to process + SampleCount _rate; ///< Sample rate in Hz bool _realtime; ///< True iff context is hard realtime bool _copy; ///< True iff this is a copy (shared event_sink) }; diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp index fa56f23a..bec57104 100644 --- a/src/server/events/Get.cpp +++ b/src/server/events/Get.cpp @@ -1,6 +1,6 @@ /* This file is part of Ingen. - Copyright 2007-2015 David Robillard <http://drobilla.net/> + Copyright 2007-2016 David Robillard <http://drobilla.net/> Ingen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free @@ -90,10 +90,14 @@ Get::post_process() } else if (_uri == "ingen:/engine") { // TODO: Keep a proper RDF model of the engine URIs& uris = _engine.world()->uris(); - _request_client->set_property( + _request_client->put( Raul::URI("ingen:/engine"), - uris.param_sampleRate, - uris.forge.make(int32_t(_engine.driver()->sample_rate()))); + { { uris.param_sampleRate, + uris.forge.make(int32_t(_engine.driver()->sample_rate())) }, + { uris.bufsz_maxBlockLength, + uris.forge.make(int32_t(_engine.driver()->block_length())) }, + { uris.ingen_numThreads, + uris.forge.make(int32_t(_engine.n_threads())) } }); } else { _response.send(_request_client.get()); } |