From fafad52781050c8660bd41886627e50045c54740 Mon Sep 17 00:00:00 2001 From: Kazumasa Mitsunari Date: Wed, 27 Jun 2018 15:47:56 +0900 Subject: Add new APIs High level API * stream_open * stream_close * set_stream_state Get functions * getListMainSinks * getListMainSources * getListMainConnections New event * stream_state_event Related Change: https://gerrit.automotivelinux.org/gerrit/gitweb?p=apps/agl-service-soundmanager-2017.git;a=commit;h=a5da9bcbc5b3eb9a23cb97c8d3ef711c713e6c83 v2: Consolidate the coding style into camelCase. Change-Id: I65b6159ffce57d71588c6edd7bed9d5b0a17d19a Signed-off-by: Kazumasa Mitsunari --- src/libsoundmanager.cpp | 459 +++++++++++++++++++++++++++++++++++++++++------- src/libsoundmanager.hpp | 33 ++-- 2 files changed, 414 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/libsoundmanager.cpp b/src/libsoundmanager.cpp index 9eeac92..ebc3fa3 100644 --- a/src/libsoundmanager.cpp +++ b/src/libsoundmanager.cpp @@ -41,13 +41,18 @@ static const std::vector api_list{ std::string("volumeStep"), std::string("setSinkMuteState"), std::string("getListMainConnections"), + std::string("getListMainSources"), + std::string("getListMainSinks"), std::string("ackConnect"), std::string("ackDisconnect"), std::string("ackSetSourceState"), std::string("registerSource"), std::string("deregisterSource"), std::string("subscribe"), - std::string("unsubscribe") + std::string("unsubscribe"), + std::string("stream_open"), + std::string("stream_close"), + std::string("set_stream_state") }; static const std::vector event_list{ @@ -59,27 +64,28 @@ static const std::vector event_list{ std::string("mainConnectionStateChanged"), std::string("setRoutingReady"), std::string("setRoutingRundown"), - std::string("asyncConnect") + std::string("asyncConnect"), + std::string("stream_state_event") }; -static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj) +static void _onHangupStatic(void *closure, struct afb_wsj1 *wsj) { - static_cast(closure)->on_hangup(NULL,wsj); + static_cast(closure)->_onHangup(NULL,wsj); } -static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) +static void _onCallStatic(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) { /* Soundmanager is not called from other process */ } -static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg) +static void _onEventStatic(void* closure, const char* event, struct afb_wsj1_msg *msg) { - static_cast(closure)->on_event(NULL,event,msg); + static_cast(closure)->_onEvent(NULL,event,msg); } -static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg) +static void _onReplyStatic(void *closure, struct afb_wsj1_msg *msg) { - static_cast(closure)->on_reply(NULL,msg); + static_cast(closure)->_onReply(NULL,msg); } Soundmanager::Soundmanager() @@ -98,7 +104,6 @@ Soundmanager::~Soundmanager() } } - /** * This function is initialization function * @@ -126,13 +131,13 @@ int Soundmanager::init(int port, const string& token) return -1; } - ret = initialize_websocket(); + ret = initializeWebsocket(); if(ret != 0 ) { ELOG("Failed to initialize websocket"); return -1; } - ret = init_event(); + ret = initEvent(); if(ret != 0 ) { ELOG("Failed to initialize websocket"); @@ -141,7 +146,7 @@ int Soundmanager::init(int port, const string& token) return 0; } -int Soundmanager::initialize_websocket() +int Soundmanager::initializeWebsocket() { mploop = NULL; onEvent = nullptr; @@ -154,9 +159,9 @@ int Soundmanager::initialize_websocket() } /* Initialize interface from websocket */ { - minterface.on_hangup = _on_hangup_static; - minterface.on_call = _on_call_static; - minterface.on_event = _on_event_static; + minterface.on_hangup = _onHangupStatic; + minterface.on_call = _onCallStatic; + minterface.on_event = _onEventStatic; string muri = "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this); } @@ -175,7 +180,11 @@ END: return -1; } -int Soundmanager::init_event(){ +struct sd_event* Soundmanager::getEventLoop(){ + return mploop; +} + +int Soundmanager::initEvent(){ /* subscribe most important event for sound right */ return subscribe(string("asyncSetSourceState")); } @@ -209,25 +218,37 @@ void Soundmanager::registerCallback( * registerSource is registration as source for policy management * * #### Parameters - * - sourceName [in] : This argument should be specified to the source name (e.g. "MediaPlayer") + * - audio_role [in] : This argument should be specified to the source name (e.g. "MediaPlayer" which is role) * * #### Return * - Returns 0 on success or -1 in case of transmission error. * * #### Note * This function must be called to get source ID - * mainConnectionID is returned by async reply function + * sourceID is returned by async reply function. + * { + * "response":{ + * "verb":"registerSource", + * "error":0, + * "sourceID":101 + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"OK" + * } + * * */ -int Soundmanager::registerSource(const string& sourceName) +int Soundmanager::registerSource(const string& audio_role) { if(!sp_websock) { return -1; } struct json_object* j_obj = json_object_new_object(); - struct json_object* jsn = json_object_new_string(sourceName.c_str()); - json_object_object_add(j_obj, "appname", jsn); + struct json_object* jsn = json_object_new_string(audio_role.c_str()); + json_object_object_add(j_obj, "audio_role", jsn); return this->call(__FUNCTION__, j_obj); } @@ -244,18 +265,30 @@ int Soundmanager::registerSource(const string& sourceName) * * #### Note * This function must be called to get source right - * connectionID is returned by reply event - * + * connectionID is returned by reply event. + * Response is like here. + * { + * "response":{ + * "verb":"connect", + * "error":0, + * "mainConnectionID":2 + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"OK" + * } + * } */ -int Soundmanager::connect(int sourceID, int sinkID) +int Soundmanager::connect(int source_id, int sink_id) { if(!sp_websock) { return -1; } struct json_object* j_obj = json_object_new_object(); - struct json_object* jsource = json_object_new_int(sourceID); - struct json_object* jsink = json_object_new_int(sinkID); + struct json_object* jsource = json_object_new_int(source_id); + struct json_object* jsink = json_object_new_int(sink_id); json_object_object_add(j_obj, "sourceID", jsource); json_object_object_add(j_obj, "sinkID", jsink); return this->call(__FUNCTION__, j_obj); @@ -264,12 +297,12 @@ int Soundmanager::connect(int sourceID, int sinkID) /** * This function calls the connect of Audio Manager via WebSocket * This function is overload of connect - * Instead of sinkID as the number, abstract sinkName will be supported. + * Instead of sinkID as the number, abstract sink_name will be supported. * For now, "default" is only supported * * #### Parameters - * - sourceID [in] : This argument should be specified to the sourceID as int. This parameter is returned value of registerSource - * - sinkName [in] : This argument should be specified to the sinkID as int. ID is aliased by SoundManager (e.g: "default") + * - source_id [in] : This argument should be specified to the sourceID as int. This parameter is returned value of registerSource + * - sink_name [in] : This argument should be specified to the sinkID as string. ID is aliased by SoundManager (e.g: "default") * * #### Rreturn * - Returns 0 on success or -1 in case of transmission error. @@ -279,16 +312,16 @@ int Soundmanager::connect(int sourceID, int sinkID) * Just "default" is usable. * */ -int Soundmanager::connect(int sourceID, const string& sinkName) +int Soundmanager::connect(int source_id, const string& sink_name) { if(!sp_websock) { return -1; } struct json_object* j_obj = json_object_new_object(); - struct json_object* jsource = json_object_new_int(sourceID); + struct json_object* jsource = json_object_new_int(source_id); //struct json_object* jsink = json_object_new_int(1); - struct json_object* jsink = json_object_new_string(sinkName.c_str()); + struct json_object* jsink = json_object_new_string(sink_name.c_str()); json_object_object_add(j_obj, "sourceID", jsource); json_object_object_add(j_obj, "sinkID", jsink); return this->call(__FUNCTION__, j_obj); @@ -298,23 +331,34 @@ int Soundmanager::connect(int sourceID, const string& sinkName) * This function calls the disconnect of Audio Manager via WebSocket * * #### Parameters - * - connectionID [in] : This parameter is returned value of connect + * - connection_id [in] : This parameter is returned value of connect * * #### Return * - Returns 0 on success or -1 in case of transmission error. * * #### Note - * + * Response is like here + * { + * "response":{ + * "verb":"disconnect", + * "error":0 + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"OK" + * } + * } * */ -int Soundmanager::disconnect(int connectionID) +int Soundmanager::disconnect(int connection_id) { if(!sp_websock) { return -1; } struct json_object* j_obj = json_object_new_object(); - struct json_object* jconnection = json_object_new_int(connectionID); + struct json_object* jconnection = json_object_new_int(connection_id); json_object_object_add(j_obj, "mainConnectionID", jconnection); return this->call(__FUNCTION__, j_obj); } @@ -323,7 +367,6 @@ int Soundmanager::disconnect(int connectionID) * This function calls the ackSetSourceState of Audio Manager via WebSocket * * #### Parameters - * - sourceID [in] : This parameter is returned value of ackSetSourceState * - handle [in] : This parameter is returned value of ackSetSourceState * - errno [in] : If you have some errors, input ohter than 0. 0 means acknowledge * @@ -348,6 +391,303 @@ int Soundmanager::ackSetSourceState(int handle, int error) return this->call(__FUNCTION__, j_obj); } +/** + * This function calls the getListMainSources of Audio Manager via WebSocket. + * Get source list like here. + * { + * "response":{ + * "verb":"getListMainSources", + * "sources":[ + * { + * "sourceID":102, + * "sourceName":"radio", + * "availability":1, + * "availabilityReason":0, + * "sourceClassID":101 + * } + * ] + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"Success to get main source list" + * } + * + * + * #### Parameters + * NULL + * + * #### Return + * - Returns 0 on success or -1 in case of transmission error. + * + * #### Note + */ +int Soundmanager::getListMainSources(){ + if(!sp_websock) + { + return -1; + } + return this->call(__FUNCTION__, json_object_new_object()); +} + +/** + * This function calls the getListMainSinks of Audio Manager via WebSocket. + * Get sink list like here. + * { + * "response":{ + * "verb":"getListMainSinks", + * "sinks":[ + * { + * "sinkID":1, + * "sinkName":"rsnd-dai.0-ak4642-hifi#Analog#Stereo", + * "availability":1, + * "availabilityReason":0, + * "volume":100, + * "muteState":2, + * "sinkClassID":101 + * }, + * { + * "sinkID":2, + * "sinkName":"Microchip#MOST:0#Multichannel", + * "availability":1, + * "availabilityReason":0, + * "volume":100, + * "muteState":2, + * "sinkClassID":101 + * } + * ] + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"Success to get main sink list", + * "uuid":"ba34fdc7-5c9e-436e-90ca-62abe6a7c4d3" + * } + * + * #### Parameters + * NULL + * + * #### Return + * - Returns 0 on success or -1 in case of transmission error. + * + * #### Note + */ +int Soundmanager::getListMainSinks(){ + if(!sp_websock) + { + return -1; + } + return this->call(__FUNCTION__, json_object_new_object()); +} + +/** + * This function calls the getListMainConnections of Audio Manager via WebSocket. + * Connection is established with success of "connect". + * Get main connection list like here + * { + * "response":{ + * "verb":"getListMainConnections", + * "connections":[ + * { + * "mainConnectionID":1, + * "sourceID":101, + * "sinkID":1, + * "delay":-1, + * "connectionState":2 + * } + * ] + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"Success to get main connection list" + * } + * } + * + * #### Parameters + * NULL + * + * #### Return + * - Returns 0 on success or -1 in case of transmission error. + * + * #### Note + */ +int Soundmanager::getListMainConnections(){ + if(!sp_websock) + { + return -1; + } + return this->call(__FUNCTION__, json_object_new_object()); +} + +/** + * This function calls the stream_open via WebSocket. + * stream_id which is same meaning as sourceID is returned with success. + * Sound Manager bypasses registerSource. + * + * #### Parameters + * - audio_role [in] : This argument should be specified to the source name + * - endpoint_id [in] : This argument should be specified to the sinkID as int. ID is specified by AudioManager + * + * #### Return + * - Returns 0 on success or -1 in case of transmission error. + * + * #### Note + * Response is like here + * { + * "response":{ + * "verb":"stream_open", + * "error":0, + * "stream_id":102 + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"OK" + * } + * } + * + */ +int Soundmanager::streamOpen(const std::string& audio_role, int endpoint_id){ + if(!sp_websock) + { + return -1; + } + struct json_object* j_obj = json_object_new_object(); + struct json_object* jsource = json_object_new_string(audio_role.c_str()); + struct json_object* jsink = json_object_new_int(endpoint_id); + json_object_object_add(j_obj, "audio_role", jsource); + json_object_object_add(j_obj, "endpoint_id", jsink); + return this->call("stream_open", j_obj); +} + +/** + * This function is overload function. + * "default" is only supported for now. + */ +int Soundmanager::streamOpen(const std::string& audio_role, const std::string& endpoint_id){ + if(!sp_websock) + { + return -1; + } + struct json_object* j_obj = json_object_new_object(); + struct json_object* jsource = json_object_new_string(audio_role.c_str()); + struct json_object* jsink = json_object_new_string(endpoint_id.c_str()); + json_object_object_add(j_obj, "audio_role", jsource); + json_object_object_add(j_obj, "endpoint_id", jsink); + return this->call("stream_open", j_obj); +} + +/** + * This function calls the stream_close via WebSocket + * + * #### Parameters + * - stream_id [in] : This parameter is returned value of stream_open + * + * #### Return + * - Returns 0 on success or -1 in case of transmission error. + * + * #### Note + * Response is like here + * { + * "response":{ + * "verb":"stream_close", + * "error":0 + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"OK" + * } + * } + * + */ +int Soundmanager::streamClose(int stream_id){ + if(!sp_websock) + { + return -1; + } + struct json_object* j_obj = json_object_new_object(); + struct json_object* jstream = json_object_new_int(stream_id); + json_object_object_add(j_obj, "stream_id", jstream); + return this->call("stream_close", j_obj); +} + +/** + * This function calls set_stream_state via WebSocket. + * Sound Manager bypasses connect/disconnect judging with mute state. + * If mute is 0, which means unmute, Sound Manager calls connect, but + * Sound Manager doesn't return mainConnectionID because Sound Manager stores + * and remember mainConnectionID for stream_id inside itself using afb-binder function. + * If mute is 1, which means mute, Sound Manager calls disconnect. + * + * After set_source_state with unmute(call connect), + * "stream_state_event" is published. + * This event is same as "asyncSetSourceState" and means authorization to output sound. + * + * The json message of "stream_state_event" is like here. + * { + * "event":"soundmanager\/stream_state_event", + * "data":{ + * "handle":2, + * "sourceID":101, + * "sourceState":"on", + * "event_name":"ahl_stream_state_event", + * "stream_id":101, + * "state_event":1 + * }, + * "jtype":"afb-event" + * } + * + * stream_id is same as sourceID. + * state_event is same as sourceState but type is different. + * + * This event is same as asyncSetSourceState but application doesn't have to call + * "ackSetSourceState" because Sound Manager send it to Audio MAnager on behalf of applicaiton. + * if the application uses high level API. + * + * Not recommend to confuse connect/disconnect with set_stream_state + * and confuse registerSource with stream_open even if those are same meanings because + * the processes of those functions inside Sound Manager are a little different. + * + * #### Parameters + * - streamID [in] : This parameter is returned value of stream_open + * + * #### Return + * - Returns 0 on success or -1 in case of transmission error. + * + * #### Note + * This function must be called to get source right. + * If an application uses "connect" instead of "set_stream_state", + * it must send "ackSetSourceState" otherwise connection will be removed. + * + * Response of this funcion is like here. + * { + * "response":{ + * "verb":"set_stream_state", + * "error":0 + * }, + * "jtype":"afb-reply", + * "request":{ + * "status":"success", + * "info":"OK" + * } + * } + * + */ +int Soundmanager::setStreamState(int stream_id, int mute_state){ + if(!sp_websock) + { + return -1; + } + struct json_object* j_obj = json_object_new_object(); + struct json_object* jstream = json_object_new_int(stream_id); + struct json_object* jmute = json_object_new_int(mute_state); + json_object_object_add(j_obj, "stream_id", jstream); + json_object_object_add(j_obj, "mute", jmute); + return this->call("set_stream_state", j_obj); +} + /** * This function calls the API of Audio Manager via WebSocket * @@ -364,17 +704,17 @@ int Soundmanager::ackSetSourceState(int handle, int error) */ int Soundmanager::call(const string& verb, struct json_object* arg) { - int ret; + int ret = -1; if(!sp_websock) { - return -1; + return ret; } if (!has_verb(verb)) { ELOG("verb doesn't exit"); - return -1; + return ret; } - ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this); + ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _onReplyStatic, this); if (ret < 0) { ELOG("Failed to call verb:%s",verb.c_str()); } @@ -398,17 +738,17 @@ int Soundmanager::call(const string& verb, struct json_object* arg) */ int Soundmanager::call(const char* verb, struct json_object* arg) { - int ret; + int ret = -1; if(!sp_websock) { - return -1; + return ret; } if (!has_verb(string(verb))) { ELOG("verb doesn't exit"); - return -1; + return ret; } - ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this); + ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _onReplyStatic, this); if (ret < 0) { ELOG("Failed to call verb:%s",verb); } @@ -438,7 +778,7 @@ int Soundmanager::subscribe(const string& event_name) struct json_object* j_obj = json_object_new_object(); json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str())); - int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this); + int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _onReplyStatic, this); if (ret < 0) { ELOG("Failed to call verb:%s",__FUNCTION__); } @@ -467,7 +807,7 @@ int Soundmanager::unsubscribe(const string& event_name) struct json_object* j_obj = json_object_new_object(); json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str())); - int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this); + int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _onReplyStatic, this); if (ret < 0) { ELOG("Failed to call verb:%s",__FUNCTION__); } @@ -476,7 +816,7 @@ int Soundmanager::unsubscribe(const string& event_name) /************* Callback Function *************/ -void Soundmanager::on_hangup(void *closure, struct afb_wsj1 *wsj) +void Soundmanager::_onHangup(void *closure, struct afb_wsj1 *wsj) { DLOG("%s called", __FUNCTION__); if(onHangup != nullptr) @@ -485,7 +825,7 @@ void Soundmanager::on_hangup(void *closure, struct afb_wsj1 *wsj) } } -void Soundmanager::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) +void Soundmanager::_onCall(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) { } @@ -496,7 +836,7 @@ void Soundmanager::on_call(void *closure, const char *api, const char *verb, str * so you can get event name : struct json_object obj = json_object_object_get(msg,"event") */ -void Soundmanager::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg) +void Soundmanager::_onEvent(void *closure, const char *event, struct afb_wsj1_msg *msg) { /* check event is for us */ string ev = string(event); @@ -514,22 +854,9 @@ void Soundmanager::on_event(void *closure, const char *event, struct afb_wsj1_ms json_object_put(ev_contents); } -void Soundmanager::on_reply(void *closure, struct afb_wsj1_msg *msg) +void Soundmanager::_onReply(void *closure, struct afb_wsj1_msg *msg) { struct json_object* reply = afb_wsj1_msg_object_j(msg); - /*struct json_object *json_data = json_object_object_get(reply, "response"); - struct json_object *jverb = json_object_object_get(json_data, "verb"); - const char* cverb = json_object_get_string(jverb); - DLOG("cverb is %s",cverb); - string verb = string(cverb); - DLOG("verb is %s",verb.c_str()); - - if(verb == "registerSource"){ - struct json_object *jsourceID = json_object_object_get(json_data, "sourceID"); - int sourceID = json_object_get_int(jsourceID); - msourceIDs.push_back(sourceID); - DLOG("my sourceID is created: %d", sourceID); - }*/ if(onReply != nullptr) { onReply(reply); @@ -546,7 +873,7 @@ static void _ELOG(const char* func, const int line, const char* log, ...) va_start(args, log); if (log == NULL || vasprintf(&message, log, args) < 0) message = NULL; - cout << "[ERROR: soundmanager]" << func << "(" << line << "):" << message << endl; + cerr << "[ERROR: soundmanager]" << func << "(" << line << "):" << message << endl; va_end(args); free(message); } diff --git a/src/libsoundmanager.hpp b/src/libsoundmanager.hpp index 96346e8..7a20f57 100644 --- a/src/libsoundmanager.hpp +++ b/src/libsoundmanager.hpp @@ -39,13 +39,22 @@ public: void (*event_cb)(const std::string& event, struct json_object* event_contents), void (*reply_cb)(struct json_object* reply_contents), void (*hangup_cb)(void) = nullptr); + struct sd_event* getEventLoop(); /* Method */ - int registerSource(const std::string& sourceName); - int connect(int sourceID, int sinkID); - int connect(int sourceID, const std::string& sinkName); - int disconnect(int connectionID); - int ackSetSourceState(int handle, int error); + int registerSource(const std::string& audio_role); + int connect(int source_id, int sink_id); + int connect(int source_id, const std::string& sink_name); + int disconnect(int connection_id); + int ackSetSourceState(int handle, int error = 0); + int getListMainSources(); + int getListMainSinks(); + int getListMainConnections(); + + int streamOpen(const std::string& audio_role, int endpoint_id); + int streamOpen(const std::string& audio_role, const std::string& endpoint_id = "default"); + int streamClose(int stream_id); + int setStreamState(int stream_id, int mute_state = 0); // 0 is unmute, 1 is mute int call(const std::string& verb, struct json_object* arg); int call(const char* verb, struct json_object* arg); @@ -53,8 +62,8 @@ public: int unsubscribe(const std::string& event_name); private: - int init_event(); - int initialize_websocket(); + int initEvent(); + int initializeWebsocket(); void (*onEvent)(const std::string& event, struct json_object* event_contents); void (*onReply)(struct json_object* reply); @@ -68,11 +77,11 @@ private: std::vector msourceIDs; public: - /* Don't use/ Internal only */ - void on_hangup(void *closure, struct afb_wsj1 *wsj); - void on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg); - void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg); - void on_reply(void *closure, struct afb_wsj1_msg *msg); + /* Don't use. Internal only */ + void _onHangup(void *closure, struct afb_wsj1 *wsj); + void _onCall(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg); + void _onEvent(void *closure, const char *event, struct afb_wsj1_msg *msg); + void _onReply(void *closure, struct afb_wsj1_msg *msg); }; #endif /* LIBSOUNDMANAGER_H */ -- cgit 1.2.3-korg