aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt23
-rw-r--r--LICENSE201
-rw-r--r--README.md1
-rw-r--r--conf.d/CMakeLists.txt22
-rwxr-xr-xconf.d/autobuild/agl/autobuild63
-rwxr-xr-xconf.d/autobuild/linux/autobuild63
-rw-r--r--conf.d/cmake/config.cmake156
-rw-r--r--conf.d/project/CMakeLists.txt24
-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--src/CMakeLists.txt52
-rw-r--r--src/ahl-apidef.h297
-rw-r--r--src/ahl-apidef.json601
-rw-r--r--src/ahl-binding.c450
-rw-r--r--src/ahl-binding.h96
-rw-r--r--src/ahl-deviceenum.c40
18 files changed, 2327 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..2f42d5a
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,23 @@
+###########################################################################
+# Copyright 2017 Audiokinetic.com
+#
+# author: Francois Thibault <fthibault@audiokinetic.com>
+#
+# 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.
+###########################################################################
+
+CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
+
+# Do not change this file, config is located into conf.d
+include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d73cf13
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# afb-audiohighlevel
diff --git a/conf.d/CMakeLists.txt b/conf.d/CMakeLists.txt
new file mode 100644
index 0000000..28a0609
--- /dev/null
+++ b/conf.d/CMakeLists.txt
@@ -0,0 +1,22 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 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.
+###########################################################################
+
+
+# Include any directory not starting with _
+# -----------------------------------------------------
+PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN})
diff --git a/conf.d/autobuild/agl/autobuild b/conf.d/autobuild/agl/autobuild
new file mode 100755
index 0000000..4811441
--- /dev/null
+++ b/conf.d/autobuild/agl/autobuild
@@ -0,0 +1,63 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015, 2016 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@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.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build)
+DEST := ${BUILD_DIR}/target
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all"
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build"
+ @echo "- package"
+ @echo ""
+ @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+update: configure
+ @cmake --build ${BUILD_DIR} --target autobuild
+
+clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean
+
+distclean:
+ @rm -rf ${BUILD_DIR}
+
+configure: ${BUILD_DIR}/Makefile
+
+build: configure
+ @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
+
+package: build
+ @mkdir -p ${BUILD_DIR}/$@/bin
+ @mkdir -p ${BUILD_DIR}/$@/etc
+ @mkdir -p ${BUILD_DIR}/$@/lib
+ @mkdir -p ${BUILD_DIR}/$@/htdocs
+ @mkdir -p ${BUILD_DIR}/$@/data
+ @cmake --build ${BUILD_DIR} --target widget
+ @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST}
+
+${BUILD_DIR}/Makefile:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
diff --git a/conf.d/autobuild/linux/autobuild b/conf.d/autobuild/linux/autobuild
new file mode 100755
index 0000000..4811441
--- /dev/null
+++ b/conf.d/autobuild/linux/autobuild
@@ -0,0 +1,63 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015, 2016 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@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.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build)
+DEST := ${BUILD_DIR}/target
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all"
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build"
+ @echo "- package"
+ @echo ""
+ @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+update: configure
+ @cmake --build ${BUILD_DIR} --target autobuild
+
+clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean
+
+distclean:
+ @rm -rf ${BUILD_DIR}
+
+configure: ${BUILD_DIR}/Makefile
+
+build: configure
+ @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
+
+package: build
+ @mkdir -p ${BUILD_DIR}/$@/bin
+ @mkdir -p ${BUILD_DIR}/$@/etc
+ @mkdir -p ${BUILD_DIR}/$@/lib
+ @mkdir -p ${BUILD_DIR}/$@/htdocs
+ @mkdir -p ${BUILD_DIR}/$@/data
+ @cmake --build ${BUILD_DIR} --target widget
+ @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST}
+
+${BUILD_DIR}/Makefile:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
new file mode 100644
index 0000000..08e79fc
--- /dev/null
+++ b/conf.d/cmake/config.cmake
@@ -0,0 +1,156 @@
+###########################################################################
+# Copyright 2017 Audiokinetic
+#
+# author: Tai Vuong <tvuong@audiokinetic.com>
+#
+# 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.
+###########################################################################
+
+# Project Info
+# ------------------
+set(PROJECT_NAME afb-audiohighlevel)
+set(PROJECT_VERSION "0.1")
+set(PROJECT_PRETTY_NAME "Audio High Level Binding")
+set(PROJECT_DESCRIPTION "Give an High Level Binding for all AGL applications")
+set(PROJECT_URL "https://github.com/Audiokinetic-Automotive/afb-audiohighlevel")
+set(PROJECT_ICON "icon.png")
+set(PROJECT_AUTHOR "Tai, Vuong")
+set(PROJECT_AUTHOR_MAIL "tvuong@audiokinetic.com")
+set(PROJECT_LICENCE "Apache-V2")
+set(PROJECT_LANGUAGES,"C")
+
+
+# Where are stored default templates files from submodule or subtree app-templates in your project tree
+# relative to the root project directory
+set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates")
+
+# Use any directory that does not start with _ as valid source rep
+set(PROJECT_SRC_DIR_PATTERN "[^_]*")
+
+# Compilation Mode (DEBUG, RELEASE)
+# ----------------------------------
+set(CMAKE_BUILD_TYPE "DEBUG")
+
+# Compiler selection if needed. Overload the detected compiler.
+# -----------------------------------------------
+set (gcc_minimal_version 4.9)
+#set(CMAKE_C_COMPILER "gcc")
+#set(CMAKE_CXX_COMPILER "g++")
+
+# When Present LUA is used by the controller
+# ---------------------------------------------------------------
+set(CONTROL_SUPPORT_LUA 1 CACHE BOOL "Active or not LUA Support")
+
+# PKG_CONFIG required packages
+# -----------------------------
+set (PKG_REQUIRED_LIST
+ alsa>=1.1.2
+ libsystemd>=222
+ libmicrohttpd>=0.9.55
+ afb-daemon
+ json-c
+ libafbwsc
+)
+
+# Compilation options definition
+# Use CMake generator expressions to specify only for a specific language
+# Values are prefilled with default options that is currently used.
+# Either separate options with ";", or each options must be quoted separately
+# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED !
+# ----------------------------------------------------------------------------
+set(COMPILE_OPTIONS
+-Wall
+-Wextra
+-Wconversion
+-Wno-unused-parameter
+-Wno-sign-compare
+-Wno-sign-conversion
+-Werror=maybe-uninitialized
+-Werror=implicit-function-declaration
+-ffunction-sections
+-fdata-sections
+-fPIC
+# Personal compilation options
+-DMAX_SND_CARD=16 # should be more than enough even in luxury vehicule
+-DMAX_LINEAR_DB_SCALE=24 # until 24db volume normalisation use a simple linear scale
+-DTLV_BYTE_SIZE=256 # Alsa use 4096 as default but 256 should fit most sndcards
+-DCONTROL_MAXPATH_LEN=255
+ CACHE STRING "Compilation flags")
+#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.")
+#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.")
+#set(PROFILING_COMPILE_OPTIONS -g -O0 -pg -Wp,-U_FORTIFY_SOURCE CACHE STRING "Compilation flags for PROFILING build type.")
+#set(DEBUG_COMPILE_OPTIONS -g -ggdb -Wp,-U_FORTIFY_SOURCE CACHE STRING "Compilation flags for DEBUG build type.")
+#set(CCOV_COMPILE_OPTIONS -g -O2 --coverage CACHE STRING "Compilation flags for CCOV build type.")
+#set(RELEASE_COMPILE_OPTIONS -g -O2 CACHE STRING "Compilation flags for RELEASE build type.")
+
+# Print a helper message when every thing is finished
+# ----------------------------------------------------
+if(IS_DIRECTORY $ENV{HOME}/opt/afb-monitoring)
+set(MONITORING_ALIAS "--alias=/monitoring:$ENV{HOME}/opt/afb-monitoring")
+endif()
+
+
+
+# Optional location for config.xml.in
+# -----------------------------------
+#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon")
+#set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)")
+
+# Optional dependencies order
+# ---------------------------
+#set(EXTRA_DEPENDENCIES_ORDER)
+
+# Optional Extra global include path
+# -----------------------------------
+# set(EXTRA_INCLUDE_DIRS)
+
+# Optional extra libraries
+# -------------------------
+# set(EXTRA_LINK_LIBRARIES)
+
+# Optional force binding installation
+# ------------------------------------
+# set(BINDINGS_INSTALL_PREFIX PrefixPath )
+
+# Optional force widget prefix generation
+# ------------------------------------------------
+# set(WIDGET_PREFIX DestinationPath)
+
+# Optional Widget entry point file.
+# ---------------------------------------------------------
+# This is the file that will be executed, loaded,...
+# at launch time by the application framework
+set(WIDGET_ENTRY_POINT lib/afb-audiohighlevel.so)
+
+# Optional Widget Mimetype specification
+# --------------------------------------------------
+# Choose between :
+# - application/x-executable
+# - application/vnd.agl.url
+# - application/vnd.agl.service
+# - application/vnd.agl.native
+# - text/vnd.qt.qml
+# - application/vnd.agl.qml
+# - application/vnd.agl.qml.hybrid
+# - application/vnd.agl.html.hybrid
+#
+set(WIDGET_TYPE application/vnd.agl.service)
+
+# Optional force binding Linking flag
+# ------------------------------------
+# set(BINDINGS_LINK_FLAG LinkOptions )
+
+# This include is mandatory and MUST happens at the end
+# of this file, else you expose you to unexpected behavior
+# -----------------------------------------------------------
+include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake)
diff --git a/conf.d/project/CMakeLists.txt b/conf.d/project/CMakeLists.txt
new file mode 100644
index 0000000..fd4d454
--- /dev/null
+++ b/conf.d/project/CMakeLists.txt
@@ -0,0 +1,24 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 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.
+###########################################################################
+
+
+
+# Include anything not starting with _
+PROJECT_SUBDIRS_ADD(${PROJECT_SRC_DIR_PATTERN})
+
+
diff --git a/conf.d/project/alsa.d/asoundrc.sample b/conf.d/project/alsa.d/asoundrc.sample
new file mode 100644
index 0000000..8976077
--- /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&uuid=audio-agent-session"
+ 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/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..cbfe5dc
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,52 @@
+###########################################################################
+# Copyright 2017 Audiokinetic.com
+#
+# author: Francois Thibault <fthibault@audiokinetic.com>
+#
+# 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.
+###########################################################################
+# Generate API-v2 hat from OpenAPI json definition
+macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME)
+ add_custom_command(OUTPUT ${API_DEF_NAME}.h
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DEPENDS ${API_DEF_NAME}.json
+ COMMAND afb-genskel ${API_DEF_NAME}.json >${API_DEF_NAME}.h
+ )
+ add_custom_target(${API_DEF_NAME}_OPENAPI DEPENDS ${API_DEF_NAME}.h)
+ add_dependencies(${TARGET_NAME} ${API_DEF_NAME}_OPENAPI)
+
+endmacro(SET_TARGET_GENSKEL)
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(audiohighlevel-afb)
+
+ # Define project Targets
+ ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c)
+
+ # Generate API-v2 hat from OpenAPI json definition
+ SET_TARGET_GENSKEL(${TARGET_NAME} ahl-apidef)
+
+ # Binder exposes a unique public entry point
+ SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ PREFIX "afb-"
+ LABELS "BINDING"
+ LINK_FLAGS ${BINDINGS_LINK_FLAG}
+ OUTPUT_NAME ${TARGET_NAME}
+ )
+
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ afb-utilities
+ ${link_libraries}
+ )
+
diff --git a/src/ahl-apidef.h b/src/ahl-apidef.h
new file mode 100644
index 0000000..fa78681
--- /dev/null
+++ b/src/ahl-apidef.h
@@ -0,0 +1,297 @@
+
+static const char _afb_description_v2_audiohl[] =
+ "{\"openapi\":\"3.0.0\",\"$schema\":\"http:iot.bzh/download/openapi/schem"
+ "a-3.0/default-schema.json\",\"info\":{\"description\":\"Audio high level"
+ " API for AGL applications\",\"title\":\"audiohighlevel\",\"version\":\"1"
+ ".0\",\"x-binding-c-generator\":{\"api\":\"audiohl\",\"version\":2,\"pref"
+ "ix\":\"audiohlapi_\",\"postfix\":\"\",\"start\":null,\"onevent\":null,\""
+ "init\":\"AhlBindingInit\",\"scope\":\"\",\"private\":false}},\"servers\""
+ ":[{\"url\":\"ws://{host}:{port}/api/audiohl\",\"description\":\"Audio hi"
+ "gh level API for AGL applications.\",\"variables\":{\"host\":{\"default\""
+ ":\"localhost\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\":[{\"$r"
+ "ef\":\"#/components/schemas/afb-event\"}]}],\"components\":{\"schemas\":"
+ "{\"afb-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"afb-ev"
+ "ent\":{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply-v2\":"
+ "{\"title\":\"Generic response.\",\"type\":\"object\",\"required\":[\"jty"
+ "pe\",\"request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const"
+ "\":\"afb-reply\"},\"request\":{\"type\":\"object\",\"required\":[\"statu"
+ "s\"],\"properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\""
+ ":\"string\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string"
+ "\"},\"reqid\":{\"type\":\"string\"}}},\"response\":{\"type\":\"object\"}"
+ "}},\"afb-event-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"event"
+ "\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event"
+ "\"},\"event\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}},\"e"
+ "ndpoint_info\":{\"type\":\"object\",\"required\":[\"endpoint_id\",\"type"
+ "\",\"name\"],\"properties\":{\"endpoint_id\":{\"type\":\"int\"},\"type\""
+ ":{\"type\":\"enum\"},\"name\":{\"type\":\"string\"}}},\"stream_info\":{\""
+ "type\":\"object\",\"required\":[\"stream_id\",\"pcm_name\",\"name\"],\"p"
+ "roperties\":{\"stream_id\":{\"type\":\"int\"},\"pcm_name\":{\"type\":\"s"
+ "tring\"},\"$ref\":\"#/components/schemas/endpoint_info\"}},\"routing_inf"
+ "o\":{\"type\":\"object\",\"required\":[\"routing_id\",\"source_id\",\"si"
+ "nk_id\"],\"properties\":{\"routing_id\":{\"type\":\"int\"},\"source_id\""
+ ":{\"type\":\"int\"},\"sink_id\":{\"type\":\"int\"}}}},\"x-permissions\":"
+ "{\"streamcontrol\":{\"permission\":\"urn:AGL:permission:audio:public:str"
+ "eamcontrol\"},\"routingcontrol\":{\"permission\":\"urn:AGL:permission:au"
+ "dio:public:routingcontrol\"},\"soundevent\":{\"permission\":\"urn:AGL:pe"
+ "rmission:audio:public:soundevent\"}},\"responses\":{\"200\":{\"descripti"
+ "on\":\"A complex object array response\",\"content\":{\"application/json"
+ "\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"}}}},\"400\":"
+ "{\"description\":\"Invalid arguments\"}}},\"paths\":{\"/get_sources\":{\""
+ "description\":\"Retrieve array of available audio sources\",\"get\":{\"p"
+ "arameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":fals"
+ "e,\"schema\":{\"type\":\"enum\"}}],\"responses\":{\"200\":{\"$ref\":\"#/"
+ "components/responses/200\",\"response\":{\"description\":\"Array of endp"
+ "oint info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/compon"
+ "ents/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/respons"
+ "es/400\"}}}},\"/get_sinks\":{\"description\":\"Retrieve array of availab"
+ "le audio sinks\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"a"
+ "udio_role\",\"required\":false,\"schema\":{\"type\":\"enum\"}}],\"respon"
+ "ses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"response\":{\""
+ "description\":\"Array of endpoint info structures\",\"type\":\"array\",\""
+ "items\":{\"$ref\":\"#/components/schemas/endpoint_info\"}}},\"400\":{\"$"
+ "ref\":\"#/components/responses/400\"}}}},\"/stream_open\":{\"description"
+ "\":\"Request opening a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#"
+ "/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"que"
+ "ry\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"en"
+ "um\"}},{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\""
+ "schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\""
+ ",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200"
+ "\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"description\""
+ ":\"Stream information structure\",\"$ref\":\"#/components/schemas/stream"
+ "_info\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/stream"
+ "_close\":{\"description\":\"Request closing a stream\",\"get\":{\"x-perm"
+ "issions\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"para"
+ "meters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":true,\"s"
+ "chema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compon"
+ "ents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}"
+ "}},\"/get_available_routings\":{\"description\":\"Retrieve array of avai"
+ "lable routing info structures\",\"get\":{\"parameters\":[{\"in\":\"query"
+ "\",\"name\":\"audio_role\",\"required\":false,\"schema\":{\"type\":\"enu"
+ "m\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\""
+ "response\":{\"description\":\"Array of routing info structures\",\"type\""
+ ":\"array\",\"items\":{\"description\":\"Routing info structure {routingI"
+ "D, sourceID, sinkID }\",\"type\":\"object\"}}},\"400\":{\"$ref\":\"#/com"
+ "ponents/responses/400\"}}}},\"/add_routing\":{\"description\":\"Request "
+ "a routing connection between available devices\",\"get\":{\"x-permission"
+ "s\":{\"$ref\":\"#/components/x-permissions/routingcontrol\"},\"parameter"
+ "s\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schem"
+ "a\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"routing_id\",\"req"
+ "uired\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\""
+ "$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"Ro"
+ "uting information structure\",\"$ref\":\"#/components/schemas/routing_in"
+ "fo\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/remove_ro"
+ "uting\":{\"description\":\"Request to remove a routing connection betwee"
+ "n devices\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permis"
+ "sions/routingcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"rou"
+ "ting_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\""
+ ":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\""
+ "#/components/responses/400\"}}}},\"/set_endpoint_volume\":{\"description"
+ "\":\"Set endpoint volume\",\"get\":{\"x-permissions\":{\"$ref\":\"#/comp"
+ "onents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\","
+ "\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum"
+ "\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"sche"
+ "ma\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"volume\",\"require"
+ "d\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\""
+ "ramp_time_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"resp"
+ "onses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$r"
+ "ef\":\"#/components/responses/400\"}}}},\"/get_endpoint_volume\":{\"desc"
+ "ription\":\"Get endpoint volume\",\"get\":{\"parameters\":[{\"in\":\"que"
+ "ry\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\""
+ "enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\""
+ "schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compo"
+ "nents/responses/200\",\"response\":{\"description\":\"Endpoint volume va"
+ "lue\",\"type\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/4"
+ "00\"}}}},\"/set_endpoint_property\":{\"description\":\"Set endpoint prop"
+ "erty value\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permi"
+ "ssions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"end"
+ "point_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\""
+ "query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\""
+ ":\"int\"}},{\"in\":\"query\",\"name\":\"property_name\",\"required\":tru"
+ "e,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"value\""
+ ",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\""
+ "name\":\"ramp_time_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}"
+ "}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"40"
+ "0\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_endpoint_propert"
+ "y\":{\"description\":\"Get endpoint property value\",\"get\":{\"paramete"
+ "rs\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"s"
+ "chema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\","
+ "\"required\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"na"
+ "me\":\"property_name\",\"required\":true,\"schema\":{\"type\":\"string\""
+ "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"re"
+ "sponse\":{\"description\":\"Property value\",\"type\":\"double\"}},\"400"
+ "\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_endpoint_state\":"
+ "{\"description\":\"Set endpoint state\",\"get\":{\"x-permissions\":{\"$r"
+ "ef\":\"#/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in"
+ "\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\""
+ "type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\""
+ ":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_"
+ "name\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"que"
+ "ry\",\"name\":\"state_value\",\"required\":true,\"schema\":{\"type\":\"s"
+ "tring\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200"
+ "\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_endpoint"
+ "_state\":{\"description\":\"Get endpoint state value\",\"get\":{\"parame"
+ "ters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\""
+ "schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\""
+ ",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"na"
+ "me\":\"state_name\",\"required\":true,\"schema\":{\"type\":\"string\"}}]"
+ ",\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"respo"
+ "nse\":{\"description\":\"Endpoint state value\",\"type\":\"string\"}},\""
+ "400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post_sound_event\""
+ ":{\"description\":\"Post sound event\",\"get\":{\"x-permissions\":{\"$re"
+ "f\":\"#/components/x-permissions/soundevent\"},\"parameters\":[{\"in\":\""
+ "query\",\"name\":\"event_name\",\"required\":true,\"schema\":{\"type\":\""
+ "string\"}},{\"in\":\"query\",\"name\":\"audio_role\",\"required\":false,"
+ "\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"media_name\""
+ ",\"required\":false,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\","
+ "\"name\":\"audio_context\",\"required\":false,\"schema\":{\"type\":\"obj"
+ "ect\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\""
+ "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/subscribe\":{\""
+ "description\":\"Subscribe to audio high level events\",\"get\":{\"parame"
+ "ters\":[{\"in\":\"query\",\"name\":\"events\",\"required\":true,\"schema"
+ "\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}}],\"responses\":"
+ "{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#"
+ "/components/responses/400\"}}}}}}"
+;
+
+static const struct afb_auth _afb_auths_v2_audiohl[] = {
+ { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:streamcontrol" },
+ { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:routingcontrol" },
+ { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" }
+};
+
+ void audiohlapi_get_sources(struct afb_req req);
+ void audiohlapi_get_sinks(struct afb_req req);
+ void audiohlapi_stream_open(struct afb_req req);
+ void audiohlapi_stream_close(struct afb_req req);
+ void audiohlapi_get_available_routings(struct afb_req req);
+ void audiohlapi_add_routing(struct afb_req req);
+ void audiohlapi_remove_routing(struct afb_req req);
+ void audiohlapi_set_endpoint_volume(struct afb_req req);
+ void audiohlapi_get_endpoint_volume(struct afb_req req);
+ void audiohlapi_set_endpoint_property(struct afb_req req);
+ void audiohlapi_get_endpoint_property(struct afb_req req);
+ void audiohlapi_set_endpoint_state(struct afb_req req);
+ void audiohlapi_get_endpoint_state(struct afb_req req);
+ void audiohlapi_post_sound_event(struct afb_req req);
+ void audiohlapi_subscribe(struct afb_req req);
+
+static const struct afb_verb_v2 _afb_verbs_v2_audiohl[] = {
+ {
+ .verb = "get_sources",
+ .callback = audiohlapi_get_sources,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "get_sinks",
+ .callback = audiohlapi_get_sinks,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "stream_open",
+ .callback = audiohlapi_stream_open,
+ .auth = &_afb_auths_v2_audiohl[0],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "stream_close",
+ .callback = audiohlapi_stream_close,
+ .auth = &_afb_auths_v2_audiohl[0],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "get_available_routings",
+ .callback = audiohlapi_get_available_routings,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "add_routing",
+ .callback = audiohlapi_add_routing,
+ .auth = &_afb_auths_v2_audiohl[1],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "remove_routing",
+ .callback = audiohlapi_remove_routing,
+ .auth = &_afb_auths_v2_audiohl[1],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "set_endpoint_volume",
+ .callback = audiohlapi_set_endpoint_volume,
+ .auth = &_afb_auths_v2_audiohl[0],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "get_endpoint_volume",
+ .callback = audiohlapi_get_endpoint_volume,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "set_endpoint_property",
+ .callback = audiohlapi_set_endpoint_property,
+ .auth = &_afb_auths_v2_audiohl[0],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "get_endpoint_property",
+ .callback = audiohlapi_get_endpoint_property,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "set_endpoint_state",
+ .callback = audiohlapi_set_endpoint_state,
+ .auth = &_afb_auths_v2_audiohl[0],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "get_endpoint_state",
+ .callback = audiohlapi_get_endpoint_state,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "post_sound_event",
+ .callback = audiohlapi_post_sound_event,
+ .auth = &_afb_auths_v2_audiohl[2],
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = "subscribe",
+ .callback = audiohlapi_subscribe,
+ .auth = NULL,
+ .info = NULL,
+ .session = AFB_SESSION_NONE_V2
+ },
+ { .verb = NULL }
+};
+
+const struct afb_binding_v2 afbBindingV2 = {
+ .api = "audiohl",
+ .specification = _afb_description_v2_audiohl,
+ .info = NULL,
+ .verbs = _afb_verbs_v2_audiohl,
+ .preinit = NULL,
+ .init = AhlBindingInit,
+ .onevent = NULL,
+ .noconcurrency = 0
+};
+
diff --git a/src/ahl-apidef.json b/src/ahl-apidef.json
new file mode 100644
index 0000000..cbe25a9
--- /dev/null
+++ b/src/ahl-apidef.json
@@ -0,0 +1,601 @@
+{
+ "openapi": "3.0.0",
+ "$schema": "http:iot.bzh/download/openapi/schema-3.0/default-schema.json",
+ "info": {
+ "description": "Audio high level API for AGL applications",
+ "title": "audiohighlevel",
+ "version": "1.0",
+ "x-binding-c-generator": {
+ "api": "audiohl",
+ "version": 2,
+ "prefix": "audiohlapi_",
+ "postfix": "",
+ "start": null,
+ "onevent": null,
+ "init": "AhlBindingInit",
+ "scope": "",
+ "private": false
+ }
+ },
+ "servers": [
+ {
+ "url": "ws://{host}:{port}/api/audiohl",
+ "description": "Audio high level API for AGL applications.",
+ "variables": {
+ "host": {
+ "default": "localhost"
+ },
+ "port": {
+ "default": "1234"
+ }
+ },
+ "x-afb-events": [
+ {
+ "$ref": "#/components/schemas/afb-event"
+ }
+ ]
+ }
+ ],
+ "components": {
+ "schemas": {
+ "afb-reply": {
+ "$ref": "#/components/schemas/afb-reply-v2"
+ },
+ "afb-event": {
+ "$ref": "#/components/schemas/afb-event-v2"
+ },
+ "afb-reply-v2": {
+ "title": "Generic response.",
+ "type": "object",
+ "required": ["jtype", "request"],
+ "properties": {
+ "jtype": {
+ "type": "string",
+ "const": "afb-reply"
+ },
+ "request": {
+ "type": "object",
+ "required": ["status"],
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "info": {
+ "type": "string"
+ },
+ "token": {
+ "type": "string"
+ },
+ "uuid": {
+ "type": "string"
+ },
+ "reqid": {
+ "type": "string"
+ }
+ }
+ },
+ "response": {
+ "type": "object"
+ }
+ }
+ },
+ "afb-event-v2": {
+ "type": "object",
+ "required": ["jtype", "event"],
+ "properties": {
+ "jtype": {
+ "type": "string",
+ "const": "afb-event"
+ },
+ "event": {
+ "type": "string"
+ },
+ "data": {
+ "type": "object"
+ }
+ }
+ },
+ "endpoint_info": {
+ "type": "object",
+ "required": [ "endpoint_id", "type", "name" ],
+ "properties": {
+ "endpoint_id": { "type": "int" },
+ "type": { "type": "enum" },
+ "name": { "type": "string" }
+ }
+ },
+ "stream_info": {
+ "type": "object",
+ "required": [ "stream_id", "pcm_name", "name" ],
+ "properties": {
+ "stream_id": { "type": "int" },
+ "pcm_name": { "type": "string" },
+ "$ref": "#/components/schemas/endpoint_info"
+ }
+ },
+ "routing_info": {
+ "type": "object",
+ "required": [ "routing_id", "source_id", "sink_id" ],
+ "properties": {
+ "routing_id": { "type": "int" },
+ "source_id": { "type": "int" },
+ "sink_id": { "type": "int" }
+ }
+ }
+ },
+ "x-permissions": {
+ "streamcontrol": {
+ "permission": "urn:AGL:permission:audio:public:streamcontrol"
+ },
+ "routingcontrol": {
+ "permission": "urn:AGL:permission:audio:public:routingcontrol"
+ },
+ "soundevent": {
+ "permission": "urn:AGL:permission:audio:public:soundevent"
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "A complex object array response",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/afb-reply"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid arguments"
+ }
+ }
+ },
+ "paths": {
+ "/get_sources": {
+ "description": "Retrieve array of available audio sources",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "audio_role",
+ "required": false,
+ "schema": { "type": "enum" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Array of endpoint info structures",
+ "type": "array",
+ "items": { "$ref": "#/components/schemas/endpoint_info"}
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/get_sinks": {
+ "description": "Retrieve array of available audio sinks",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "audio_role",
+ "required": false,
+ "schema": { "type": "enum" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Array of endpoint info structures",
+ "type": "array",
+ "items": { "$ref": "#/components/schemas/endpoint_info"}
+ }
+ },
+ "400": {
+ "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/stream_open": {
+ "description": "Request opening a stream",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "audio_role",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": false,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Stream information structure",
+ "$ref": "#/components/schemas/stream_info"
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/stream_close": {
+ "description": "Request closing a stream",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "stream_id",
+ "required": true,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/get_available_routings": {
+ "description": "Retrieve array of available routing info structures",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "audio_role",
+ "required": false,
+ "schema": { "type": "enum" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Array of routing info structures",
+ "type": "array",
+ "items": {
+ "description": "Routing info structure {routingID, sourceID, sinkID }",
+ "type": "object"
+ }
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/add_routing": {
+ "description": "Request a routing connection between available devices",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/routingcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "audio_role",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "routing_id",
+ "required": false,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Routing information structure",
+ "$ref": "#/components/schemas/routing_info"
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/remove_routing": {
+ "description": "Request to remove a routing connection between devices",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/routingcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "routing_id",
+ "required": true,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/set_endpoint_volume": {
+ "description": "Set endpoint volume",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": true,
+ "schema": { "type": "int" }
+ },
+ {
+ "in": "query",
+ "name": "volume",
+ "required": true,
+ "schema": { "type": "string" }
+ },
+ {
+ "in": "query",
+ "name": "ramp_time_ms",
+ "required": false,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/get_endpoint_volume": {
+ "description": "Get endpoint volume",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": true,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Endpoint volume value",
+ "type": "double"
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/set_endpoint_property": {
+ "description": "Set endpoint property value",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": false,
+ "schema": { "type": "int" }
+ },
+ {
+ "in": "query",
+ "name": "property_name",
+ "required": true,
+ "schema": { "type": "string" }
+ },
+ {
+ "in": "query",
+ "name": "value",
+ "required": true,
+ "schema": { "type": "string" }
+ },
+ {
+ "in": "query",
+ "name": "ramp_time_ms",
+ "required": false,
+ "schema": { "type": "int" }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/get_endpoint_property": {
+ "description": "Get endpoint property value",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": false,
+ "schema": { "type": "int" }
+ },
+ {
+ "in": "query",
+ "name": "property_name",
+ "required": true,
+ "schema": { "type": "string" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Property value",
+ "type": "double"
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/set_endpoint_state": {
+ "description": "Set endpoint state",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": true,
+ "schema": { "type": "int" }
+ },
+ {
+ "in": "query",
+ "name": "state_name",
+ "required": true,
+ "schema": { "type": "string" }
+ },
+ {
+ "in": "query",
+ "name": "state_value",
+ "required": true,
+ "schema": { "type": "string" }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/get_endpoint_state": {
+ "description": "Get endpoint state value",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "endpoint_type",
+ "required": true,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "endpoint_id",
+ "required": true,
+ "schema": { "type": "int" }
+ },
+ {
+ "in": "query",
+ "name": "state_name",
+ "required": true,
+ "schema": { "type": "string" }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200",
+ "response": {
+ "description": "Endpoint state value",
+ "type": "string"
+ }
+ },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/post_sound_event": {
+ "description": "Post sound event",
+ "get": {
+ "x-permissions": { "$ref": "#/components/x-permissions/soundevent" },
+ "parameters": [
+ {
+ "in": "query",
+ "name": "event_name",
+ "required": true,
+ "schema": { "type": "string" }
+ },
+ {
+ "in": "query",
+ "name": "audio_role",
+ "required": false,
+ "schema": { "type": "enum" }
+ },
+ {
+ "in": "query",
+ "name": "media_name",
+ "required": false,
+ "schema": { "type": "string"}
+ },
+ {
+ "in": "query",
+ "name": "audio_context",
+ "required": false,
+ "schema": { "type": "object" }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ },
+ "/subscribe": {
+ "description": "Subscribe to audio high level events",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "events",
+ "required": true,
+ "schema": { "type": "array",
+ "items": { "type": "string" }
+ }
+ }
+ ],
+ "responses": {
+ "200": { "$ref": "#/components/responses/200" },
+ "400": { "$ref": "#/components/responses/400" }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ahl-binding.c b/src/ahl-binding.c
new file mode 100644
index 0000000..99236bd
--- /dev/null
+++ b/src/ahl-binding.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ * Author Francois Thibault <fthibault@audiokinetic.com>
+ *
+ * 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
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "ahl-binding.h"
+#include "ahl-apidef.h" // Generated from JSON OpenAPI
+#include "wrap-json.h"
+
+// TODO: json_object_put to free JSON objects? potential leaks currently
+
+// Helper macros/func for packaging JSON objects from C structures
+
+#define EndpointInfoStructToJSON(__JSON_OBJECT__, __ENDPOINTINFOSTRUCT__) \
+ wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:s}", "endpoint_id", __ENDPOINTINFOSTRUCT__.endpoint_id, "type", __ENDPOINTINFOSTRUCT__.type, "name", __ENDPOINTINFOSTRUCT__.name);
+
+#define RoutingInfoStructToJSON(__JSON_OBJECT__, __ROUTINGINFOSTRUCT__) \
+ wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:i}", "routing_id", __ROUTINGINFOSTRUCT__.routing_id, "source_id", __ROUTINGINFOSTRUCT__.source_id, "sink_id", __ROUTINGINFOSTRUCT__.sink_id);
+
+static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT streamInfo)
+{
+ json_object *endpointInfoJ;
+ EndpointInfoStructToJSON(endpointInfoJ,streamInfo.endpoint_info);
+ wrap_json_pack(streamInfoJ, "{s:i,s:s}", "stream_id", streamInfo.stream_id, "pcm_name", streamInfo.pcm_name);
+ json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ);
+}
+
+// Binding initialization
+PUBLIC int AhlBindingInit()
+{
+
+ int errcount = 0;
+
+ // Initialize list of available sources/sinks using lower level services
+ errcount += EnumerateSources();
+ errcount += EnumerateSinks();
+
+ // TODO: Register for device changes from lower level services
+
+ // TODO: Parse high-level binding configuration file
+
+ // TODO: Perform other binding initialization tasks
+
+ AFB_DEBUG("Audio high-level Binding success errcount=%d", errcount);
+ return errcount;
+}
+
+PUBLIC void audiohlapi_get_sources(struct afb_req req)
+{
+ json_object *sourcesJ = NULL;
+ json_object *sourceJ = NULL;
+ json_object *queryJ = NULL;
+ AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+
+ if (audioRole != AUDIOROLE_MAXVALUE)
+ {
+ AFB_DEBUG("Filtering according to specified audio role=%d", audioRole);
+ }
+
+ // Fake run-time data for test purposes
+ EndpointInfoT sources[3];
+ sources[0].endpoint_id = 0;
+ sources[0].type = 0;
+ sources[0].name = "Source0";
+ sources[1].endpoint_id = 1;
+ sources[1].type = 1;
+ sources[1].name = "Source1";
+ sources[2].endpoint_id = 2;
+ sources[2].type = 2;
+ sources[2].name = "Source2";
+
+ sourcesJ = json_object_new_array();
+ for ( unsigned int i = 0 ; i < 3; i++)
+ {
+ EndpointInfoStructToJSON(sourceJ, sources[i]);
+ json_object_array_add(sourcesJ, sourceJ);
+ }
+
+ afb_req_success(req, sourcesJ, "List of sources");
+}
+
+PUBLIC void audiohlapi_get_sinks(struct afb_req req)
+{
+ json_object *sinksJ = NULL;
+ json_object *sinkJ = NULL;
+ json_object *queryJ = NULL;
+ AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+
+ if (audioRole != AUDIOROLE_MAXVALUE)
+ {
+ AFB_DEBUG("Filtering according to specified audio role=%d", audioRole);
+ }
+
+ // Fake run-time data for test purposes
+ EndpointInfoT sinks[3];
+ sinks[0].endpoint_id = 0;
+ sinks[0].type = 0;
+ sinks[0].name = "Sink0";
+ sinks[1].endpoint_id = 1;
+ sinks[1].type = 1;
+ sinks[1].name = "Sink1";
+ sinks[2].endpoint_id = 2;
+ sinks[2].type = 2;
+ sinks[2].name = "Sink2";
+
+ sinksJ = json_object_new_array();
+ for ( unsigned int i = 0 ; i < 3; i++)
+ {
+ EndpointInfoStructToJSON(sinkJ, sinks[i]);
+ json_object_array_add(sinksJ, sinkJ);
+ }
+
+ afb_req_success(req, sinksJ, "List of sinks");
+}
+
+PUBLIC void audiohlapi_stream_open(struct afb_req req)
+{
+ json_object *streamInfoJ = NULL;
+ StreamInfoT streamInfo;
+ json_object *queryJ = NULL;
+ AudioRoleT audioRole;
+ EndpointTypeT endpointType;
+ endpointID_t endpointID = UNDEFINED_ID;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = audio_role:%d endpointType:%d endpointID:%d", audioRole,endpointType,endpointID);
+
+ if (endpointID == UNDEFINED_ID)
+ {
+ // TODO: Go through configuration and available devices to find best device for specified role
+ endpointID = 2;
+ }
+
+ // Fake run-time data for test purposes
+ streamInfo.stream_id = 12;
+ streamInfo.pcm_name = "plug:Entertainment";
+ streamInfo.endpoint_info.endpoint_id = endpointID;
+ streamInfo.endpoint_info.type = endpointType;
+ streamInfo.endpoint_info.name = "MainSpeakers";
+
+ StreamInfoStructToJSON(&streamInfoJ,streamInfo);
+
+ afb_req_success(req, streamInfoJ, "Stream info structure");
+}
+
+PUBLIC void audiohlapi_stream_close(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ streamID_t streamID = UNDEFINED_ID;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = stream_id:%d", streamID);
+
+ // TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing
+
+ afb_req_success(req, NULL, "Stream close completed");
+}
+
+// Routings
+PUBLIC void audiohlapi_get_available_routings(struct afb_req req)
+{
+ json_object *routingsJ;
+ json_object *routingJ;
+ json_object *queryJ = NULL;
+ AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+
+ if (audioRole != AUDIOROLE_MAXVALUE)
+ {
+ AFB_DEBUG("Filtering according to specified audio role=%d", audioRole);
+ }
+
+ // Fake run-time data for test purposes
+ RoutingInfoT routings[3];
+ routings[0].source_id = 0;
+ routings[0].sink_id = 0;
+ routings[0].routing_id = 0;
+ routings[1].source_id = 1;
+ routings[1].sink_id = 1;
+ routings[1].routing_id = 1;
+ routings[2].source_id = 2;
+ routings[2].sink_id = 2;
+ routings[2].routing_id = 2;
+
+ routingsJ = json_object_new_array();
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ RoutingInfoStructToJSON(routingJ, routings[i]);
+ json_object_array_add(routingsJ, routingJ);
+ }
+
+ afb_req_success(req, routingsJ, "List of available routings");
+}
+
+PUBLIC void audiohlapi_add_routing(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
+ routingID_t routingID = UNDEFINED_ID;
+ json_object *routingJ = NULL;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s?i}", "audio_role", &audioRole,"routing_id",routingID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = audio_role:%d routing_id:%d", audioRole,routingID);
+
+ // Fake run-time data for test purposes
+ RoutingInfoT routingInfo;
+ routingInfo.routing_id = routingID;
+ routingInfo.source_id = 3;
+ routingInfo.sink_id = 4;
+
+ RoutingInfoStructToJSON(routingJ,routingInfo);
+
+ afb_req_success(req,routingJ, "Selected routing information");
+}
+
+PUBLIC void audiohlapi_remove_routing(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ routingID_t routingID = UNDEFINED_ID;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i}", "routing_id", &routingID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = routing_id:%d", routingID);
+
+ // TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing
+
+ afb_req_success(req, NULL, "Remove routing completed");
+}
+
+// Endpoints
+PUBLIC void audiohlapi_set_endpoint_volume(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ char * volumeStr = NULL;
+ int rampTimeMS = 0;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr,"ramp_time_ms",&rampTimeMS);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s ramp_time_ms: %d", endpointType,endpointID,volumeStr,rampTimeMS);
+
+ // TODO: Parse volume string to support increment/absolute/percent notation
+
+ afb_req_success(req, NULL, "Set volume completed");
+}
+
+PUBLIC void audiohlapi_get_endpoint_volume(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ json_object *volumeJ;
+ double volume = 0.0;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID);
+
+ volume = 87.0; // TODO: Get actual volume value
+ volumeJ = json_object_new_double(volume);
+
+ afb_req_success(req, volumeJ, "Retrieved volume value");
+}
+
+PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ char * propertyName = NULL;
+ char * propValueStr = NULL;
+ int rampTimeMS = 0;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueStr,"ramp_time_ms",&rampTimeMS);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s value:%s ramp_time_ms:%d", endpointType,endpointID,propertyName,propValueStr,rampTimeMS);
+
+ // TODO: Parse property value string to support increment/absolute/percent notation
+
+ afb_req_success(req, NULL, "Set property completed");
+}
+
+PUBLIC void audiohlapi_get_endpoint_property(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ char * propertyName = NULL;
+ json_object *propertyValJ;
+ double value = 0.0;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s?i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s", endpointType,endpointID,propertyName);
+
+ value = 93.0; // TODO: Get actual property value
+ propertyValJ = json_object_new_double(value);
+
+ afb_req_success(req, propertyValJ, "Retrieved property value");
+}
+
+PUBLIC void audiohlapi_set_endpoint_state(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ char * stateName = NULL;
+ char * stateValue = NULL;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"state_name",&stateName,"state_value",&stateValue);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s state_value:%s", endpointType,endpointID,stateName,stateValue);
+
+ afb_req_success(req, NULL, "Set endpoint state completed");
+}
+
+PUBLIC void audiohlapi_get_endpoint_state(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ json_object *stateValJ;
+ char * stateName = NULL;
+ char * stateValue = NULL;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"state_name",&stateName);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s", endpointType,endpointID,stateName);
+
+ stateValJ = json_object_new_string(stateValue);
+
+ afb_req_success(req, stateValJ, "Retrieved state value");
+}
+
+// Sound events
+PUBLIC void audiohlapi_post_sound_event(struct afb_req req)
+{
+ json_object *queryJ = NULL;
+ char * eventName = NULL;
+ char * mediaName = NULL;
+ AudioRoleT audioRole;
+ json_object *audioContext = NULL;
+
+ queryJ = afb_req_json(req);
+ int err = wrap_json_unpack(queryJ, "{s:s,s?i,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"audio_context",&audioContext);
+ if (err) {
+ afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ return;
+ }
+ AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%d media_name:%s", eventName,audioRole,mediaName);
+
+ // TODO: Post sound event to rendering services
+
+ afb_req_success(req, NULL, "Posted sound event");
+ }
+
+
+// Monitoring
+PUBLIC void audiohlapi_subscribe(struct afb_req req)
+{
+ // json_object *queryJ = NULL;
+
+ // queryJ = afb_req_json(req);
+
+ // TODO: Iterate through array length, parsing the string value to actual events
+ // TODO: Subscribe to appropriate events from other services
+
+ afb_req_success(req, NULL, "Subscribe to events finished");
+}
diff --git a/src/ahl-binding.h b/src/ahl-binding.h
new file mode 100644
index 0000000..7bd0653
--- /dev/null
+++ b/src/ahl-binding.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ * Author Francois Thibault <fthibault@audiokinetic.com>
+ *
+ * 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.
+ */
+
+
+#ifndef AHL_BINDING_INCLUDE
+#define AHL_BINDING_INCLUDE
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+#include <json-c/json.h>
+
+#ifndef PUBLIC
+ #define PUBLIC
+#endif
+
+#define UNDEFINED_ID -1
+
+typedef int endpointID_t;
+typedef int streamID_t;
+typedef int routingID_t;
+
+typedef enum EndpointType {
+ ENDPOINTTYPE_SOURCE = 0, // source devices
+ ENDPOINTTYPE_SINK, // sink devices
+ ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end
+} EndpointTypeT;
+
+typedef enum AudioRole {
+ AUDIOROLE_WARNING = 0, // Safety-relevant or critical alerts/alarms
+ AUDIOROLE_GUIDANCE, // Important user information where user action is expected (e.g. navigation instruction)
+ AUDIOROLE_NOTIFICATION, // HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)
+ AUDIOROLE_COMMUNICATIONS, // Voice communications (e.g. handsfree, speech recognition)
+ AUDIOROLE_ENTERTAINMENT, // Multimedia content (e.g. tuner, media player, etc.)
+ AUDIOROLE_SYSTEM, // System level content
+ AUDIOROLE_DEFAULT, // No specific audio role (legacy applications)
+ AUDIOROLE_MAXVALUE // Enum count, keep at the end
+} AudioRoleT;
+
+typedef enum AudioDeviceClass {
+ AUDIODEVICE_SPEAKERMAIN = 0,
+ AUDIODEVICE_SPEAKERHEADREST,
+ AUDIODEVICE_HEADSET,
+ AUDIODEVICE_HEADPHONE,
+ AUDIODEVICE_LINEOUT,
+ AUDIODEVICE_LINEIN,
+ AUDIODEVICE_BLUETOOTH,
+ AUDIODEVICE_HANDSET,
+ AUDIODEVICE_HDMI,
+ AUDIODEVICE_USB,
+ AUDIODEVICE_TONES,
+ AUDIODEVICE_VOICE,
+ AUDIODEVICE_PHONELINK,
+ AUDIODEVICE_DEFAULT,
+ AUDIODEVICE_MAXVALUE // Enum count, keep at the end
+} AudioDeviceClassT;
+
+typedef struct EndpointInfo
+{
+ endpointID_t endpoint_id;
+ EndpointTypeT type;
+ char * name;
+ // TODO: Consider adding associated device class
+} EndpointInfoT;
+
+typedef struct StreamInfo {
+ streamID_t stream_id;
+ char * pcm_name;
+ EndpointInfoT endpoint_info;
+} StreamInfoT;
+
+typedef struct RoutingInfo {
+ routingID_t routing_id;
+ endpointID_t source_id;
+ endpointID_t sink_id;
+} RoutingInfoT;
+
+PUBLIC int AhlBindingInit();
+// ahl-deviceenum.c
+PUBLIC int EnumerateSources();
+PUBLIC int EnumerateSinks();
+
+#endif
diff --git a/src/ahl-deviceenum.c b/src/ahl-deviceenum.c
new file mode 100644
index 0000000..6629de2
--- /dev/null
+++ b/src/ahl-deviceenum.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ * Author Francois Thibault <fthibault@audiokinetic.com>
+ *
+ * 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
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "ahl-binding.h"
+
+PUBLIC int EnumerateSources() {
+
+ // TODO: Use lower level services to build a list of available source devices
+
+ AFB_DEBUG ("Audio high-level - Enumerate sources done");
+ return 0;
+}
+
+PUBLIC int EnumerateSinks() {
+
+ // TODO: Use lower level services to build a list of available sink devices
+
+ AFB_DEBUG ("Audio high-level - Enumerate sinks done");
+ return 0;
+}
+