summaryrefslogtreecommitdiffstats
path: root/conf.d/project
diff options
context:
space:
mode:
Diffstat (limited to 'conf.d/project')
-rw-r--r--conf.d/project/alsa.d/asoundrc.sample146
-rw-r--r--conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf6
-rw-r--r--conf.d/project/alsa.d/ucm.sample/HiFi.conf84
-rw-r--r--conf.d/project/alsa.d/ucm.sample/README2
-rw-r--r--conf.d/project/config.d/onload-audio-control.json14
-rw-r--r--conf.d/project/lua.d/doscript-helloworld.lua2
-rw-r--r--conf.d/project/lua.d/onload-audio-0utils.lua86
-rw-r--r--conf.d/project/lua.d/onload-audio-controls.lua159
-rw-r--r--conf.d/project/lua.d/onload-audio-oncall.lua16
-rw-r--r--conf.d/project/lua.d/onload-audio-timer.lua10
-rw-r--r--conf.d/project/lua.d/onload-audio-utils.lua32
11 files changed, 468 insertions, 89 deletions
diff --git a/conf.d/project/alsa.d/asoundrc.sample b/conf.d/project/alsa.d/asoundrc.sample
new file mode 100644
index 0000000..b208a09
--- /dev/null
+++ b/conf.d/project/alsa.d/asoundrc.sample
@@ -0,0 +1,146 @@
+#
+# Author: Fulup Ar Foll
+# Object: PCM hook type
+#
+# Test : Note: Jabra_USB=hw:v1340
+# Check SoundCard speaker-test -Dhw:v1340 -c2 -twav
+# Check MixerPCM speaker-test -DMyMixerPCM -c2 -twav
+# Check HookPCM speaker-test -DMyNavigationHook -c2 -twav
+# Check NavPCM speaker-test -DMyNavPCM -c2 -twav
+# MultiMedia aplay -DDMyNavPCM /usr/share/sounds/alsa/test.wav
+#
+# Bug/Feature: when softvol control is initialised from plugin and not
+# from AGL binding. At 1st run ctl has invalid TLV and cannot be
+# use. Bypass Solution:
+# * start audio-binder before playing sound (binding create control before softvol plugin)
+# * run a dummy aplay -DMyNavPCM "" to get a clean control
+#
+# References: https://www.spinics.net/lists/alsa-devel/msg54235.html
+# --------------------------------------------------------------------
+
+# Mixer PCM allow to play multiple stream simultaneously
+# ------------------------------------------------------
+pcm.MyMixerPCM {
+ type dmix
+ ipc_key 1024
+ ipc_key_add_uid false
+ ipc_perm 0666 # mixing for all users
+
+ # Define target effective sound card (cannot be a plugin)
+ slave {
+ pcm "hw:v1340" #Jabra Solmate
+ period_time 0
+ period_size 1024
+ buffer_size 8192
+ rate 44100
+ }
+
+ # DMIX can only map two channels
+ bindings {
+ 0 0
+ 1 1
+ }
+}
+
+# Define a Hook_type with a private sharelib
+# -------------------------------------------
+pcm_hook_type.MyHookPlugin {
+ install "AlsaInstallHook"
+ lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Policy-Hook/policy_hook_cb.so"
+}
+
+
+# Define a HookedPCM that point to Hook_type sharelib
+# ----------------------------------------------------
+pcm.MyNavigationHook {
+ type hooks
+ slave.pcm "MyMixerPCM"
+ # Defined used hook sharelib and provide arguments/config to install func
+ hooks.0 {
+ type "MyHookPlugin"
+ hook_args {
+ verbose true # print few log messages (default false);
+
+ # Every Call should return OK in order PCM to open (default timeout 100ms)
+ uri "ws://localhost:1234/api?token='audio-agent-token'"
+ request {
+ # Request autorisation to write on navigation
+ RequestNavigation {
+ api "control"
+ verb "dispatch"
+ query "{'target':'navigation', 'args':{'device':'Jabra SOLEMATE v1.34.0'}}"
+ }
+ }
+ # map event reception to self generated signal
+ event {
+ pause 30
+ resume 31
+ stop 3
+ }
+ }
+ }
+}
+
+# If hardware does not support mixer emulate it with softvol
+# -----------------------------------------------------------
+pcm.MyMultimediaPCM {
+ type softvol
+
+ # Point Slave on HOOK for policies control
+ slave.pcm "MyNavigationHook"
+
+ # resolution=HAL(valMax+1) (default=256)
+ resolution 256
+
+ # name should match with HAL but do not set card=xx
+ control.name "Playback Navigation"
+
+ # Make this plugin visible from aplay -L
+ hint {
+ show on
+ description "Navigation SolftVol PCM"
+ }
+}
+
+# If hardware does not support mixer emulate it with softvol
+# -----------------------------------------------------------
+pcm.MyNavPCM {
+ type softvol
+
+ # Point Slave on HOOK for policies control
+ slave.pcm "MyNavigationHook"
+
+ # resolution=HAL(valMax+1) (default=256)
+ resolution 256
+
+ # name should match with HAL but do not set card=xx
+ control.name "Playback Navigation"
+
+ # Make this plugin visible from aplay -L
+ hint {
+ show on
+ description "Navigation SolftVol PCM"
+ }
+}
+
+# If hardware does not support mixer emulate it with softvol
+# -----------------------------------------------------------
+pcm.MyAlarmPCM {
+ type softvol
+
+ # Point Slave on HOOK for policies control
+ slave.pcm "MyNavigationHook"
+
+ # resolution=HAL(valMax+1) (default=256)
+ resolution 256
+
+ # name should match with HAL but do not set card=xx
+ control.name "Playback Navigation"
+
+ # Make this plugin visible from aplay -L
+ hint {
+ show on
+ description "Navigation SolftVol PCM"
+ }
+}
+
diff --git a/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf b/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf
new file mode 100644
index 0000000..f6608a0
--- /dev/null
+++ b/conf.d/project/alsa.d/ucm.sample/HDA Intel PCH.conf
@@ -0,0 +1,6 @@
+Comment "Leon internal card"
+
+SectionUseCase."HiFi" {
+ File "HiFi.conf"
+ Comment "Default"
+}
diff --git a/conf.d/project/alsa.d/ucm.sample/HiFi.conf b/conf.d/project/alsa.d/ucm.sample/HiFi.conf
new file mode 100644
index 0000000..9a53c8c
--- /dev/null
+++ b/conf.d/project/alsa.d/ucm.sample/HiFi.conf
@@ -0,0 +1,84 @@
+SectionVerb {
+ EnableSequence [
+ cdev "hw:PCH"
+
+ cset "name='Master Playback Switch' on"
+ cset "name='Headphone Playback Switch' off"
+ cset "name='Speaker Playback Switch' on"
+
+ cset "name='Capture Switch' on"
+ cset "name='Capture Volume' 39"
+ cset "name='Mic Boost Volume' 2"
+ cset "name='Internal Mic Boost Volume' 0"
+ #cset "name='Capture Source' 0"
+ ]
+ DisableSequence [
+ ]
+ Value {
+ TQ "Music"
+ OutputDspName "speaker_eq"
+ PlaybackPCM "hw:PCH,0"
+ }
+}
+
+SectionDevice."Headphone".0 {
+ Value {
+ JackName "Headphone Jack"
+ OutputDspName "Jheadphone"
+ }
+ EnableSequence [
+ cdev "hw:PCH"
+
+ cset "name='Speaker Playback Switch' off"
+ cset "name='Headphone Playback Switch' on"
+ ]
+ DisableSequence [
+ cdev "hw:PCH"
+
+ cset "name='Headphone Playback Switch' off"
+ cset "name='Speaker Playback Switch' on"
+ ]
+}
+
+SectionDevice."Mic".0 {
+ Value {
+ JackName "Mic Jack"
+ }
+ EnableSequence [
+ cdev "hw:PCH"
+
+ #cset "name='Capture Source' 1"
+ ]
+ DisableSequence [
+ cdev "hw:PCH"
+
+ cset "name='Capture Source' 0"
+ ]
+}
+
+SectionModifier."RecordMedia".0 {
+ SupportedDevice [
+ "Headphone"
+ ]
+ EnableSequence [
+ cdev "hw:PCH"
+ ]
+
+ DisableSequence [
+ cdev "hw:PCH"
+ ]
+
+ TransitionSequence."ToModifierName" [
+ cdev "hw:PCH"
+ ]
+
+ # Optional TQ and ALSA PCMs
+ Value {
+ TQ Voice
+ CapturePCM "hw:1"
+ PlaybackVolume "name='Master Playback Volume',index=2"
+ PlaybackSwitch "name='Master Playback Switch',index=2"
+ }
+
+}
+
diff --git a/conf.d/project/alsa.d/ucm.sample/README b/conf.d/project/alsa.d/ucm.sample/README
new file mode 100644
index 0000000..e7f08ae
--- /dev/null
+++ b/conf.d/project/alsa.d/ucm.sample/README
@@ -0,0 +1,2 @@
+Should match sound card name ex: "HDA Intel PCH"
+cp -r . /usr/share/alsa/ucm
diff --git a/conf.d/project/config.d/onload-audio-control.json b/conf.d/project/config.d/onload-audio-control.json
index 7180bd5..77a8fce 100644
--- a/conf.d/project/config.d/onload-audio-control.json
+++ b/conf.d/project/config.d/onload-audio-control.json
@@ -9,7 +9,7 @@
"label": "onload-default",
"info": "onload initialisation config",
"plugin": {
- "label" : "MyPlug",
+ "label" : "_MyPlug",
"sharelib": "ctl-audio-plugin-sample.ctlso",
"lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"]
},
@@ -28,11 +28,11 @@
"info": "Assert AlsaCore Presence",
"api": "alsacore",
"verb": "ping",
- "args": "test"
+ "args": {"data": "none"}
}, {
"label": "onload-hal-lua",
"info": "Load avaliable HALs",
- "lua": "Audio_Init_Hal"
+ "lua": "_Alsa_Get_Hal"
}
]
}],
@@ -43,20 +43,20 @@
"actions": {
"label": "multimedia-control-lua",
"info": "Call Lua Script function Test_Lua_Engin",
- "lua": "Audio_Set_Multimedia"
+ "lua": "_Audio_Set_Multimedia"
}
}, {
"label": "navigation",
"actions": {
"label": "navigation-control-lua",
"info": "Call Lua Script to set Navigation",
- "lua": "Audio_Set_Navigation"
+ "lua": "_Audio_Set_Navigation"
}
}, {
"label": "emergency",
"actions": {
"label": "emergency-control-ucm",
- "lua": "Audio_Set_Emergency"
+ "lua": "_Audio_Set_Emergency"
}
}, {
"label": "multi-step-sample",
@@ -79,7 +79,7 @@
}, {
"label": "navigation-control-lua",
"info": "Call Lua Script to set Navigation",
- "lua": "Audio_Set_Navigation"
+ "lua": "_Audio_Set_Navigation"
}]
}
],
diff --git a/conf.d/project/lua.d/doscript-helloworld.lua b/conf.d/project/lua.d/doscript-helloworld.lua
index 64bc94a..a06c3db 100644
--- a/conf.d/project/lua.d/doscript-helloworld.lua
+++ b/conf.d/project/lua.d/doscript-helloworld.lua
@@ -21,7 +21,7 @@
--]]
-function helloworld (request, query)
+local function helloworld (request, query)
AFB:notice ("LUA HelloWorld: Simple test query=%s", query);
diff --git a/conf.d/project/lua.d/onload-audio-0utils.lua b/conf.d/project/lua.d/onload-audio-0utils.lua
new file mode 100644
index 0000000..b8ecd7e
--- /dev/null
+++ b/conf.d/project/lua.d/onload-audio-0utils.lua
@@ -0,0 +1,86 @@
+--[[
+ Copyright (C) 2016 "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.
+
+ Note: this file should be called before any other to assert declare function
+ is loaded before anything else.
+
+ References:
+ http://lua-users.org/wiki/DetectingUndefinedVariables
+
+--]]
+
+
+--===================================================
+--= Niklas Frykholm
+-- basically if user tries to create global variable
+-- the system will not let them!!
+-- call GLOBAL_lock(_G)
+--
+--===================================================
+function GLOBAL_lock(t)
+ local mt = getmetatable(t) or {}
+ mt.__newindex = lock_new_index
+ setmetatable(t, mt)
+end
+
+--===================================================
+-- call GLOBAL_unlock(_G)
+-- to change things back to normal.
+--===================================================
+function GLOBAL_unlock(t)
+ local mt = getmetatable(t) or {}
+ mt.__newindex = unlock_new_index
+ setmetatable(t, mt)
+end
+
+function lock_new_index(t, k, v)
+ if (string.sub(k,1,1) ~= "_") then
+ GLOBAL_unlock(_G)
+ error("GLOBALS are locked -- " .. k ..
+ " must be declared local or prefix with '_' for globals.", 2)
+ else
+ rawset(t, k, v)
+ end
+end
+
+function unlock_new_index(t, k, v)
+ rawset(t, k, v)
+end
+
+-- return serialised version of printable table
+function Dump_Table(o)
+ if type(o) == 'table' then
+ local s = '{ '
+ for k,v in pairs(o) do
+ if type(k) ~= 'number' then k = '"'..k..'"' end
+ s = s .. '['..k..'] = ' .. Dump_Table(v) .. ','
+ end
+ return s .. '} '
+ else
+ return tostring(o)
+ end
+end
+
+
+-- simulate C prinf function
+printf = function(s,...)
+ io.write(s:format(...))
+ io.write("\n")
+ return
+end
+
+-- lock global variable
+GLOBAL_lock(_G)
diff --git a/conf.d/project/lua.d/onload-audio-controls.lua b/conf.d/project/lua.d/onload-audio-controls.lua
index 4849632..24c4f71 100644
--- a/conf.d/project/lua.d/onload-audio-controls.lua
+++ b/conf.d/project/lua.d/onload-audio-controls.lua
@@ -18,54 +18,145 @@
Provide sample policy function for AGL Advance Audio Agent
--]]
-function Audio_Init_CB (status, result, context)
- print ("--inlua:Audio_Init_CB-- result=", Dump_Table(result))
- print ("--inlua:Audio_Init_CB-- context=", Dump_Table(context))
-
- AFB:notice("Audio_Init_Hal_CB result='%s' context='%s'", result, context)
+-- Global HAL registry
+_Audio_Hal_Registry={}
+
+-- Callback when receiving HAL registry
+function _Alsa_Get_Hal_CB (error, result, context)
+ -- Initialise an empty table
+ local registry={}
+
+ -- Only process when response is valid
+ if (error) then
+ AFB_ErrOr ("[Audio_Init_CB] ErrOr result=%s", result)
+ return
+ end
+
+ -- Extract response from result
+ local response=result["response"]
+
+ -- Index HAL Bindings APIs by shortname
+ for key,value in pairs(response) do
+ registry[value["shortname"]]=value["api"]
+ end
+
+ -- store Exiting HAL for further use
+ printf ("-- [Audio_Init_CB] -- Audio_register_Hal=%s", Dump_Table(registry))
+ _Audio_Hal_Registry=registry
end
-- Function call at binding load time
-function Audio_Init_Hal(args, query)
+function _Alsa_Get_Hal(args)
- local nested = {
- ["next1"]=1234,
- ["next2"]="nested",
- ["next3"]=9999,
- }
- local context = {
- ["arg1"]=1234,
- ["arg2"]=nested,
- ["arg3"]=5678,
- }
+ printf ("[-- Audio_Get_Hal --] args=%s", Dump_Table(argsT))
+
+ -- Query AlsaCore for Active HALs (no query, no context)
+ AFB:service ('alsacore', 'hallist', {}, "_Alsa_Get_Hal_CB", {})
+
+end
- print ("--inlua:Audio_Init-- response=", Dump_Table(responseT))
+-- In sample configuration Query/Args parsing is common to all audio control
+local function Audio_Parse_Request (source, args, query)
- print("myplug=", Dump_Table(MyPlug));
+ local apihal={}
- -- This routine is defined in C sample plugin
- local status=MyPlug:Lua2cHelloWorld1(nested)
-
- -- query asynchronously loaded HAL
- AFB:service ('alsacore', 'hallist', {}, "Audio_Init_CB", context)
-
+ -- In this test we expect targeted device to be given from query (could come for args as well)
+ if (query == nil ) then
+ AFB:error ("--LUA:Audio_Set_Navigation query should contain and args with targeted apihal|device")
+ return -- refuse control
+ end
+
+ -- Alsa Hook plugin asound sample config provides target sound card by name
+ if (query["device"] ~= nil) then
+ apihal=_Audio_Hal_Registry[query["device"]]
+ end
+
+ -- HTML5 test page provides directly HAL api.
+ if (query["apihal"] ~= nil) then
+ apihal= query["apihal"]
+ end
+
+ -- if requested HAL is not found then deny the control
+ if (apihal == nil) then
+ AFB:error ("--LUA:Audio_Set_Navigation No Active HAL Found")
+ return -- refuse control
+ end
+
+ -- return api or nil when not found
+ return apihal
end
-function Audio_Set_Navigation(args, query)
+-- Set Navigation lower sound when play
+function _Audio_Set_Navigation(source, args, query)
+
+ -- in strict mode every variables should be declared
+ local err=0
+ local ctlhal={}
+ local response={}
+ local apihal={}
+
+ AFB:notice ("LUA:Audio_Set_Use_Case source=%d args=%s query=%s", source, args, query);
+
+ -- Parse Query/Args and if HAL not found then refuse access
+ apihal= Audio_Parse_Request (source, args, query)
+ if (apihal == nil) then return 1 end
+
- AFB:notice ("LUA:Audio_Set_Use_Case args=%s query=%s", args, query);
+ -- if source < 0 then Alsa HookPlugin is closing PCM
+ if (source < 0) then
+ -- Ramp Up Multimedia channel synchronously
+ ctlhal={['label']='Master_Playback_Volume', ['val']=100}
+ err, response= AFB:servsync (apihal, 'ctlset',ctlhal)
+ else
+ -- Ramp Down Multimedia channel synchronously
+ ctlhal={['label']='Master_Playback_Volume', ['val']=50}
+ err, response= AFB:servsync (apihal, 'ctlset',ctlhal)
+ end
- -- synchronous call to alsacore service
- local error,data= AFB:callsync ('alsacore', 'ping', {})
- if (error) then
- AFB:error ("LUA:Audio_Set_Use_Case FAIL args=%s", args)
- else
- AFB:notice ("--LUA:Audio_Set_Use_Case DONE args=%s response=%s", args, data["response"])
+ if (err) then
+ AFB:error("--LUA:Audio_Set_Navigation halapi=%s refuse ctl=%s", apihal, ctlhal)
+ return 1 -- control refused
end
- -- return OK
- return 0
+
+ return 0 -- control accepted
end
+-- Select Multimedia mode
+function _Audio_Set_Multimedia (source, args, query)
+
+ -- in strict mode every variables should be declared
+ local err=0
+ local ctlhal={}
+ local response={}
+ local apihal={}
+
+ AFB:notice ("LUA:Audio_Set_Use_Case source=%d args=%s query=%s", source, args, query);
+
+ -- Parse Query/Args and if HAL not found then refuse access
+ apihal= Audio_Parse_Request (source, args, query)
+ if (apihal == nil) then return 1 end
+
+
+ -- if Mumtimedia control only increase volume on open
+ if (source >= 0) then
+ -- Ramp Down Multimedia channel synchronously
+ ctlhal={['label']='Master_Playback_Volume', ['val']=100}
+ err, response= AFB:servsync (apihal, 'ctlset',ctlhal)
+ end
+
+ if (err) then
+ AFB:error("--LUA:Audio_Set_Navigation halapi=%s refuse ctl=%s", apihal, ctlhal)
+ return 1 -- control refused
+ end
+
+
+ return 0 -- control accepted
+end
+
+-- Select Emergency Mode
+function _Audio_Set_Emergency(source, args, query)
+ return 1 -- Always refuse in this test
+end
diff --git a/conf.d/project/lua.d/onload-audio-oncall.lua b/conf.d/project/lua.d/onload-audio-oncall.lua
index 08b25e5..23b538e 100644
--- a/conf.d/project/lua.d/onload-audio-oncall.lua
+++ b/conf.d/project/lua.d/onload-audio-oncall.lua
@@ -18,18 +18,18 @@
Provide sample LUA routines to be used with AGL control "lua_docall" API
--]]
--- global counter to keep track of calls
-count=0
+--global counter to keep track of calls
+_count=0
-- Display receive arguments and echo them to caller
-function Simple_Echo_Args (request, args)
- count=count+1
+function _Simple_Echo_Args (request, args)
+ _count=_count+1
AFB:notice("LUA OnCall Echo Args count=%d args=%s", count, args)
print ("--inlua-- args=", Dump_Table(args))
local response={
- ["count"]=count,
+ ["count"]=_count,
["args"]=args,
}
@@ -37,7 +37,7 @@ function Simple_Echo_Args (request, args)
AFB:success (request, {["func"]="Simple_Echo_Args", ["ret1"]=5678, ["ret2"]="abcd"})
end
-function Test_Async_CB (request, result, context)
+local function Test_Async_CB (request, result, context)
response={
["result"]=result,
["context"]=context,
@@ -47,7 +47,7 @@ function Test_Async_CB (request, result, context)
AFB:success (request, response)
end
-function Test_Call_Async (request, args)
+function _Test_Call_Async (request, args)
local context={
["value1"]="abcd",
["value2"]=1234
@@ -57,7 +57,7 @@ function Test_Call_Async (request, args)
AFB:service("alsacore","ping", "Test_Async_CB", context)
end
-function Test_Call_Sync (request, args)
+function _Test_Call_Sync (request, args)
AFB:notice ("Test_Call_Sync args=%s", args)
local err, response= AFB:service_sync ("alsacore","ping", args)
diff --git a/conf.d/project/lua.d/onload-audio-timer.lua b/conf.d/project/lua.d/onload-audio-timer.lua
index 5f25de7..db7a937 100644
--- a/conf.d/project/lua.d/onload-audio-timer.lua
+++ b/conf.d/project/lua.d/onload-audio-timer.lua
@@ -19,11 +19,10 @@
--]]
-- Create event on Lua script load
-print ("*** Create MyTestEvent ***")
-MyEventHandle= AFB:evtmake("MyTestEvent")
+local MyEventHandle=AFB:evtmake("MyTestEvent")
-- Call count time every delay/ms
-function Timer_Test_CB (timer, context)
+local function Timer_Test_CB (timer, context)
local evtinfo= AFB:timerget(timer)
print ("timer=", Dump_Table(evtinfo))
@@ -37,7 +36,7 @@ function Timer_Test_CB (timer, context)
end
-- sendback event depending on count and delay
-function Simple_Timer_Test (request, args)
+function _Simple_Timer_Test (request, args)
local context = {
["info"]="My 1st private Event",
@@ -68,6 +67,3 @@ function Simple_Timer_Test (request, args)
return 0
end
-
-
-
diff --git a/conf.d/project/lua.d/onload-audio-utils.lua b/conf.d/project/lua.d/onload-audio-utils.lua
deleted file mode 100644
index 40ef8a3..0000000
--- a/conf.d/project/lua.d/onload-audio-utils.lua
+++ /dev/null
@@ -1,32 +0,0 @@
---[[
- Copyright (C) 2016 "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.
-
---]]
-
-
--- return serialised version of printable table
-function Dump_Table(o)
- if type(o) == 'table' then
- local s = '{ '
- for k,v in pairs(o) do
- if type(k) ~= 'number' then k = '"'..k..'"' end
- s = s .. '['..k..'] = ' .. Dump_Table(v) .. ','
- end
- return s .. '} '
- else
- return tostring(o)
- end
-end