summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2016-10-01 05:46:29 -0400
committerDavid Robillard <d@drobilla.net>2016-10-02 12:24:57 -0400
commit0f50e9239bbda77ce38f297870f8cf4158025acc (patch)
tree89e255ade0257ca3f1adf9d1e9d8a81195b45d15
parent9b8bce71893ef450992f82a28a6a0287c479baaf (diff)
downloadingen-0f50e9239bbda77ce38f297870f8cf4158025acc.tar.gz
ingen-0f50e9239bbda77ce38f297870f8cf4158025acc.tar.bz2
ingen-0f50e9239bbda77ce38f297870f8cf4158025acc.zip
Show audio and load information in status line
-rw-r--r--bundles/ingen.lv2/ingen.ttl59
-rw-r--r--ingen/URIs.hpp5
-rw-r--r--ingen/ingen.h5
-rw-r--r--src/URIs.cpp5
-rw-r--r--src/gui/App.cpp81
-rw-r--r--src/gui/App.hpp21
-rw-r--r--src/gui/GraphBox.cpp14
-rw-r--r--src/gui/GraphBox.hpp3
-rw-r--r--src/gui/Port.cpp37
-rw-r--r--src/gui/rgba.hpp58
-rw-r--r--src/server/Engine.cpp63
-rw-r--r--src/server/Engine.hpp55
-rw-r--r--src/server/GraphImpl.cpp3
-rw-r--r--src/server/RunContext.hpp15
-rw-r--r--src/server/events/Get.cpp12
15 files changed, 381 insertions, 55 deletions
diff --git a/bundles/ingen.lv2/ingen.ttl b/bundles/ingen.lv2/ingen.ttl
index 3f7f89f5..d1a4cc2c 100644
--- a/bundles/ingen.lv2/ingen.ttl
+++ b/bundles/ingen.lv2/ingen.ttl
@@ -48,6 +48,34 @@ ingen:canvasY
rdfs:label "canvas Y" ;
rdfs:comment "The Y coordinate of an item on a canvas." .
+ingen:maxEventLoad
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:range xsd:decimal ;
+ rdfs:label "maximum event load" ;
+ rdfs:comment "The maximum fraction of a cycle spent processing events." .
+
+ingen:minRunLoad
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:range xsd:decimal ;
+ rdfs:label "minimum run load" ;
+ rdfs:comment "The minimum fraction of a cycle spent running DSP." .
+
+ingen:maxRunLoad
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:range xsd:decimal ;
+ rdfs:label "maximum run load" ;
+ rdfs:comment "The maximum fraction of a cycle spent running DSP." .
+
+ingen:meanRunLoad
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:range xsd:decimal ;
+ rdfs:label "mean run load" ;
+ rdfs:comment "The average fraction of a cycle spent running DSP." .
+
ingen:block
a rdf:Property ,
owl:ObjectProperty ;
@@ -220,3 +248,34 @@ ingen:BundleEnd
a rdfs:Class ;
rdfs:label "Bundle End" ;
rdfs:comment "The end of an undo transaction." .
+
+ingen:Option
+ a rdfs:Class ;
+ rdfs:subClassOf rdf:Property ;
+ rdfs:label "Ingen Option" .
+
+ingen:shortSwitch
+ a rdf:Property ,
+ owl:ObjectProperty ,
+ owl:FunctionalProperty ;
+ rdfs:domain ingen:Option ;
+ rdfs:range xsd:string ;
+ rdfs:label "short switch" ;
+ rdfs:comment "Single character switch for short command line argument." .
+
+ingen:longSwitch
+ a rdf:Property ,
+ owl:ObjectProperty ,
+ owl:FunctionalProperty ;
+ rdfs:domain ingen:Option ;
+ rdfs:range xsd:string ;
+ rdfs:label "long switch" ;
+ rdfs:comment "Lowercase, hyphenated switch for long command line argument." .
+
+ingen:numThreads
+ a rdf:Property ,
+ owl:ObjectProperty ,
+ ingen:Option ;
+ rdfs:label "number of threads" ;
+ ingen:shortSwitch "p" ;
+ ingen:longSwitch "threads" .
diff --git a/ingen/URIs.hpp b/ingen/URIs.hpp
index 9fc771da..5f448e02 100644
--- a/ingen/URIs.hpp
+++ b/ingen/URIs.hpp
@@ -118,6 +118,11 @@ public:
const Quark ingen_head;
const Quark ingen_incidentTo;
const Quark ingen_loadedBundle;
+ const Quark ingen_maxEventLoad;
+ const Quark ingen_maxRunLoad;
+ const Quark ingen_meanRunLoad;
+ const Quark ingen_minRunLoad;
+ const Quark ingen_numThreads;
const Quark ingen_polyphonic;
const Quark ingen_polyphony;
const Quark ingen_prototype;
diff --git a/ingen/ingen.h b/ingen/ingen.h
index 3af2042c..e4a36ade 100644
--- a/ingen/ingen.h
+++ b/ingen/ingen.h
@@ -58,6 +58,11 @@
#define INGEN__head INGEN_NS "head"
#define INGEN__incidentTo INGEN_NS "incidentTo"
#define INGEN__loadedBundle INGEN_NS "loadedBundle"
+#define INGEN__maxEventLoad INGEN_NS "maxEventLoad"
+#define INGEN__maxRunLoad INGEN_NS "maxRunLoad"
+#define INGEN__meanRunLoad INGEN_NS "meanRunLoad"
+#define INGEN__minRunLoad INGEN_NS "minRunLoad"
+#define INGEN__numThreads INGEN_NS "numThreads"
#define INGEN__polyphonic INGEN_NS "polyphonic"
#define INGEN__polyphony INGEN_NS "polyphony"
#define INGEN__prototype INGEN_NS "prototype"
diff --git a/src/URIs.cpp b/src/URIs.cpp
index b272e48e..4b771473 100644
--- a/src/URIs.cpp
+++ b/src/URIs.cpp
@@ -101,6 +101,11 @@ URIs::URIs(Forge& f, URIMap* map, LilvWorld* lworld)
, ingen_head (forge, map, lworld, INGEN__head)
, ingen_incidentTo (forge, map, lworld, INGEN__incidentTo)
, ingen_loadedBundle (forge, map, lworld, INGEN__loadedBundle)
+ , ingen_maxEventLoad (forge, map, lworld, INGEN__maxEventLoad)
+ , ingen_maxRunLoad (forge, map, lworld, INGEN__maxRunLoad)
+ , ingen_meanRunLoad (forge, map, lworld, INGEN__meanRunLoad)
+ , ingen_minRunLoad (forge, map, lworld, INGEN__minRunLoad)
+ , ingen_numThreads (forge, map, lworld, INGEN__numThreads)
, ingen_polyphonic (forge, map, lworld, INGEN__polyphonic)
, ingen_polyphony (forge, map, lworld, INGEN__polyphony)
, ingen_prototype (forge, map, lworld, INGEN__prototype)
diff --git a/src/gui/App.cpp b/src/gui/App.cpp
index c023fe8c..a3055c91 100644
--- a/src/gui/App.cpp
+++ b/src/gui/App.cpp
@@ -51,6 +51,7 @@
#include "ThreadedLoader.hpp"
#include "WidgetFactory.hpp"
#include "WindowFactory.hpp"
+#include "rgba.hpp"
using namespace std;
@@ -74,6 +75,11 @@ App::App(Ingen::World* world)
, _window_factory(new WindowFactory(*this))
, _world(world)
, _sample_rate(48000)
+ , _block_length(1024)
+ , _n_threads(1)
+ , _max_event_load(0.0f)
+ , _min_run_load(0.0f)
+ , _max_run_load(0.0f)
, _enable_signal(true)
, _requested_plugins(false)
, _is_plugin(false)
@@ -193,6 +199,8 @@ App::attach(SPtr<SigClientInterface> client)
sigc::mem_fun(this, &App::response));
_client->signal_error().connect(
sigc::mem_fun(this, &App::error_message));
+ _client->signal_put().connect(
+ sigc::mem_fun(this, &App::put));
_client->signal_property_change().connect(
sigc::mem_fun(this, &App::property_change));
}
@@ -271,18 +279,77 @@ App::set_tooltip(Gtk::Widget* widget, const LilvNode* node)
}
void
+App::put(const Raul::URI& uri,
+ const Resource::Properties& properties,
+ Resource::Graph ctx)
+{
+ _enable_signal = false;
+ for (const auto& p : properties) {
+ property_change(uri, p.first, p.second);
+ }
+ _enable_signal = true;
+ _status_text = status_text();
+ signal_status_text_changed.emit(_status_text);
+}
+
+void
App::property_change(const Raul::URI& subject,
const Raul::URI& key,
const Atom& value)
{
- if (subject == Raul::URI("ingen:/engine") && key == uris().param_sampleRate) {
- if (value.type() == forge().Int) {
- log().info(fmt("Sample rate: %1%\n") % uris().forge.str(value));
- _sample_rate = value.get<int32_t>();
- } else {
- log().error("Engine sample rate property is not an integer\n");
- }
+ if (subject != Raul::URI("ingen:/engine")) {
+ return;
+ } else if (key == uris().param_sampleRate && value.type() == forge().Int) {
+ _sample_rate = value.get<int32_t>();
+ } else if (key == uris().bufsz_maxBlockLength && value.type() == forge().Int) {
+ _block_length = value.get<int32_t>();
+ } else if (key == uris().ingen_numThreads && value.type() == forge().Int) {
+ _n_threads = value.get<int>();
+ } else if (key == uris().ingen_maxEventLoad && value.type() == forge().Float) {
+ _max_event_load = value.get<float>();
+ } else if (key == uris().ingen_minRunLoad && value.type() == forge().Float) {
+ _min_run_load = value.get<float>();
+ } else if (key == uris().ingen_meanRunLoad && value.type() == forge().Float) {
+ _mean_run_load = value.get<float>();
+ } else if (key == uris().ingen_maxRunLoad && value.type() == forge().Float) {
+ _max_run_load = value.get<float>();
+ } else {
+ _world->log().warn(fmt("Unknown engine property %1%\n") % key);
+ return;
}
+
+ if (_enable_signal) {
+ _status_text = status_text();
+ signal_status_text_changed.emit(_status_text);
+ }
+}
+
+static std::string
+fraction_label(float f)
+{
+ static const uint32_t GREEN = 0x4A8A0EFF;
+ static const uint32_t RED = 0x960909FF;
+
+ const uint32_t col = rgba_interpolate(GREEN, RED, std::min(f, 1.0f));
+ char col_str[8];
+ snprintf(col_str, sizeof(col_str), "%02X%02X%02X",
+ RGBA_R(col), RGBA_G(col), RGBA_B(col));
+ return (fmt("<span color='#%s'>%d%%</span>") % col_str % (f * 100)).str();
+}
+
+std::string
+App::status_text() const
+{
+ return (fmt("<b>Audio:</b> %2.1f kHz / %.1f ms <b>Load: </b> %s events + %s <b>DSP:</b> %s ≤ %s ≤ %s")
+ % (_sample_rate / 1e3f)
+ % (_block_length * 1e3f / (float)_sample_rate)
+ % fraction_label(_max_event_load)
+ % ((_n_threads == 1)
+ ? "1 thread"
+ : (fmt("%1% threads") % _n_threads).str())
+ % fraction_label(_min_run_load)
+ % fraction_label(_mean_run_load)
+ % fraction_label(_max_run_load)).str();
}
void
diff --git a/src/gui/App.hpp b/src/gui/App.hpp
index 6071c750..61336747 100644
--- a/src/gui/App.hpp
+++ b/src/gui/App.hpp
@@ -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
@@ -25,6 +25,7 @@
#include <gtkmm/window.h>
#include "ingen/Atom.hpp"
+#include "ingen/Resource.hpp"
#include "ingen/Status.hpp"
#include "ingen/World.hpp"
#include "ingen/ingen.h"
@@ -125,8 +126,13 @@ public:
SPtr<Serialiser> serialiser();
static SPtr<App> create(Ingen::World* world);
+
void run();
+ std::string status_text() const;
+
+ sigc::signal<void, const std::string&> signal_status_text_changed;
+
inline Ingen::World* world() const { return _world; }
inline Ingen::URIs& uris() const { return _world->uris(); }
inline Ingen::Log& log() const { return _world->log(); }
@@ -137,6 +143,10 @@ protected:
bool animate();
void response(int32_t id, Ingen::Status status, const std::string& subject);
+ void put(const Raul::URI& uri,
+ const Resource::Properties& properties,
+ Resource::Graph ctx);
+
void property_change(const Raul::URI& subject,
const Raul::URI& key,
const Atom& value);
@@ -158,7 +168,14 @@ protected:
Ingen::World* _world;
- uint32_t _sample_rate;
+ int32_t _sample_rate;
+ int32_t _block_length;
+ int32_t _n_threads;
+ float _max_event_load;
+ float _mean_run_load;
+ float _min_run_load;
+ float _max_run_load;
+ std::string _status_text;
typedef std::map<Port*, bool> ActivityPorts;
ActivityPorts _activity_ports;
diff --git a/src/gui/GraphBox.cpp b/src/gui/GraphBox.cpp
index 10f063cc..692e378a 100644
--- a/src/gui/GraphBox.cpp
+++ b/src/gui/GraphBox.cpp
@@ -179,6 +179,10 @@ GraphBox::GraphBox(BaseObjectType* cobject,
Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get();
clipboard->signal_owner_change().connect(
sigc::mem_fun(this, &GraphBox::event_clipboard_changed));
+
+ _status_label = Gtk::manage(new Gtk::Label("STATUS"));
+ _status_bar->pack_start(*_status_label, false, true, 0);
+ _status_label->show();
}
GraphBox::~GraphBox()
@@ -228,6 +232,16 @@ GraphBox::init_box(App& app)
_breadcrumbs = new BreadCrumbs(*_app);
_breadcrumbs->signal_graph_selected.connect(
sigc::mem_fun(this, &GraphBox::set_graph_from_path));
+
+ _status_label->set_markup(app.status_text());
+ app.signal_status_text_changed.connect(
+ sigc::mem_fun(*this, &GraphBox::set_status_text));
+}
+
+void
+GraphBox::set_status_text(const std::string& text)
+{
+ _status_label->set_markup(text);
}
void
diff --git a/src/gui/GraphBox.hpp b/src/gui/GraphBox.hpp
index 0a55227e..af17a1ac 100644
--- a/src/gui/GraphBox.hpp
+++ b/src/gui/GraphBox.hpp
@@ -73,6 +73,8 @@ public:
void init_box(App& app);
+ void set_status_text(const std::string& text);
+
void set_graph(SPtr<const Client::GraphModel> graph,
SPtr<GraphView> view);
@@ -186,6 +188,7 @@ private:
Gtk::Alignment* _alignment;
BreadCrumbs* _breadcrumbs;
Gtk::Statusbar* _status_bar;
+ Gtk::Label* _status_label;
Gtk::HPaned* _doc_paned;
Gtk::ScrolledWindow* _doc_scrolledwindow;
diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp
index 1a9750a6..c643b379 100644
--- a/src/gui/Port.cpp
+++ b/src/gui/Port.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
@@ -33,6 +33,7 @@
#include "WidgetFactory.hpp"
#include "WindowFactory.hpp"
#include "ingen_config.h"
+#include "rgba.hpp"
using namespace Ingen::Client;
using namespace std;
@@ -339,38 +340,6 @@ Port::on_event(GdkEvent* ev)
return false;
}
-/* Peak colour stuff */
-
-static inline uint32_t
-rgba_to_uint(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
-{
- return ((((uint32_t)(r)) << 24) |
- (((uint32_t)(g)) << 16) |
- (((uint32_t)(b)) << 8) |
- (((uint32_t)(a))));
-}
-
-static inline uint8_t
-mono_interpolate(uint8_t v1, uint8_t v2, float f)
-{
- return ((int)rint((v2) * (f) + (v1) * (1 - (f))));
-}
-
-#define RGBA_R(x) (((uint32_t)(x)) >> 24)
-#define RGBA_G(x) ((((uint32_t)(x)) >> 16) & 0xFF)
-#define RGBA_B(x) ((((uint32_t)(x)) >> 8) & 0xFF)
-#define RGBA_A(x) (((uint32_t)(x)) & 0xFF)
-
-static inline uint32_t
-rgba_interpolate(uint32_t c1, uint32_t c2, float f)
-{
- return rgba_to_uint(
- mono_interpolate(RGBA_R(c1), RGBA_R(c2), f),
- mono_interpolate(RGBA_G(c1), RGBA_G(c2), f),
- mono_interpolate(RGBA_B(c1), RGBA_B(c2), f),
- mono_interpolate(RGBA_A(c1), RGBA_A(c2), f));
-}
-
inline static uint32_t
peak_color(float peak)
{
@@ -386,8 +355,6 @@ peak_color(float peak)
}
}
-/* End peak colour stuff */
-
void
Port::activity(const Atom& value)
{
diff --git a/src/gui/rgba.hpp b/src/gui/rgba.hpp
new file mode 100644
index 00000000..8648aece
--- /dev/null
+++ b/src/gui/rgba.hpp
@@ -0,0 +1,58 @@
+/*
+ This file is part of Ingen.
+ 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
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen 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 Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INGEN_GUI_RGBA_HPP
+#define INGEN_GUI_RGBA_HPP
+
+#include <math.h>
+
+namespace Ingen {
+namespace GUI {
+
+static inline uint32_t
+rgba_to_uint(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ return ((((uint32_t)(r)) << 24) |
+ (((uint32_t)(g)) << 16) |
+ (((uint32_t)(b)) << 8) |
+ (((uint32_t)(a))));
+}
+
+static inline uint8_t
+mono_interpolate(uint8_t v1, uint8_t v2, float f)
+{
+ return ((int)rint((v2) * (f) + (v1) * (1 - (f))));
+}
+
+#define RGBA_R(x) (((uint32_t)(x)) >> 24)
+#define RGBA_G(x) ((((uint32_t)(x)) >> 16) & 0xFF)
+#define RGBA_B(x) ((((uint32_t)(x)) >> 8) & 0xFF)
+#define RGBA_A(x) (((uint32_t)(x)) & 0xFF)
+
+static inline uint32_t
+rgba_interpolate(uint32_t c1, uint32_t c2, float f)
+{
+ return rgba_to_uint(
+ mono_interpolate(RGBA_R(c1), RGBA_R(c2), f),
+ mono_interpolate(RGBA_G(c1), RGBA_G(c2), f),
+ mono_interpolate(RGBA_B(c1), RGBA_B(c2), f),
+ mono_interpolate(RGBA_A(c1), RGBA_A(c2), f));
+}
+
+} // namespace GUI
+} // namespace Ingen
+
+#endif // INGEN_GUI_RGBA_HPP
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());
}