summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Collignon <loic.collignon@iot.bzh>2018-12-14 14:42:45 +0100
committerLoïc Collignon <loic.collignon@iot.bzh>2018-12-17 16:54:31 +0100
commit87be9daee783c15219f960afb092fecb755ab5fc (patch)
tree4c1088d749fa748316982c56a2b3a351349ebff3
parent22c3fc2ae2ba2125bc3af55ab8e6de4bc4102ac6 (diff)
WIP: adding a master volumesandbox/ctxnop/mastervolume
Create a master volume that binds to the master volume of the real hardware, as set up in the corresponding HAL. Change-Id: I18b02327bb42177c21ed8d9db9f7a7b8adbd87d0 Signed-off-by: Loïc Collignon <loic.collignon@iot.bzh>
-rw-r--r--ahl-binding/ahl-binding.cpp94
-rw-r--r--ahl-binding/ahl-binding.hpp13
-rw-r--r--ahl-binding/interrupt.cpp17
-rw-r--r--ahl-binding/role.cpp125
-rw-r--r--ahl-binding/role.hpp16
-rw-r--r--conf.d/project/etc/policy-4a-sample1.json22
6 files changed, 225 insertions, 62 deletions
diff --git a/ahl-binding/ahl-binding.cpp b/ahl-binding/ahl-binding.cpp
index 98d36b6..1446a81 100644
--- a/ahl-binding/ahl-binding.cpp
+++ b/ahl-binding/ahl-binding.cpp
@@ -89,6 +89,15 @@ void ahl_api_get_roles(afb_req_t req)
}
/**
+ * @brief Callback invoked when clients call the verb 'active'.
+ * @param[in] req Request to handle.
+ */
+void ahl_api_active(afb_req_t req)
+{
+ ahl_binding_t::instance().active(req);
+}
+
+/**
* @brief Callback invoked when clients call the verb 'subscribe'.
* @param[in] req Request to handle.
*/
@@ -181,16 +190,16 @@ int ahl_binding_t::init()
}
AFB_API_NOTICE(handle_, "Required '%s' API found!", HAL_MGR_API);
- afb_api_seal(handle_);
- AFB_API_NOTICE(handle_, "API is now sealed!");
-
- volume_changed_ = afb_api_make_event(handle_, "volume_changed");
+ volume_changed_ = afb_api_make_event(handle_, VOLUME_CHANGED);
if(!afb_event_is_valid(volume_changed_))
{
- AFB_API_ERROR(handle_, "Failed to create the \"volume_changed\" event!");
+ AFB_API_ERROR(handle_, "Failed to create the \"" VOLUME_CHANGED "\" event!");
return -2;
}
+ afb_api_seal(handle_);
+ AFB_API_NOTICE(handle_, "API is now sealed!");
+
if (update_streams()) return -1;
return 0;
}
@@ -202,7 +211,7 @@ int ahl_binding_t::init()
int ahl_binding_t::update_streams()
{
json_object* loaded = nullptr;
- size_t hals_count = 0, streams_count = 0;
+ size_t hals_count = 0, streams_count = 0, playbacks_count = 0;
if (afb_api_call_sync(handle_, "4a-hal-manager", "loaded", json_object_new_object(), &loaded, nullptr, nullptr))
{
@@ -217,7 +226,7 @@ int ahl_binding_t::update_streams()
if (!json_object_is_type(loaded, json_type_array))
{
- AFB_API_ERROR(handle_, "Expected an array from '4a-hal-manager/loaded', but got something else!");
+ AFB_API_ERROR(handle_, "Expected an array from '4a-hal-manager/loaded', but got something else: %s", json_object_to_json_string(loaded));
json_object_put(loaded);
return -1;
}
@@ -260,6 +269,24 @@ int ahl_binding_t::update_streams()
);
}
+ json_object* playbacksJ = nullptr;
+ json_object_object_get_ex(info, "playbacks", &playbacksJ);
+ playbacks_count = json_object_array_length(playbacksJ);
+ for(int j = 0; j < playbacks_count; ++j)
+ {
+ json_object * nameJ = nullptr, * mixerJ = nullptr;
+ json_object * playbackJ = json_object_array_get_idx(playbacksJ, j);
+
+ json_object_object_get_ex(playbackJ, "name", &nameJ);
+ json_object_object_get_ex(playbackJ, "mixer-name", &mixerJ);
+
+ update_stream(
+ halname,
+ json_object_get_string(nameJ),
+ json_object_get_string(mixerJ)
+ );
+ }
+
json_object_put(info);
}
json_object_put(loaded);
@@ -277,7 +304,7 @@ void ahl_binding_t::update_stream(std::string halname, std::string stream, std::
{
for(auto& r : roles_)
{
- if(r.stream() == stream)
+ if(r.resource() == stream)
{
if (r.device_uri().size())
AFB_API_WARNING(handle_, "Multiple stream with same name: '%s'.", stream.c_str());
@@ -311,6 +338,18 @@ void ahl_binding_t::load_static_verbs()
if (afb_api_add_verb(
handle_,
+ "active",
+ "Control the active role",
+ ahl_api_active,
+ nullptr,
+ nullptr,
+ AFB_SESSION_NONE_X2, 0))
+ {
+ throw std::runtime_error("Failed to add 'get_role' verb to the API.");
+ }
+
+ if (afb_api_add_verb(
+ handle_,
"subscribe",
"Subscribe to \"volume_changed\" event",
ahl_api_subscribe,
@@ -450,20 +489,51 @@ void ahl_binding_t::get_roles(afb_req_t req) const
afb_req_success(req, result, nullptr);
}
+void ahl_binding_t::active(afb_req_t req)
+{
+ role_t* activerole = nullptr;
+ for(role_t& r : roles_)
+ {
+ if (r.opened())
+ {
+ if (activerole)
+ {
+ if (activerole->priority() < r.priority())
+ activerole = &r;
+ }
+ else activerole = &r;
+ }
+ }
+
+ if (!activerole)
+ {
+ afb_req_fail(req, "No active role!", nullptr);
+ return;
+ }
+
+ activerole->invoke(req);
+
+ //json_object* response = nullptr;
+ //char* error = nullptr;
+ //char* info = nullptr;
+ //afb_api_call_sync(handle_, HL_API_NAME, activerole->uid().c_str(), afb_req_json(req), &response, &error, &info);
+ //afb_req_reply(req, response, error, info);
+}
+
void ahl_binding_t::subscribe(afb_req_t req) const
{
if (afb_req_subscribe(req, volume_changed_))
- afb_req_fail(req, "Failed to subscribe to \"volume_changed\" event!", nullptr);
+ afb_req_fail(req, "Failed to subscribe to \"" VOLUME_CHANGED "\" event!", nullptr);
else
- afb_req_success(req, nullptr, "Subscribed to \"volume_changed\" event!");
+ afb_req_success(req, nullptr, "Subscribed to \"" VOLUME_CHANGED "\" event!");
}
void ahl_binding_t::unsubscribe(afb_req_t req) const
{
if (afb_req_unsubscribe(req, volume_changed_))
- afb_req_fail(req, "Failed to unsubscribe from \"volume_changed\" event!", nullptr);
+ afb_req_fail(req, "Failed to unsubscribe from \"" VOLUME_CHANGED "\" event!", nullptr);
else
- afb_req_success(req, nullptr, "Unsubscribed from \"volume_changed\" event!");
+ afb_req_success(req, nullptr, "Unsubscribed from \"" VOLUME_CHANGED "\" event!");
}
int ahl_binding_t::emit_volume_changed(const std::string& role, int volume) const
diff --git a/ahl-binding/ahl-binding.hpp b/ahl-binding/ahl-binding.hpp
index 24f4acb..e5a1d05 100644
--- a/ahl-binding/ahl-binding.hpp
+++ b/ahl-binding/ahl-binding.hpp
@@ -17,13 +17,13 @@
* limitations under the License.
*/
+#include <cassert>
#include <exception>
#include <functional>
-#include <sstream>
-#include <vector>
#include <list>
#include <map>
-#include <cassert>
+#include <sstream>
+#include <stack>
#include "config_entry.hpp"
#include "role.hpp"
@@ -31,6 +31,8 @@
#define HL_API_NAME "ahl-4a"
#define HL_API_INFO "Audio high level API for AGL applications"
#define HAL_MGR_API "4a-hal-manager"
+#define VOLUME_CHANGED "volume_changed"
+#define MASTER_CONTROL "agl-master-playback-volume"
#include "afb-binding-common.h"
@@ -42,6 +44,7 @@ private:
afb_api_t handle_;
afb_event_t volume_changed_;
std::vector<role_t> roles_;
+ std::stack<std::string> active_role_;
explicit ahl_binding_t();
@@ -62,6 +65,8 @@ public:
int init();
void event(std::string name, json_object* arg);
void get_roles(afb_req_t req) const;
+ void active(afb_req_t req);
+ void master(afb_req_t req) const;
void subscribe(afb_req_t req) const;
void unsubscribe(afb_req_t req) const;
int emit_volume_changed(const std::string& role, int volume) const;
@@ -72,5 +77,3 @@ public:
void audiorole(afb_req_t req);
int parse_roles_config(json_object* o);
};
-
-
diff --git a/ahl-binding/interrupt.cpp b/ahl-binding/interrupt.cpp
index 5bf0b79..7893d99 100644
--- a/ahl-binding/interrupt.cpp
+++ b/ahl-binding/interrupt.cpp
@@ -65,9 +65,9 @@ int interrupt_t::apply(afb_req_t req, const role_t& role)
AFB_API_DEBUG(ahl_binding_t::instance().handle(),
"Call '%s'/'%s' '%s'",
- r.hal().c_str(), r.stream().c_str(), json_object_to_json_string(arg));
+ r.hal().c_str(), r.resource().c_str(), json_object_to_json_string(arg));
- if(afb_api_call_sync(ahl_binding_t::instance().handle(), r.hal().c_str(), r.stream().c_str(), arg, &result, nullptr, nullptr))
+ if(afb_api_call_sync(ahl_binding_t::instance().handle(), r.hal().c_str(), r.resource().c_str(), arg, &result, nullptr, nullptr))
{
afb_req_fail(req, "Failed to call 'ramp' action on stream", nullptr);
return -1;
@@ -78,7 +78,7 @@ int interrupt_t::apply(afb_req_t req, const role_t& role)
applied_on_.push_back(std::make_tuple<std::string, int>(r.uid(), json_object_get_int(jvolold)));
AFB_API_DEBUG(ahl_binding_t::instance().handle(),
"POLICY: Applying a ramp to '%s' stream because '%s' is opened and have higher priority!",
- r.stream().c_str(), role.stream().c_str());
+ r.resource().c_str(), role.resource().c_str());
}
}
}
@@ -117,13 +117,6 @@ void interrupt_t::clear()
// Create an fake-interrupt, with the old volume
json_object* interrupt = json_tokener_parse(json_object_to_json_string(args_));
json_object_object_add(interrupt, "volume", json_object_new_int(vol)); // Replace the volume
- /*
- json_object* volume = nullptr;
- if (json_object_object_get_ex(interrupt, "volume", &volume))
- {
- json_object_set_int(volume, vol);
- }
- */
json_object* arg = json_object_new_object();
json_object_object_add(arg, "ramp", interrupt);
@@ -131,9 +124,9 @@ void interrupt_t::clear()
AFB_API_DEBUG(ahl_binding_t::instance().handle(),
"Call '%s'/'%s' '%s",
- r.hal().c_str(), r.stream().c_str(), json_object_to_json_string(arg));
+ r.hal().c_str(), r.resource().c_str(), json_object_to_json_string(arg));
- if(afb_api_call_sync(ahl_binding_t::instance().handle(), r.hal().c_str(), r.stream().c_str(), arg, &result, nullptr, nullptr))
+ if(afb_api_call_sync(ahl_binding_t::instance().handle(), r.hal().c_str(), r.resource().c_str(), arg, &result, nullptr, nullptr))
{
AFB_API_ERROR(ahl_binding_t::instance().handle(),
"Failed to call 'ramp' action on '%s'", role.c_str());
diff --git a/ahl-binding/role.cpp b/ahl-binding/role.cpp
index 3a4dd0e..1a5d7ba 100644
--- a/ahl-binding/role.cpp
+++ b/ahl-binding/role.cpp
@@ -22,23 +22,55 @@
using session_t = std::vector<role_t*>;
+inline restype_t fromstring(const std::string& str)
+{
+ if (str == "" || str == "stream") return restype_t::stream;
+ if (str == "playback") return restype_t::playback;
+ if (str == "control") return restype_t::control;
+
+ std::stringstream ss;
+ ss << "Unknown resource type: " << str;
+ throw std::runtime_error(ss.str());
+}
+
role_t::role_t(json_object* j)
{
+ rtype_ = restype_t::stream;
+
jcast(uid_, j, "uid");
jcast(description_, j, "description");
jcast(priority_, j, "priority");
- jcast(stream_, j, "stream");
+ jcast(resource_, j, "resource");
jcast_array(interrupts_, j, "interrupts");
+
+ std::string type;
+ jcast(type, j, "type");
+ rtype_ = fromstring(type);
+
+ // device_uri_ = (rtype_ == restype_t::stream) ? "" : resource_;
+ if (rtype_ == restype_t::control)
+ {
+ device_uri_ = resource_;
+ hal_ = "4a-hal-csl-cm106-8ch-usb";
+ }
opened_ = false;
}
role_t& role_t::operator<<(json_object* j)
{
+ rtype_ = restype_t::stream;
+
jcast(uid_, j, "uid");
jcast(description_, j, "description");
jcast(priority_, j, "priority");
- jcast(stream_, j, "stream");
+ jcast(resource_, j, "resource");
jcast_array(interrupts_, j, "interrupts");
+
+ std::string type;
+ jcast(type, j, "type");
+ rtype_ = fromstring(type);
+
+ // device_uri_ = (rtype_ == restype_t::stream) ? "" : resource_;
return *this;
}
@@ -57,9 +89,14 @@ std::string role_t::hal() const
return hal_;
}
-std::string role_t::stream() const
+std::string role_t::resource() const
{
- return stream_;
+ return resource_;
+}
+
+restype_t role_t::rtype() const
+{
+ return rtype_;
}
int role_t::priority() const
@@ -67,6 +104,11 @@ int role_t::priority() const
return priority_;
}
+const std::vector<interrupt_t>& role_t::interrupts() const
+{
+ return interrupts_;
+}
+
std::string role_t::device_uri() const
{
return device_uri_;
@@ -92,9 +134,14 @@ void role_t::hal(std::string v)
hal_ = v;
}
-void role_t::stream(std::string v)
+void role_t::resource(std::string v)
+{
+ resource_ = v;
+}
+
+void role_t::rtype(restype_t v)
{
- stream_ = v;
+ rtype_ = v;
}
void role_t::priority(int v)
@@ -107,11 +154,6 @@ void role_t::device_uri(std::string v)
device_uri_ = v;
}
-const std::vector<interrupt_t>& role_t::interrupts() const
-{
- return interrupts_;
-}
-
int role_t::apply_policy(afb_req_t req)
{
return interrupts_.size() ? interrupts_[0].apply(req, *this) : 0;
@@ -158,7 +200,11 @@ void role_t::open(afb_req_t r, json_object* o)
return;
}
- if (!apply_policy(r))
+ if (rtype_ != restype_t::stream)
+ {
+ afb_req_fail(r, "Only stream resources can be opened!", nullptr);
+ }
+ else if (!apply_policy(r))
{
afb_req_context(r,
0, // Do not replace previous context if any
@@ -180,7 +226,7 @@ void role_t::open(afb_req_t r, json_object* o)
afb_api_call(
api,
role->hal_.c_str(),
- role->stream_.c_str(),
+ role->resource_.c_str(),
a,
NULL,
NULL);
@@ -229,11 +275,13 @@ void role_t::close(afb_req_t r, json_object* o)
}
void role_t::mute(afb_req_t r, json_object* o) {
- do_mute(r, true);
+ if (rtype_ != restype_t::stream) afb_req_fail(r, "Only stream resource can be muted!", nullptr);
+ else do_mute(r, true);
}
void role_t::unmute(afb_req_t r, json_object *o) {
- do_mute(r, false);
+ if (rtype_ != restype_t::stream) afb_req_fail(r, "Only stream resource can be unmuted!", nullptr);
+ else do_mute(r, false);
}
void role_t::do_mute(afb_req_t r, bool v) {
@@ -245,7 +293,7 @@ void role_t::do_mute(afb_req_t r, bool v) {
afb_api_call(
api,
hal_.c_str(),
- stream_.c_str(),
+ resource_.c_str(),
a,
[](void* closure, json_object* result, const char* error, const char* info, afb_api_t handle)
{
@@ -262,7 +310,7 @@ void role_t::do_mute(afb_req_t r, bool v) {
struct volumeclosure
{
- std::string role;
+ role_t* role;
afb_req_t req;
};
@@ -300,32 +348,57 @@ void role_t::volume(afb_req_t r, json_object* o)
json_object_get(value);
json_object* a = json_object_new_object();
- json_object_object_add(a, "volume", value);
+ json_object_object_add(a, (rtype_ == restype_t::control ? "value" : "volume"), value);
+
+ AFB_API_DEBUG(api, "VOLUME QUERY: %s", json_object_to_json_string(a));
volumeclosure* userdata = new volumeclosure();
- userdata->role = uid_;
+ userdata->role = this;
userdata->req = afb_req_addref(r);
afb_api_call(
api,
hal_.c_str(),
- stream_.c_str(),
+ resource_.c_str(),
a,
[](void* closure, json_object* result, const char* error, const char* info, afb_api_t handle)
{
AFB_API_DEBUG(handle, "Got the following answer: %s", json_object_to_json_string(result));
volumeclosure* r = reinterpret_cast<volumeclosure*>(closure);
- json_object_get(result);
- if (error) afb_req_fail(r->req, json_object_to_json_string(result), nullptr);
+ if (error)
+ {
+ json_object_get(result);
+ afb_req_fail(r->req, json_object_to_json_string(result), nullptr);
+ }
else
{
- json_object* volnew;
- if (json_object_object_get_ex(result, "volnew", &volnew))
+ if (r->role->rtype() == restype_t::control)
+ {
+ int average = 0;
+ size_t arraylen = json_object_array_length(result);
+ for (size_t i = 0; i < arraylen; ++i)
+ {
+ average += json_object_get_int(json_object_array_get_idx(result, i));
+ }
+ average = average / (int)arraylen;
+
+ json_object* response = json_object_new_object();
+ json_object* newvol = json_object_new_int(average);
+ json_object_object_add(response, "newvol", newvol);
+ ahl_binding_t::instance().emit_volume_changed(r->role->uid(), average);
+ afb_req_success(r->req, response, nullptr);
+ }
+ else
{
- ahl_binding_t::instance().emit_volume_changed(r->role, json_object_get_int(volnew));
+ json_object* volnew;
+ if (json_object_object_get_ex(result, "volnew", &volnew))
+ {
+ ahl_binding_t::instance().emit_volume_changed(r->role->uid(), json_object_get_int(volnew));
+ }
+ json_object_get(result);
+ afb_req_success(r->req, result, nullptr);
}
- afb_req_success(r->req, result, nullptr);
}
afb_req_unref(r->req);
delete r;
diff --git a/ahl-binding/role.hpp b/ahl-binding/role.hpp
index 055a405..5623f83 100644
--- a/ahl-binding/role.hpp
+++ b/ahl-binding/role.hpp
@@ -21,6 +21,13 @@
#include "interrupt.hpp"
#include "afb-binding-common.h"
+enum class restype_t
+{
+ stream,
+ playback,
+ control
+};
+
class role_t
{
private:
@@ -28,9 +35,10 @@ private:
std::string uid_;
std::string description_;
std::string hal_;
- std::string stream_;
+ std::string resource_;
int priority_;
std::vector<interrupt_t> interrupts_;
+ restype_t rtype_;
std::string device_uri_;
bool opened_ = false;
@@ -58,7 +66,8 @@ public:
std::string uid() const;
std::string description() const;
std::string hal() const;
- std::string stream() const;
+ std::string resource() const;
+ restype_t rtype() const;
int priority() const;
const std::vector<interrupt_t>& interrupts() const;
std::string device_uri() const;
@@ -67,7 +76,8 @@ public:
void uid(std::string v);
void description(std::string v);
void hal(std::string v);
- void stream(std::string v);
+ void resource(std::string v);
+ void rtype(restype_t v);
void device_uri(std::string v);
void priority(int v);
diff --git a/conf.d/project/etc/policy-4a-sample1.json b/conf.d/project/etc/policy-4a-sample1.json
index 673caa7..2cc44a6 100644
--- a/conf.d/project/etc/policy-4a-sample1.json
+++ b/conf.d/project/etc/policy-4a-sample1.json
@@ -14,22 +14,36 @@
"events": [],
"roles":[
{
+ "uid": "master-control",
+ "description": "Master volume using control",
+ "priority": 0,
+ "resource": "agl-master-playback-volume",
+ "type": "control"
+ },
+ {
+ "uid": "master-playback",
+ "description": "Master volume using playback",
+ "priority": 0,
+ "resource": "playback",
+ "type": "playback"
+ },
+ {
"uid": "radio",
"description": "Radio (tuner)",
"priority": 0,
- "stream": "radio_stream"
+ "resource": "radio_stream"
},
{
"uid": "multimedia",
"description": "Multimedia content (e.g. media player, etc.)",
"priority": 0,
- "stream": "multimedia"
+ "resource": "multimedia"
},
{
"uid": "emergency",
"description": "Safety-relevant or critical alerts/alarms",
"priority": 100,
- "stream": "emergency",
+ "resource": "emergency",
"interrupts":[
{"type": "ramp", "args": { "uid": "ramp-slow", "volume": 30} }
]
@@ -39,7 +53,7 @@
"name": "navigation",
"description": "Navigation instructions (GPS, turn directions, etc...)",
"priority": 25,
- "stream": "navigation",
+ "resource": "navigation",
"interrupts":[
{"type": "ramp", "args": { "uid": "ramp-slow", "volume": 30} }
]