aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFulup Ar Foll <fulup@iot.bzh>2018-05-17 21:43:24 +0200
committerFulup Ar Foll <fulup@iot.bzh>2018-05-17 21:43:24 +0200
commit253df14bd84f535a54f4d12c95399899e1343c20 (patch)
tree16093cc86d2874afb3311124613e69e23e74acbc
parent29f5fc4e093b8793eaeeef056d0b998182269718 (diff)
Initial version with dynamic APIs
m---------app-controller-submodule0
-rw-r--r--conf.d/cmake/config.cmake4
-rw-r--r--conf.d/project/etc/4a-softmixer-config.json42
-rw-r--r--conf.d/project/etc/4a-softmixer-test.json75
-rw-r--r--conf.d/project/lua.d/softmixer-simple-test.lua (renamed from conf.d/project/lua.d/softmixer-simple.lua)97
-rw-r--r--mixer-binding/mixer-binding.c5
-rw-r--r--nbproject/configurations.xml16
-rw-r--r--plugins/alsa/CMakeLists.txt2
-rw-r--r--plugins/alsa/alsa-api-backend.c (renamed from plugins/alsa/alsa-api-sndcards.c)78
-rw-r--r--plugins/alsa/alsa-api-frontend.c (renamed from plugins/alsa/alsa-api-sndloops.c)39
-rw-r--r--plugins/alsa/alsa-api-mixer.c150
-rw-r--r--plugins/alsa/alsa-api-streams.c (renamed from plugins/alsa/alsa-api-sndstreams.c)145
-rw-r--r--plugins/alsa/alsa-api-zones.c (renamed from plugins/alsa/alsa-api-sndzones.c)58
-rw-r--r--plugins/alsa/alsa-core-ctl.c1
-rw-r--r--plugins/alsa/alsa-plug-multi.c8
-rw-r--r--plugins/alsa/alsa-plug-route.c11
-rw-r--r--plugins/alsa/alsa-plug-vol.c24
-rw-r--r--plugins/alsa/alsa-softmixer.c5
-rw-r--r--plugins/alsa/alsa-softmixer.h30
19 files changed, 543 insertions, 247 deletions
diff --git a/app-controller-submodule b/app-controller-submodule
-Subproject e8b0b3c3cc9b0453b88d1315822fda5963754ae
+Subproject 7af94b8a6f953ed247815bf15049fe951817a21
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index afba6b7..5f5bc23 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -131,8 +131,8 @@ list(APPEND link_libraries afb-helpers)
set(CONTROL_SUPPORT_LUA 1)
add_definitions(-DCONTROL_PLUGIN_PATH="${CMAKE_BINARY_DIR}/package/lib/plugins:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib/plugins")
-add_definitions(-DCONTROL_CONFIG_PATH="${CMAKE_BINARY_DIR}/package/etc:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/etc")
-add_definitions(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/data")
+add_definitions(-DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/etc:${CMAKE_BINARY_DIR}/package/etc:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/etc")
+add_definitions(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/var")
add_definitions(-DCTL_PLUGIN_MAGIC=987456123)
add_definitions(-DUSE_API_DYN=1 -DAFB_BINDING_VERSION=dyn)
diff --git a/conf.d/project/etc/4a-softmixer-config.json b/conf.d/project/etc/4a-softmixer-config.json
deleted file mode 100644
index e67709f..0000000
--- a/conf.d/project/etc/4a-softmixer-config.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json",
- "metadata": {
- "uid": "Soft Mixer",
- "version": "1.0",
- "api": "softmixer",
- "info": "Soft Mixer emulating hardware mixer"
- },
- "resources": [
- {
- "uid": "alsa-softmixer",
- "info": "Map alsa-loop subdevices to 4A HAL streams",
- "spath":"./plugins/alsa:../conf.d/project/lua.d",
- "libs": ["alsa-router.ctlso", "softmixer-simple.lua"],
- "lua": {"prefix":"smix","functions": ["snd_cards", "snd_zones", "snd_loops", "snd_streams"]}
- }
- ],
-
- "onload": [
- {
- "uid": "init-soft-mixer",
- "info": "Initialise Audio Router",
- "action": "lua://alsa-softmixer#_init_softmixer_"
- }
- ],
-
- "controls": [
- {
- "uid": "mixer-config",
- "action": "lua://alsa-softmixer#_mixer_config_"
- },
- {
- "uid": "snd-cards",
- "action": "plugin://alsa-softmixer#snd_cards"
- },
- {
- "uid": "snd-zone",
- "action": "plugin://alsa-softmixer#snd_zones"
- }
- ]
-
-}
diff --git a/conf.d/project/etc/4a-softmixer-test.json b/conf.d/project/etc/4a-softmixer-test.json
new file mode 100644
index 0000000..f363a55
--- /dev/null
+++ b/conf.d/project/etc/4a-softmixer-test.json
@@ -0,0 +1,75 @@
+{
+ "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json",
+ "metadata": {
+ "uid": "Soft Mixer",
+ "version": "1.0",
+ "api": "softmixer",
+ "info": "Soft Mixer emulating hardware mixer"
+ },
+ "resources": [
+ {
+ "uid": "softmixer",
+ "info": "Map alsa-loop subdevices to 4A HAL streams",
+ "spath": "./plugins/alsa:../conf.d/project/lua.d",
+ "libs": ["alsa-softmixer.ctlso", "softmixer-simple-test.lua"],
+ "lua": {
+ "prefix": "smix",
+ "functions": ["_mixer_new_"]
+ }
+ }
+ ],
+
+ "onload": [
+ {
+ "uid": "init-soft-mixer",
+ "info": "Initialise Audio Router",
+ "action": "lua://softmixer#_mixer_simple_test_"
+ }
+ ],
+ "controls": [
+ {
+ "uid": "new",
+ "action": "plugin://softmixer#_mixer_new_",
+ "args": {
+ "devices": {
+ "playback": 0,
+ "capture": 1
+ },
+ "subdevs": [
+ {
+ "subdev": 0,
+ "numid": 51
+ },
+ {
+ "subdev": 1,
+ "numid": 57
+ },
+ {
+ "subdev": 2,
+ "numid": 63
+ },
+ {
+ "subdev": 3,
+ "numid": 69
+ },
+ {
+ "subdev": 4,
+ "numid": 75
+ },
+ {
+ "subdev": 5,
+ "numid": 81
+ },
+ {
+ "subdev": 6,
+ "numid": 87
+ },
+ {
+ "subdev": 7,
+ "numid": 93
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/conf.d/project/lua.d/softmixer-simple.lua b/conf.d/project/lua.d/softmixer-simple-test.lua
index e78eacd..6d6dffc 100644
--- a/conf.d/project/lua.d/softmixer-simple.lua
+++ b/conf.d/project/lua.d/softmixer-simple-test.lua
@@ -30,24 +30,14 @@ end
-- Display receive arguments and echo them to caller
-function _mixer_config_ (source, args)
+function _mixer_simple_test_ (source, args)
do
local error
local response
- -- ==================== Default rate ===========================
-
- local audio_defaults = {
- ["rate"] = 48000,
- }
-
- -- ======================= Loop PCM ===========================
-
- local snd_aloop = {
- ["uid"] = "Alsa-Loop",
- ["devpath"] = "/dev/snd/by-path/platform-snd_aloop.0",
+ -- ================== Default Alsa snd-aloop numid and subdev config
+ local aloop = {
["devices"] = {["playback"]=0,["capture"]=1},
- ["params"] = audio_defaults,
["subdevs"] = {
{["subdev"]= 0, ["numid"]= 51},
{["subdev"]= 1, ["numid"]= 57},
@@ -60,17 +50,23 @@ function _mixer_config_ (source, args)
}
}
- error,response= smix:snd_loops (source, snd_aloop)
- if (error ~= 0) then
- AFB:error (source, "--InLua-- smix:snd_loops fail to attach sndcards=%s", Dump_Table(aloop))
- goto OnErrorExit
- else
- AFB:notice (source, "--InLua-- smix:snd_loops done response=%s\n", Dump_Table(response))
- end
-
+ -- ==================== Default rate ===========================
+
+ local audio_defaults = {
+ ["rate"] = 48000,
+ }
+
+ -- ======================= Loop PCM ===========================
+ local snd_aloop = {
+ ["uid"] = "Alsa-Loop",
+ ["devpath"] = "/dev/snd/by-path/platform-snd_aloop.0",
+ ["params"] = audio_defaults,
+ ["devices"] = aloop.devices,
+ ["subdevs"] = aloop.subdevs,
+ }
- -- ============================= Sound Cards ===================
+ -- ============================= Sound Cards ===================
local snd_yamaha = {
["uid"]= "YAMAHA-APU70",
["devpath"]= "/dev/snd/by-id/usb-YAMAHA_Corporation_YAMAHA_AP-U70_USB_Audio_00-00",
@@ -91,21 +87,8 @@ function _mixer_config_ (source, args)
}
}
- -- group sound card as one multi channels card
- local sndcards= {
- snd_yamaha,
- }
-
- error,response= smix:snd_cards (source, sndcards)
- if (error ~= 0) then
- AFB:error (source, "--InLua-- smix:snd_cards fail to attach sndcards=%s", Dump_Table(sndcards))
- goto OnErrorExit
- else
- AFB:notice (source, "--InLua-- smix:snd_cards done response=%s\n", Dump_Table(response))
- end
-- ============================= Zones ===================
-
local zone_front= {
["uid"] = "front-seats",
["type"] = "playback",
@@ -115,20 +98,7 @@ function _mixer_config_ (source, args)
}
}
- local multi_zones = {
- zone_front,
- }
-
- error,response= smix:snd_zones (source, multi_zones)
- if (error ~= 0) then
- AFB:error (source, "--InLua-- smix:snd_zones fail to attach sndcards=%s", Dump_Table(multi_zones))
- goto OnErrorExit
- else
- AFB:notice (source, "--InLua-- smix:snd_zones done response=%s\n", Dump_Table(response))
- end
-
-- =================== Audio Stream ============================
-
local stream_music= {
["uid"] = "multimedia",
["zone"] = "front-seats",
@@ -143,20 +113,24 @@ function _mixer_config_ (source, args)
["mute"] = false,
}
- local snd_streams = {
- stream_music,
- stream_navigation,
+ --- ================ Create Mixer =========================
+ local MyMixer= {
+ ["uid"]="Simple_Mixer",
+ ["backend"] = {snd_yamaha},
+ ["frontend"]= {snd_aloop},
+ ["zones"] = {zone_front},
+ ["streams"] = {stream_music,stream_navigation},
}
- error,response= smix:snd_streams (source, snd_streams)
+ local error,response= smix:_mixer_new_ (source, MyMixer)
if (error ~= 0) then
- AFB:error (source, "--InLua-- smix:snd_streams fail to attach sndcards=%s", Dump_Table(aloop))
+ AFB:error (source, "--InLua-- smix:_mixer_new_ fail config=%s", Dump_Table(aloop))
goto OnErrorExit
else
- AFB:notice (source, "--InLua-- smix:streams_loops done response=%s\n", Dump_Table(response))
+ AFB:notice (source, "--InLua-- smix:_mixer_new_ done response=%s\n", Dump_Table(response))
end
-
+
-- ================== Happy End =============================
AFB:notice (source, "--InLua-- _mixer_config_ done")
return 0 end
@@ -166,16 +140,3 @@ function _mixer_config_ (source, args)
AFB:error (source, "--InLua-- snd_attach fail")
return 1 -- unhappy end --
end
-
--- Display receive arguments and echo them to caller
-function _init_softmixer_ (source, args)
-
- printf ("*********** in print ")
- -- create event to push change audio roles to potential listeners
- _EventHandle=AFB:evtmake(source, "control")
-
- _mixer_config_ (source, args)
-
-end
-
-printf ("*********** load done ")
diff --git a/mixer-binding/mixer-binding.c b/mixer-binding/mixer-binding.c
index 7f199bc..fe4b284 100644
--- a/mixer-binding/mixer-binding.c
+++ b/mixer-binding/mixer-binding.c
@@ -56,7 +56,7 @@ STATIC int CtrlLoadStaticVerbs (afb_dynapi *apiHandle, AFB_ApiVerbs *verbs) {
int errcount=0;
for (int idx=0; verbs[idx].verb; idx++) {
- errcount+= afb_dynapi_add_verb(apiHandle, CtrlApiVerbs[idx].verb, NULL, CtrlApiVerbs[idx].callback, (void*)&CtrlApiVerbs[idx], CtrlApiVerbs[idx].auth, 0);
+ errcount+= afb_dynapi_add_verb(apiHandle, CtrlApiVerbs[idx].verb, CtrlApiVerbs[idx].info, CtrlApiVerbs[idx].callback, (void*)&CtrlApiVerbs[idx], CtrlApiVerbs[idx].auth, 0);
}
return errcount;
@@ -100,7 +100,8 @@ STATIC int CtrlLoadOneApi (void *cbdata, AFB_ApiT apiHandle) {
// init API function (does not receive user closure ???
afb_dynapi_on_init(apiHandle, CtrlInitOneApi);
- afb_dynapi_seal(apiHandle);
+ // should not seal API as each mixer+stream create a new verb
+ // afb_dynapi_seal(apiHandle);
return err;
OnErrorExit:
diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml
index 96a283e..812b398 100644
--- a/nbproject/configurations.xml
+++ b/nbproject/configurations.xml
@@ -26,10 +26,10 @@
</df>
<df name="plugins">
<df name="alsa">
- <in>alsa-api-sndcards.c</in>
- <in>alsa-api-sndloops.c</in>
- <in>alsa-api-sndstreams.c</in>
- <in>alsa-api-sndzones.c</in>
+ <in>alsa-api-backend.c</in>
+ <in>alsa-api-frontend.c</in>
+ <in>alsa-api-streams.c</in>
+ <in>alsa-api-zones.c</in>
<in>alsa-capture.c</in>
<in>alsa-core-ctl.c</in>
<in>alsa-core-pcm.c</in>
@@ -367,28 +367,28 @@
<cTool flags="1">
</cTool>
</item>
- <item path="plugins/alsa/alsa-api-sndcards.c" ex="false" tool="0" flavor2="2">
+ <item path="plugins/alsa/alsa-api-backend.c" ex="false" tool="0" flavor2="2">
<cTool flags="0">
<incDir>
<pElem>/usr/include/lua5.3</pElem>
</incDir>
</cTool>
</item>
- <item path="plugins/alsa/alsa-api-sndloops.c" ex="false" tool="0" flavor2="2">
+ <item path="plugins/alsa/alsa-api-frontend.c" ex="false" tool="0" flavor2="2">
<cTool flags="0">
<incDir>
<pElem>/usr/include/lua5.3</pElem>
</incDir>
</cTool>
</item>
- <item path="plugins/alsa/alsa-api-sndstreams.c" ex="false" tool="0" flavor2="2">
+ <item path="plugins/alsa/alsa-api-streams.c" ex="false" tool="0" flavor2="2">
<cTool flags="0">
<incDir>
<pElem>/usr/include/lua5.3</pElem>
</incDir>
</cTool>
</item>
- <item path="plugins/alsa/alsa-api-sndzones.c" ex="false" tool="0" flavor2="2">
+ <item path="plugins/alsa/alsa-api-zones.c" ex="false" tool="0" flavor2="2">
<cTool flags="0">
<incDir>
<pElem>/usr/include/lua5.3</pElem>
diff --git a/plugins/alsa/CMakeLists.txt b/plugins/alsa/CMakeLists.txt
index 51f4639..687ccb2 100644
--- a/plugins/alsa/CMakeLists.txt
+++ b/plugins/alsa/CMakeLists.txt
@@ -17,7 +17,7 @@
###########################################################################
-PROJECT_TARGET_ADD(alsa-router)
+PROJECT_TARGET_ADD(alsa-softmixer)
file(GLOB SOURCE_FILES "alsa-*.c")
diff --git a/plugins/alsa/alsa-api-sndcards.c b/plugins/alsa/alsa-api-backend.c
index daa996a..948d30d 100644
--- a/plugins/alsa/alsa-api-sndcards.c
+++ b/plugins/alsa/alsa-api-backend.c
@@ -38,16 +38,16 @@ OnErrorExit:
}
PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params) {
- const char *format=NULL, *access=NULL;
+ const char *format = NULL, *access = NULL;
// some default values
- params->rate= ALSA_DEFAULT_PCM_RATE;
- params->channels= 2;
- params->sampleSize=0;
+ params->rate = ALSA_DEFAULT_PCM_RATE;
+ params->channels = 2;
+ params->sampleSize = 0;
- int error = wrap_json_unpack(paramsJ, "{s?i,s?i, s?s, s?s !}", "rate",&params->rate,"channels", &params->channels, "format",&format, "access",&access);
+ int error = wrap_json_unpack(paramsJ, "{s?i,s?i, s?s, s?s !}", "rate", &params->rate, "channels", &params->channels, "format", &format, "access", &access);
if (error) goto OnErrorExit;
-
+
if (!format) params->format = SND_PCM_FORMAT_S16_LE;
else if (!strcasecmp(format, "S16_LE")) params->format = SND_PCM_FORMAT_S16_LE;
else if (!strcasecmp(format, "S16_BE")) params->format = SND_PCM_FORMAT_S16_BE;
@@ -69,8 +69,8 @@ PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *pa
AFB_ApiNotice(source->api, "ProcessSndParams:%s(params) unsupported format 'S16_LE|S32_L|...' format=%s", uid, format);
goto OnErrorExit;
}
-
- if (!access) params->access = SND_PCM_ACCESS_RW_INTERLEAVED;
+
+ if (!access) params->access = SND_PCM_ACCESS_RW_INTERLEAVED;
else if (!strcasecmp(access, "MMAP_INTERLEAVED")) params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (!strcasecmp(access, "MMAP_NONINTERLEAVED")) params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else if (!strcasecmp(access, "MMAP_COMPLEX")) params->access = SND_PCM_ACCESS_MMAP_COMPLEX;
@@ -78,7 +78,7 @@ PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *pa
else if (!strcasecmp(access, "RW_NONINTERLEAVED")) params->access = SND_PCM_ACCESS_RW_NONINTERLEAVED;
else {
- AFB_ApiNotice(source->api, "ProcessSndParams:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s",uid, access);
+ AFB_ApiNotice(source->api, "ProcessSndParams:%s(params) unsupported access 'RW_INTERLEAVED|MMAP_INTERLEAVED|MMAP_COMPLEX' access=%s", uid, access);
goto OnErrorExit;
}
@@ -89,11 +89,19 @@ OnErrorExit:
}
STATIC int ProcessOneSndCard(CtlSourceT *source, json_object *sndcardJ, AlsaPcmInfoT *snd) {
- json_object *sinkJ=NULL, *paramsJ = NULL;
+ json_object *sinkJ = NULL, *paramsJ = NULL;
int error;
- error = wrap_json_unpack(sndcardJ, "{ss,s?s,s?s,s?i,s?i,s?i,so,s?o !}", "uid", &snd->uid, "devpath", &snd->devpath, "cardid", &snd->cardid
- , "cardidx", &snd->cardidx, "device", &snd->device, "subdev", &snd->subdev, "sink", &sinkJ, "params", &paramsJ);
+ error = wrap_json_unpack(sndcardJ, "{ss,s?s,s?s,s?i,s?i,s?i,so,s?o !}"
+ , "uid", &snd->uid
+ , "devpath", &snd->devpath
+ , "cardid", &snd->cardid
+ , "cardidx", &snd->cardidx
+ , "device", &snd->device
+ , "subdev", &snd->subdev
+ , "sink", &sinkJ
+ , "params", &paramsJ
+ );
if (error || !snd->uid || !sinkJ || (!snd->devpath && !snd->cardid && snd->cardidx)) {
AFB_ApiNotice(source->api, "ProcessOneSndCard missing 'uid|path|cardid|cardidx|channels|device|subdev|numid|params' devin=%s", json_object_get_string(sndcardJ));
goto OnErrorExit;
@@ -106,10 +114,10 @@ STATIC int ProcessOneSndCard(CtlSourceT *source, json_object *sndcardJ, AlsaPcmI
goto OnErrorExit;
}
} else {
- snd->params.rate= ALSA_DEFAULT_PCM_RATE;
- snd->params.access= SND_PCM_ACCESS_RW_INTERLEAVED;
- snd->params.format=SND_PCM_FORMAT_S16_LE;
- snd->params.channels=2;
+ snd->params.rate = ALSA_DEFAULT_PCM_RATE;
+ snd->params.access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ snd->params.format = SND_PCM_FORMAT_S16_LE;
+ snd->params.channels = 2;
}
// check snd card is accessible
@@ -157,54 +165,54 @@ OnErrorExit:
return -1;
}
-CTLP_LUA2C(snd_cards, source, argsJ, responseJ) {
- AlsaPcmInfoT *sndcards;
-
+PUBLIC int SndBackend(CtlSourceT *source, json_object *argsJ) {
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
int error;
size_t count;
+ assert(mixerHandle);
+
+ if (mixerHandle->backend) {
+ AFB_ApiError(source->api, "SndBackend: mixer=%s backend already declared %s", mixerHandle->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
switch (json_object_get_type(argsJ)) {
case json_type_object:
count = 1;
- sndcards = calloc(count + 1, sizeof (AlsaPcmInfoT));
- error = ProcessOneSndCard(source, argsJ, &sndcards[0]);
+ mixerHandle->backend = calloc(count + 1, sizeof (AlsaPcmInfoT));
+ error = ProcessOneSndCard(source, argsJ, &mixerHandle->backend[0]);
if (error) goto OnErrorExit;
break;
case json_type_array:
count = json_object_array_length(argsJ);
- sndcards = calloc(count + 1, sizeof (AlsaPcmInfoT));
+ mixerHandle->backend = calloc(count + 1, sizeof (AlsaPcmInfoT));
for (int idx = 0; idx < count; idx++) {
json_object *sndcardJ = json_object_array_get_idx(argsJ, idx);
- error = ProcessOneSndCard(source, sndcardJ, &sndcards[idx]);
+ error = ProcessOneSndCard(source, sndcardJ, &mixerHandle->backend[idx]);
if (error) goto OnErrorExit;
}
break;
default:
- AFB_ApiError(source->api, "L2C:sndcards: invalid argsJ= %s", json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndBackend: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
- // register Sound card and multi when needed
- Softmixer->sndcardCtl = sndcards;
if (count == 1) {
-
// only one sound card we multi would be useless
- Softmixer->multiPcm = &sndcards[0];
+ mixerHandle->multiPcm = &mixerHandle->backend[0];
} else {
- AlsaPcmInfoT *pcmMulti;
-
+
// instantiate an alsa multi plugin
- pcmMulti = AlsaCreateMulti(source, "PcmMulti", 0);
- if (!pcmMulti) goto OnErrorExit;
+ mixerHandle->multiPcm = AlsaCreateMulti(source, "PcmMulti", 0);
+ if (!mixerHandle->multiPcm) goto OnErrorExit;
- Softmixer->multiPcm = pcmMulti;
}
-
return 0;
OnErrorExit:
- AFB_ApiNotice(source->api, "L2C:sndcards fail to process: %s", json_object_get_string(argsJ));
+ AFB_ApiNotice(source->api, "SndBackend mixer=%s fail to process: %s", mixerHandle->uid, json_object_get_string(argsJ));
return -1;
}
diff --git a/plugins/alsa/alsa-api-sndloops.c b/plugins/alsa/alsa-api-frontend.c
index cf7bf55..c01a88b 100644
--- a/plugins/alsa/alsa-api-sndloops.c
+++ b/plugins/alsa/alsa-api-frontend.c
@@ -126,7 +126,7 @@ STATIC int ProcessOneLoop(CtlSourceT *source, json_object *loopJ, AlsaSndLoopT *
}
break;
default:
- AFB_ApiError(source->api, "L2C:ProcessOneLoop=%s invalid subdevs= %s", loop->uid, json_object_get_string(subdevsJ));
+ AFB_ApiError(source->api, "ProcessOneLoop=%s invalid subdevs= %s", loop->uid, json_object_get_string(subdevsJ));
goto OnErrorExit;
}
@@ -136,25 +136,42 @@ OnErrorExit:
return -1;
}
-CTLP_LUA2C(snd_loops, source, argsJ, responseJ) {
+PUBLIC int SndFrontend (CtlSourceT *source, json_object *argsJ) {
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
int error;
- AlsaSndLoopT *sndLoop = calloc(1, sizeof (AlsaSndLoopT));
-
- if (json_object_get_type(argsJ) != json_type_object) {
- AFB_ApiError(source->api, "L2C:sndloops: invalid object type= %s", json_object_get_string(argsJ));
+ assert (mixerHandle);
+
+ if (mixerHandle->loop) {
+ AFB_ApiError(source->api, "SndFrontend: mixer=%s SndFrontend already declared %s", mixerHandle->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ mixerHandle->loop= calloc(1, sizeof (AlsaSndLoopT));
+
+ // or syntax purpose array is accepted but frontend should have a single driver entry
+ json_type type= json_object_get_type(argsJ);
+ if (type == json_type_array) {
+ size_t count= json_object_array_length(argsJ);
+ if (count != 1) {
+ AFB_ApiError(source->api, "SndFrontend: mixer=%s frontend only support on input driver args=%s", mixerHandle->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+ argsJ= json_object_array_get_idx(argsJ,0);
+ }
+
+ type= json_object_get_type(argsJ);
+ if (type != json_type_object) {
+ AFB_ApiError(source->api, "SndFrontend: mixer=%s invalid object type= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
- error = ProcessOneLoop(source, argsJ, sndLoop);
+ error = ProcessOneLoop(source, argsJ, mixerHandle->loop);
if (error) {
- AFB_ApiError(source->api, "L2C:sndloops: invalid object= %s", json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndFrontend: mixer=%s invalid object= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
- // register routed into global softmixer handle
- Softmixer->loopCtl = sndLoop;
-
return 0;
OnErrorExit:
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
new file mode 100644
index 0000000..f1619c2
--- /dev/null
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Fulup Ar Foll <fulup@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE // needed for vasprintf
+
+#include "alsa-softmixer.h"
+#include <string.h>
+
+// Fulup need to be cleanup with new controller version
+extern Lua2cWrapperT Lua2cWrap;
+
+// API
+
+static void MixerApiVerbCB(AFB_ReqT request) {
+ json_object *responseJ, *backendJ = NULL, *frontendJ = NULL, *zonesJ = NULL, *streamsJ = NULL;
+ // retrieve action handle from request and execute the request
+ json_object *argsJ = afb_request_json(request);
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) afb_request_get_vcbdata(request);
+ int error;
+ int close = 0;
+
+ CtlSourceT *source = alloca(sizeof (CtlSourceT));
+ source->uid = mixerHandle->uid;
+ source->api = request->dynapi;
+ source->request = request;
+ source->context = mixerHandle;
+
+ error = wrap_json_unpack(argsJ, "{s?b s?o,s?o,s?o,s?o !}"
+ , "close", &close
+ , "backend", &backendJ
+ , "frontend", &frontendJ
+ , "zones", &zonesJ
+ , "streams", &streamsJ
+ );
+ if (error) {
+ AFB_ReqFailF(request, "MixerApiVerbCB", "missing 'uid|backend|frontend|zones|streams' mixer=%s", json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ // Free attached resources and free mixer
+ if (close) {
+ AFB_ReqFailF(request, "MixerApiVerbCB", "(Fulup) Close action still to be done mixer=%s", json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ if (backendJ) {
+ error = SndBackend(source, backendJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (frontendJ) {
+ error = SndFrontend(source, frontendJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (zonesJ) {
+ error = SndZones(source, zonesJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (streamsJ) {
+ error = SndStreams(source, streamsJ, &responseJ);
+ if (error) goto OnErrorExit;
+ }
+
+
+ AFB_ReqSucess(request, responseJ, mixerHandle->uid);
+ return;
+
+OnErrorExit:
+ return;
+}
+
+CTLP_LUA2C(_mixer_new_, source, argsJ, responseJ) {
+ SoftMixerHandleT *mixerHandle = calloc(1, sizeof (SoftMixerHandleT));
+ json_object *backendJ = NULL, *frontendJ = NULL, *zonesJ = NULL, *streamsJ = NULL;
+ int error;
+ assert(source->api);
+
+ if (json_object_get_type(argsJ) != json_type_object) {
+ AFB_ApiError(source->api, "_mixer_new_: invalid object type= %s", json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?o,s?o,s?o,s?o !}"
+ , "uid", &mixerHandle->uid
+ , "info", &mixerHandle->info
+ , "backend", &backendJ
+ , "frontend", &frontendJ
+ , "zones", &zonesJ
+ , "streams", &streamsJ);
+ if (error) {
+ AFB_ApiNotice(source->api, "_mixer_new_ missing 'uid|backend|frontend|zones|streams' mixer=%s", json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ // make sure string do not get deleted
+ mixerHandle->uid = strdup(mixerHandle->uid);
+ if (mixerHandle->info)mixerHandle->info = strdup(mixerHandle->info);
+
+ // create mixer verb within API.
+ error = afb_dynapi_add_verb(source->api, mixerHandle->uid, mixerHandle->info, MixerApiVerbCB, mixerHandle, NULL, 0);
+ if (error) {
+ AFB_ApiError(source->api, "_mixer_new_ mixer=%s fail to register API verb", mixerHandle->uid);
+ return -1;
+ }
+
+ // make sure sub command get access to mixer handle
+ source->context = mixerHandle;
+
+ if (backendJ) {
+ error = SndBackend(source, backendJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (frontendJ) {
+ error = SndFrontend(source, frontendJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (zonesJ) {
+ error = SndZones(source, zonesJ);
+ if (error) goto OnErrorExit;
+ }
+
+ if (streamsJ) {
+ error = SndStreams(source, streamsJ, responseJ);
+ if (error) goto OnErrorExit;
+ }
+
+ return 0;
+
+OnErrorExit:
+ return -1;
+} \ No newline at end of file
diff --git a/plugins/alsa/alsa-api-sndstreams.c b/plugins/alsa/alsa-api-streams.c
index e55aeff..42d8ade 100644
--- a/plugins/alsa/alsa-api-sndstreams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -29,6 +29,82 @@
// Fulup need to be cleanup with new controller version
extern Lua2cWrapperT Lua2cWrap;
+typedef struct {
+ const char* verb;
+ AlsaSndStreamT *streams;
+ SoftMixerHandleT *mixer;
+} apiHandleT;
+
+static void StreamApiVerbCB(AFB_ReqT request) {
+ int close=0, error=0, quiet=0;
+ long volume=-1, mute=-1;
+ json_object *responseJ, *argsJ= afb_request_json(request);
+ apiHandleT *handle = (apiHandleT*) afb_request_get_vcbdata(request);
+
+ CtlSourceT *source = alloca(sizeof (CtlSourceT));
+ source->uid = handle->verb;
+ source->api = request->dynapi;
+ source->request = NULL;
+ source->context = NULL;
+
+ error = wrap_json_unpack(argsJ, "{s?b s?b,s?b, s?i !}"
+ , "quiet", &quiet
+ , "close", &close
+ , "mute", &mute
+ , "volume", &volume
+ );
+ if (error) {
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Missing 'close|mute|volume|quiet' args=%s", json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ snd_ctl_t *ctlDev= AlsaCtlOpenCtl (source, handle->mixer->loop->cardid);
+ if (!ctlDev) {
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to open sndcard=%s", handle->mixer->loop->cardid);
+ goto OnErrorExit;
+ }
+
+ if (close) {
+ AFB_ReqFailF(request, "StreamApiVerbCB", "(Fulup) Close action still to be done mixer=%s", json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ if (volume != -1) {
+ error= AlsaCtlNumidSetLong (source, ctlDev, handle->streams->volume, volume);
+ if (error) {
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%ld", handle->streams->volume, volume);
+ goto OnErrorExit;
+ }
+ }
+
+ if (mute != -1) {
+ error= AlsaCtlNumidSetLong (source, ctlDev, handle->streams->volume, !mute);
+ if (error) {
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to set stream volume numid=%d value=%d", handle->streams->volume, !mute);
+ goto OnErrorExit;
+ }
+ }
+
+ // if not in quiet mode return effective selected control values
+ if (quiet) responseJ=NULL;
+ else {
+ error+= AlsaCtlNumidGetLong (source, ctlDev, handle->streams->volume, &volume);
+ error+= AlsaCtlNumidGetLong (source, ctlDev, handle->streams->mute, &mute);
+ if (error) {
+ AFB_ReqFailF(request, "StreamApiVerbCB", "Fail to get stream numids volume=%ld mute=%ld",volume, mute);
+ goto OnErrorExit;
+ }
+ wrap_json_pack (&responseJ,"{volume:si, mute:sb}", volume, mute);
+ }
+
+ AFB_ReqSucess(request, responseJ, handle->verb);
+ return;
+
+OnErrorExit:
+ return;
+
+}
+
STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaSndStreamT *stream) {
int error;
json_object *paramsJ = NULL;
@@ -36,11 +112,17 @@ STATIC int ProcessOneStream(CtlSourceT *source, json_object *streamJ, AlsaSndStr
// Make sure default runs
stream->volume = ALSA_DEFAULT_PCM_VOLUME;
stream->mute = 0;
-
- error = wrap_json_unpack(streamJ, "{ss,ss,s?i,s?b,s?o !}", "uid", &stream->uid, "zone", &stream->zone, "volume", &stream->volume
- , "mute", stream->mute, "params", &paramsJ);
+ stream->info = NULL;
+
+ error = wrap_json_unpack(streamJ, "{ss,s?s,ss,s?i,s?b,s?o !}"
+ , "uid", &stream->uid
+ , "info", &stream->info
+ , "zone", &stream->zone
+ , "volume", &stream->volume
+ , "mute", stream->mute
+ , "params", &paramsJ);
if (error) {
- AFB_ApiNotice(source->api, "ProcessOneStream missing 'uid|zone|volume|rate|mute|params' stream=%s", json_object_get_string(streamJ));
+ AFB_ApiNotice(source->api, "ProcessOneStream missing 'uid|[info]|zone|[volume]|[mute]|[params]' stream=%s", json_object_get_string(streamJ));
goto OnErrorExit;
}
@@ -67,16 +149,19 @@ OnErrorExit:
return -1;
}
-CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
+PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ) {
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
AlsaSndStreamT *sndStream;
int error;
long value;
size_t count;
+ assert(mixerHandle);
+
// assert static/global softmixer handle get requited info
- AlsaSndLoopT *ctlLoop = Softmixer->loopCtl;
+ AlsaSndLoopT *ctlLoop = mixerHandle->loop;
if (!ctlLoop) {
- AFB_ApiError(source->api, "L2C:sndstreams: No Loop found [should register snd_loop first]");
+ AFB_ApiError(source->api, "SndStreams: mixer=%s No Loop found [should register snd_loop first]", mixerHandle->uid);
goto OnErrorExit;
}
@@ -86,7 +171,7 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
sndStream = calloc(count + 1, sizeof (AlsaSndStreamT));
error = ProcessOneStream(source, argsJ, &sndStream[0]);
if (error) {
- AFB_ApiError(source->api, "L2C:sndstreams: invalid stream= %s", json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndStreams: mixer=%s invalid stream= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
break;
@@ -98,13 +183,13 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
json_object *sndStreamJ = json_object_array_get_idx(argsJ, idx);
error = ProcessOneStream(source, sndStreamJ, &sndStream[idx]);
if (error) {
- AFB_ApiError(source->api, "sndstreams: invalid stream= %s", json_object_get_string(sndStreamJ));
+ AFB_ApiError(source->api, "sndstreams: mixer=%s invalid stream= %s", mixerHandle->uid, json_object_get_string(sndStreamJ));
goto OnErrorExit;
}
}
break;
default:
- AFB_ApiError(source->api, "L2C:sndstreams: invalid argsJ= %s", json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndStreams: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
@@ -116,10 +201,10 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
json_object *streamJ, *paramsJ;
// Search for a free loop capture device
- AFB_ApiNotice(source->api, "L2C:sndstreams stream=%s Start", (char*) sndStream[idx].uid);
+ AFB_ApiNotice(source->api, "SndStreams: mixer=%s stream=%s Start", mixerHandle->uid, (char*) sndStream[idx].uid);
ctlLoop->scount--;
if (ctlLoop->scount < 0) {
- AFB_ApiError(source->api, "L2C:sndstreams no more subdev avaliable in loopback=%s", ctlLoop->uid);
+ AFB_ApiError(source->api, "SndStreams: mixer=%s stream=%s no more subdev avaliable in loopback=%s", mixerHandle->uid, sndStream[idx].uid, ctlLoop->uid);
goto OnErrorExit;
}
@@ -144,7 +229,7 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
// Try to create/setup volume control.
snd_ctl_t* ctlDev = AlsaCrlFromPcm(source, captureDev->handle);
if (!ctlDev) {
- AFB_ApiError(source->api, "AlsaCtlRegister [pcm=%s] fail attache sndcard", captureDev->cardid);
+ AFB_ApiError(source->api, "SndStreams: mixer=%s [pcm=%s] fail attache sndcard", mixerHandle->uid, captureDev->cardid);
goto OnErrorExit;
}
@@ -163,9 +248,9 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
// create stream and delay pcm openning until vol control is created
char volName[ALSA_CARDID_MAX_LEN];
snprintf(volName, sizeof (volName), "vol-%s", sndStream[idx].uid);
- AlsaPcmInfoT *streamPcm = AlsaCreateStream(source, &sndStream[idx], captureDev, volName, VOL_CONTROL_MAX, 0);
+ AlsaPcmInfoT *streamPcm = AlsaCreateSoftvol(source, &sndStream[idx], captureDev, volName, VOL_CONTROL_MAX, 0);
if (!streamPcm) {
- AFB_ApiError(source->api, "L2C:sndstreams:%s(pcm) fail to create stream", sndStream[idx].uid);
+ AFB_ApiError(source->api, "SndStreams: mixer=%s%s(pcm) fail to create stream", mixerHandle->uid, sndStream[idx].uid);
goto OnErrorExit;
}
@@ -179,14 +264,14 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
// snprintf(rateName, sizeof (rateName), "rate-%s", sndStream[idx].uid);
// AlsaPcmInfoT *ratePcm= AlsaCreateRate(source, rateName, streamPcm, 1);
// if (!ratePcm) {
- // AFB_ApiError(source->api, "L2C:sndstreams:%s(pcm) fail to create rate converter", sndStream[idx].uid);
+ // AFB_ApiError(source->api, "SndStreams: mixer=%s%s(pcm) fail to create rate converter", sndStream[idx].uid);
// goto OnErrorExit;
// }
// everything is not ready to open capture pcm
error = snd_pcm_open(&streamPcm->handle, sndStream[idx].uid, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "L2C:sndstreams:%s(pcm) fail to open capture", sndStream[idx].uid);
+ AFB_ApiError(source->api, "SndStreams: mixer=%s%s(pcm) fail to open capture", mixerHandle->uid, sndStream[idx].uid);
goto OnErrorExit;
}
@@ -204,7 +289,7 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
// toggle pause/resume (should be done after pcm_start)
if ((error = snd_pcm_pause(captureDev->handle, !value)) < 0) {
- AFB_ApiError(source->api, "L2C:sndstreams [capture=%s] fail to pause error=%s", captureDev->cardid, snd_strerror(error));
+ AFB_ApiError(source->api, "SndStreams: mixer=%s [capture=%s] fail to pause error=%s", mixerHandle->uid, captureDev->cardid, snd_strerror(error));
goto OnErrorExit;
}
@@ -216,16 +301,36 @@ CTLP_LUA2C(snd_streams, source, argsJ, responseJ) {
error += wrap_json_pack(&streamJ, "{ss ss si si so}", "uid", streamPcm->uid, "alsa", playbackDev->cardid, "volid", volNumid, "runid", runNumid, "params", paramsJ);
error += json_object_array_add(*responseJ, streamJ);
if (error) {
- AFB_ApiError(source->api, "L2C:sndstreams:%s(stream) fail to prepare response", captureDev->cardid);
+ AFB_ApiError(source->api, "SndStreams: mixer=%s stream=%s fail to prepare response", mixerHandle->uid, captureDev->cardid);
goto OnErrorExit;
}
+ // create a dedicated verb for this stream compose of mixeruid/streamuid
+ apiHandleT *apiHandle = calloc(1, sizeof (apiHandleT));
+ char apiVerb[128];
+ error = snprintf(apiVerb, sizeof (apiVerb), "%s/%s", mixerHandle->uid, sndStream[idx].uid);
+ if (error == sizeof (apiVerb)) {
+ AFB_ApiError(source->api, "SndStreams mixer=%s fail to register Stream API too long %s/%s", mixerHandle->uid, mixerHandle->uid, sndStream[idx].uid);
+ return -1;
+ }
+
+ apiHandle->mixer = mixerHandle;
+ apiHandle->streams = &sndStream[idx];
+ apiHandle->verb = strdup(apiVerb);
+ error = afb_dynapi_add_verb(source->api, apiHandle->verb, sndStream[idx].info, StreamApiVerbCB, mixerHandle, NULL, 0);
+ if (error) {
+ AFB_ApiError(source->api, "SndStreams mixer=%s fail to register API verb=%s", mixerHandle->uid, apiHandle->verb);
+ return -1;
+ }
+
+ // free tempry resource
snd_ctl_close(ctlDev);
+
// Debug Alsa Config
//AlsaDumpElemConfig (source, "\n\nAlsa_Config\n------------\n", "pcm");
//AlsaDumpPcmInfo(source, "\n\nPcm_config\n-----------\n", streamPcm->handle);
- AFB_ApiNotice(source->api, "L2C:sndstreams:%s(stream) OK reponse=%s\n", streamPcm->uid, json_object_get_string(streamJ));
+ AFB_ApiNotice(source->api, "SndStreams: mixer=%s stream=%s OK reponse=%s\n", mixerHandle->uid, streamPcm->uid, json_object_get_string(streamJ));
}
return 0;
diff --git a/plugins/alsa/alsa-api-sndzones.c b/plugins/alsa/alsa-api-zones.c
index 7ca61ad..de834df 100644
--- a/plugins/alsa/alsa-api-sndzones.c
+++ b/plugins/alsa/alsa-api-zones.c
@@ -26,11 +26,11 @@ extern Lua2cWrapperT Lua2cWrap;
STATIC int ProcessOneChannel(CtlSourceT *source, const char* uid, json_object *channelJ, AlsaPcmChannels *channel) {
const char*channelUid;
-
+
int error = wrap_json_unpack(channelJ, "{ss,si,s?i !}", "target", &channelUid, "channel", &channel->port);
if (error) goto OnErrorExit;
-
- channel->uid=strdup(channelUid);
+
+ channel->uid = strdup(channelUid);
return 0;
OnErrorExit:
@@ -44,9 +44,13 @@ STATIC int ProcessOneZone(CtlSourceT *source, json_object *zoneJ, AlsaSndZoneT *
const char* streamType;
int error;
- error = wrap_json_unpack(zoneJ, "{ss,s?s,so !}", "uid", &zone->uid, "type", &streamType, "mapping", &mappingJ);
+ error = wrap_json_unpack(zoneJ, "{ss,s?s,so !}"
+ , "uid", &zone->uid
+ , "type", &streamType
+ , "mapping", &mappingJ
+ );
if (error) {
- AFB_ApiNotice(source->api, "ProcessOneone missing 'uid|type|mapping' zone=%s", json_object_get_string(zoneJ));
+ AFB_ApiNotice(source->api, "ProcessOneZone missing 'uid|type|mapping' zone=%s", json_object_get_string(zoneJ));
goto OnErrorExit;
}
@@ -59,9 +63,9 @@ STATIC int ProcessOneZone(CtlSourceT *source, json_object *zoneJ, AlsaSndZoneT *
goto OnErrorExit;
}
}
-
+
// make sure remain valid even when json object is removed
- zone->uid= strdup(zone->uid);
+ zone->uid = strdup(zone->uid);
switch (json_object_get_type(mappingJ)) {
case json_type_object:
@@ -90,55 +94,63 @@ OnErrorExit:
return -1;
}
-CTLP_LUA2C(snd_zones, source, argsJ, responseJ) {
- AlsaSndZoneT *sndZone;
+PUBLIC int SndZones(CtlSourceT *source, json_object *argsJ) {
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
+ AlsaSndZoneT *zones=NULL;
int error;
size_t count;
+ assert(mixerHandle);
+
+ if (mixerHandle->routes) {
+ AFB_ApiError(source->api, "SndZones: mixer=%s Zones already registered %s", mixerHandle->uid, json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
switch (json_object_get_type(argsJ)) {
case json_type_object:
count = 1;
- sndZone = calloc(count + 1, sizeof (AlsaSndZoneT));
- error = ProcessOneZone(source, argsJ, &sndZone[0]);
+ zones = calloc(count + 1, sizeof (AlsaSndZoneT));
+ error = ProcessOneZone(source, argsJ, &zones[0]);
if (error) {
- AFB_ApiError(source->api, "L2C:sndzones: invalid zone= %s", json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndZones: mixer=%s invalid zone= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
break;
case json_type_array:
count = json_object_array_length(argsJ);
- sndZone = calloc(count + 1, sizeof (AlsaSndZoneT));
+ zones = calloc(count + 1, sizeof (AlsaSndZoneT));
for (int idx = 0; idx < count; idx++) {
json_object *sndZoneJ = json_object_array_get_idx(argsJ, idx);
- error = ProcessOneZone(source, sndZoneJ, &sndZone[idx]);
+ error = ProcessOneZone(source, sndZoneJ, &zones[idx]);
if (error) {
- AFB_ApiError(source->api, "L2C:sndzones: invalid zone= %s", json_object_get_string(sndZoneJ));
+ AFB_ApiError(source->api, "SndZones: mixer=%s invalid zone= %s", mixerHandle->uid, json_object_get_string(sndZoneJ));
goto OnErrorExit;
}
}
break;
default:
- AFB_ApiError(source->api, "L2C:sndzones: invalid argsJ= %s", json_object_get_string(argsJ));
+ AFB_ApiError(source->api, "SndZones: mixer=%s invalid argsJ= %s", mixerHandle->uid, json_object_get_string(argsJ));
goto OnErrorExit;
}
// register routed into global softmixer handle
- Softmixer->zonePcms = calloc(count+1, sizeof (AlsaPcmInfoT*));
+ mixerHandle->routes= calloc(count + 1, sizeof (AlsaPcmInfoT*));
// instantiate one route PCM per zone with multi plugin as slave
- for (int idx = 0; sndZone[idx].uid != NULL; idx++) {
- Softmixer->zonePcms[idx] = AlsaCreateRoute(source, &sndZone[idx], 0);
- if (!Softmixer->zonePcms[idx]) {
- AFB_ApiNotice(source->api, "L2C:sndzones fail to create route zone=%s", sndZone[idx].uid);
+ for (int idx = 0; zones[idx].uid != NULL; idx++) {
+ mixerHandle->routes[idx] = AlsaCreateRoute(source, &zones[idx], 0);
+ if (!mixerHandle->routes[idx]) {
+ AFB_ApiNotice(source->api, "SndZones: mixer=%s fail to create route zone=%s", mixerHandle->uid, zones[idx].uid);
goto OnErrorExit;
}
}
- // do not need this handle anymore
- free (sndZone);
+ free (zones);
return 0;
OnErrorExit:
+ if (zones) free(zones);
return -1;
} \ No newline at end of file
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index 9a0b844..f224f92 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -170,6 +170,7 @@ OnErrorExit:
return NULL;
}
+
STATIC int CtlElemIdGetNumid(AFB_ApiT api, snd_ctl_t *ctlDev, snd_ctl_elem_id_t *elemId, int *numid) {
snd_ctl_elem_info_t *elemInfo;
diff --git a/plugins/alsa/alsa-plug-multi.c b/plugins/alsa/alsa-plug-multi.c
index 3e82e1e..0dd417d 100644
--- a/plugins/alsa/alsa-plug-multi.c
+++ b/plugins/alsa/alsa-plug-multi.c
@@ -23,15 +23,17 @@
ALSA_PLUG_PROTO(multi);
PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmUid, int open) {
-
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
snd_config_t *multiConfig, *elemConfig, *slavesConfig, *slaveConfig, *bindingsConfig, *bindingConfig, *pcmConfig;
int error = 0, channelIdx=0;
AlsaPcmInfoT *pcmPlug = calloc(1, sizeof (AlsaPcmInfoT));
pcmPlug->uid = pcmUid;
pcmPlug->cardid = pcmUid;
- AlsaPcmInfoT* pcmSlaves=Softmixer->sndcardCtl;
- if (!Softmixer->sndcardCtl) {
+ assert(mixerHandle);
+
+ AlsaPcmInfoT* pcmSlaves=mixerHandle->backend;
+ if (!pcmSlaves) {
AFB_ApiError(source->api, "AlsaCreateMulti: No Sound Card find [should register snd_cards first]");
goto OnErrorExit;
}
diff --git a/plugins/alsa/alsa-plug-route.c b/plugins/alsa/alsa-plug-route.c
index 8fa5de8..d2ca223 100644
--- a/plugins/alsa/alsa-plug-route.c
+++ b/plugins/alsa/alsa-plug-route.c
@@ -48,17 +48,20 @@ OnErrorExit:
}
PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone, int open) {
-
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
snd_config_t *routeConfig, *elemConfig, *slaveConfig, *tableConfig, *pcmConfig;
int error = 0;
AlsaPcmInfoT *pcmPlug = calloc(1, sizeof (AlsaPcmInfoT));
pcmPlug->uid = zone->uid;
pcmPlug->cardid = zone->uid;
+
+ assert(mixerHandle);
- AlsaPcmInfoT *pcmBackend = Softmixer->sndcardCtl;
- AlsaPcmInfoT* pcmSlave=Softmixer->multiPcm;
+ AlsaPcmInfoT *pcmBackend = mixerHandle->backend;
+ AlsaPcmInfoT* pcmSlave=mixerHandle->multiPcm;
if (!pcmBackend || !pcmSlave) {
- AFB_ApiError(source->api, "AlsaCreateRoute:zone(%s)(zone) No Sound Card Ctl find [should register snd_cards first]", zone->uid);
+ AFB_ApiError(source->api, "AlsaCreateRoute: mixer=%s zone(%s)(zone) No Sound Card Ctl find [should register snd_cards first]"
+ , mixerHandle->uid, zone->uid);
goto OnErrorExit;
}
diff --git a/plugins/alsa/alsa-plug-vol.c b/plugins/alsa/alsa-plug-vol.c
index dc600f9..09bc51a 100644
--- a/plugins/alsa/alsa-plug-vol.c
+++ b/plugins/alsa/alsa-plug-vol.c
@@ -36,30 +36,32 @@ STATIC AlsaPcmInfoT* SlaveZoneByUid(CtlSourceT *source, AlsaPcmInfoT **pcmZones
return NULL;
}
-PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open) {
-
+PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open) {
+ SoftMixerHandleT *mixerHandle = (SoftMixerHandleT*) source->context;
snd_config_t *streamConfig, *elemConfig, *slaveConfig, *controlConfig,*pcmConfig;
int error = 0;
AlsaPcmInfoT *pcmPlug= calloc(1,sizeof(AlsaPcmInfoT));
+ assert (mixerHandle);
+
// assert static/global softmixer handle get requited info
- AlsaSndLoopT *ctlLoop = Softmixer->loopCtl;
+ AlsaSndLoopT *ctlLoop = mixerHandle->loop;
if (!ctlLoop) {
- AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) No Loop found [should register snd_loop first]",stream->uid);
+ AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Loop found [should register snd_loop first]",stream->uid);
goto OnErrorExit;
}
// assert static/global softmixer handle get requited info
- AlsaPcmInfoT **pcmZones = Softmixer->zonePcms;
+ AlsaPcmInfoT **pcmZones = mixerHandle->routes;
if (!pcmZones) {
- AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) No Zone found [should register snd_zones first]", stream->uid);
+ AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) No Zone found [should register snd_zones first]", stream->uid);
goto OnErrorExit;
}
// search for target zone uid
AlsaPcmInfoT *pcmSlave= SlaveZoneByUid (source, pcmZones, stream->zone);
if (!pcmSlave || !pcmSlave->uid) {
- AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) fail to find Zone=%s", stream->uid, stream->zone);
+ AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) fail to find Zone=%s", stream->uid, stream->zone);
goto OnErrorExit;
}
@@ -102,24 +104,24 @@ PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream
if (open) error = _snd_pcm_softvol_open(&pcmPlug->handle, stream->uid, snd_config, streamConfig, SND_PCM_STREAM_PLAYBACK , SND_PCM_NONBLOCK);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) fail to create Plug=%s Slave=%s error=%s", stream->uid, pcmPlug->cardid, pcmSlave->cardid, snd_strerror(error));
+ AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) fail to create Plug=%s Slave=%s error=%s", stream->uid, pcmPlug->cardid, pcmSlave->cardid, snd_strerror(error));
goto OnErrorExit;
}
error += snd_config_search(snd_config, "pcm", &pcmConfig);
error += snd_config_add(pcmConfig, streamConfig);
if (error) {
- AFB_ApiError(source->api, "AlsaCreateStream:%s(stream) fail to add config", stream->uid);
+ AFB_ApiError(source->api, "AlsaCreateSoftvol:%s(stream) fail to add config", stream->uid);
goto OnErrorExit;
}
// Debug config & pcm
//AlsaDumpCtlConfig (source, "plug-stream", streamConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateStream:%s(stream) done\n", stream->uid);
+ AFB_ApiNotice(source->api, "AlsaCreateSoftvol:%s(stream) done\n", stream->uid);
return pcmPlug;
OnErrorExit:
AlsaDumpCtlConfig(source, "plug-stream", streamConfig, 1);
- AFB_ApiNotice(source->api, "AlsaCreateStream:%s(stream) OnErrorExit\n", stream->uid);
+ AFB_ApiNotice(source->api, "AlsaCreateSoftvol:%s(stream) OnErrorExit\n", stream->uid);
return NULL;
} \ No newline at end of file
diff --git a/plugins/alsa/alsa-softmixer.c b/plugins/alsa/alsa-softmixer.c
index 033e87b..4528087 100644
--- a/plugins/alsa/alsa-softmixer.c
+++ b/plugins/alsa/alsa-softmixer.c
@@ -24,12 +24,9 @@
CTLP_LUA_REGISTER("alsa-softmixer")
-SoftMixerHandleT *Softmixer;
-
// Call at initialisation time
CTLP_ONLOAD(plugin, callbacks) {
- AFB_ApiDebug(plugin->api, "SoftMixer plugin: uid='%s' 'info='%s'", plugin->uid, plugin->info);
- Softmixer = calloc(1, sizeof(SoftMixerHandleT));
+ AFB_ApiDebug(plugin->api, "SoftMixer Plugin Registered: uid='%s' 'info='%s'", plugin->uid, plugin->info);
return NULL;
}
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index e2c9370..7146e78 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -24,13 +24,14 @@
#include <afb/afb-binding.h>
#include <systemd/sd-event.h>
#include <json-c/json_object.h>
-#include <stdio.h>
#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <alsa/asoundlib.h>
#include "ctl-plugin.h"
#include "wrap-json.h"
-#include <alsa/asoundlib.h>
#ifndef PUBLIC
#define PUBLIC
@@ -104,10 +105,9 @@ typedef struct {
AlsaPcmInfoT *subdevs;
} AlsaSndLoopT;
-
-
typedef struct {
const char *uid;
+ const char *info;
const char *zone;
int volume;
int mute;
@@ -116,23 +116,20 @@ typedef struct {
} AlsaSndStreamT;
typedef struct {
- AlsaSndLoopT *loopCtl;
- AlsaPcmInfoT *sndcardCtl;
+ const char *uid;
+ const char *info;
+ AlsaSndLoopT *loop;
+ AlsaPcmInfoT *backend;
AlsaPcmInfoT *multiPcm;
- AlsaPcmInfoT **zonePcms;
+ AlsaPcmInfoT **routes;
} SoftMixerHandleT;
-extern SoftMixerHandleT *Softmixer;
-
// alsa-utils-bypath.c
PUBLIC snd_ctl_card_info_t* AlsaByPathInfo(CtlSourceT *source, const char *control);
PUBLIC AlsaPcmInfoT* AlsaByPathOpenPcm(CtlSourceT *source, AlsaPcmInfoT *dev, snd_pcm_stream_t direction);
PUBLIC snd_ctl_t *AlsaByPathOpenCtl(CtlSourceT *source, AlsaPcmInfoT *dev);
PUBLIC int AlsaByPathDevid(CtlSourceT *source, AlsaPcmInfoT *dev);
-// alsa-api-*.c
-PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params);
-
// alsa-utils-dump.c
PUBLIC void AlsaDumpFormats(CtlSourceT *source, snd_pcm_t *pcmHandle);
PUBLIC char *AlsaDumpPcmUid(snd_pcm_t *pcmHandle, char *buffer, size_t len);
@@ -168,7 +165,14 @@ PUBLIC int AlsaPcmCopy(CtlSourceT *source, AlsaPcmInfoT *pcmIn, AlsaPcmInfoT *pc
PUBLIC AlsaPcmInfoT* AlsaCreateDmix(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open);
PUBLIC AlsaPcmInfoT* AlsaCreateMulti(CtlSourceT *source, const char *pcmName, int open);
PUBLIC AlsaPcmInfoT* AlsaCreateRoute(CtlSourceT *source, AlsaSndZoneT *zone, int open);
-PUBLIC AlsaPcmInfoT* AlsaCreateStream(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open);
+PUBLIC AlsaPcmInfoT* AlsaCreateSoftvol(CtlSourceT *source, AlsaSndStreamT *stream, AlsaPcmInfoT *ctlControl, const char* ctlName, int max, int open);
PUBLIC AlsaPcmInfoT* AlsaCreateRate(CtlSourceT *source, const char* pcmName, AlsaPcmInfoT *pcmSlave, int open);
+// alsa-api-*
+PUBLIC int ProcessSndParams(CtlSourceT *source, const char* uid, json_object *paramsJ, AlsaPcmHwInfoT *params);
+PUBLIC int SndFrontend (CtlSourceT *source, json_object *argsJ);
+PUBLIC int SndBackend (CtlSourceT *source, json_object *argsJ);
+PUBLIC int SndZones (CtlSourceT *source, json_object *argsJ);
+PUBLIC int SndStreams(CtlSourceT *source, json_object *argsJ, json_object **responseJ);
+
#endif \ No newline at end of file