aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Bultel <thierry.bultel@iot.bzh>2018-08-31 13:53:47 +0200
committerThierry Bultel <thierry.bultel@iot.bzh>2018-08-31 13:53:47 +0200
commit80deafbe1bfb87c3a5e9f547c9491afd210e736a (patch)
tree67f83684cf67268dcb65d8062280f848057a74c2
parentded8e37b50982677480869763f3573ff43858505 (diff)
Added bluez sound playback support
This adds sound playback for incoming sound from connected bluetooth devices. In this version, the softmixer relies on a modified bluez-alsa version (https://github.com/iotbzh/bluez-alsa), that provides an ioplug PCM bluezalsa connection proxy. The softmixer api has a new verb to dynamically set the device to listen to: afb-client-demo ws://localhost:1234/api?token=\uuid=123 smixer bluezalsa_dev '{"interface":"hci0", "device":"F6:32:15:2A:80:70", "profile":"a2dp"}' In this way it is possible to switch from a bluezalsa audio source to another without any further configuration. For now, only interface hci0 is supported. This commit also migrates the softmixer binding to API v3 Signed-off-by: Thierry Bultel <thierry.bultel@iot.bzh>
m---------app-controller-submodule0
-rw-r--r--conf.d/cmake/config.cmake2
-rw-r--r--mixer-binding/mixer-binding.c30
-rw-r--r--plugins/alsa/alsa-api-loop.c1
-rw-r--r--plugins/alsa/alsa-api-mixer.c64
-rw-r--r--plugins/alsa/alsa-api-pcm.c9
-rw-r--r--plugins/alsa/alsa-api-streams.c28
-rw-r--r--plugins/alsa/alsa-bluez.c67
-rw-r--r--plugins/alsa/alsa-bluez.h29
-rw-r--r--plugins/alsa/alsa-core-ctl.c16
-rw-r--r--plugins/alsa/alsa-core-pcm.c68
-rw-r--r--plugins/alsa/alsa-softmixer.h5
-rw-r--r--plugins/alsa/alsa-utils-bypath.c9
13 files changed, 271 insertions, 57 deletions
diff --git a/app-controller-submodule b/app-controller-submodule
-Subproject 4e30eb1444fc88b73582db421d3a7467a364706
+Subproject c3a70d7718d51f2266ce671ee112a25398df076
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index 1c5fedd..9b4dc5f 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -135,7 +135,7 @@ add_definitions(-DCONTROL_PLUGIN_PATH="${CMAKE_BINARY_DIR}/package/lib/plugins:$
add_definitions(-DCONTROL_CONFIG_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/etc:${CMAKE_BINARY_DIR}/package/etc:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/etc:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}")
add_definitions(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/var:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}")
add_definitions(-DCTL_PLUGIN_MAGIC=987456123)
-add_definitions(-DUSE_API_DYN=1 -DAFB_BINDING_VERSION=3 -DAFB_BINDING_WANT_DYNAPI=1)
+add_definitions(-DAFB_BINDING_VERSION=3)
# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable]
diff --git a/mixer-binding/mixer-binding.c b/mixer-binding/mixer-binding.c
index 3af9c9b..ed5b5a8 100644
--- a/mixer-binding/mixer-binding.c
+++ b/mixer-binding/mixer-binding.c
@@ -23,8 +23,8 @@
#include "mixer-binding.h"
-// default api to print log when apihandle not avaliable
-PUBLIC afb_dynapi *AFB_default;
+// default api to print log when apihandle not avalaible
+PUBLIC afb_api_t AFB_default;
// Config Section definition (note: controls section index should match handle retrieval in HalConfigExec)
static CtlSectionT ctrlSections[]= {
@@ -52,16 +52,16 @@ STATIC AFB_ApiVerbs CtrlApiVerbs[] = {
{ .verb = NULL} /* marker for end of the array */
};
-STATIC int CtrlLoadStaticVerbs (afb_dynapi *apiHandle, AFB_ApiVerbs *verbs) {
+STATIC int CtrlLoadStaticVerbs (afb_api_t apiHandle, AFB_ApiVerbs *verbs) {
int errcount=0;
for (int idx=0; verbs[idx].verb; idx++) {
- errcount+= afb_dynapi_add_verb(apiHandle,
- CtrlApiVerbs[idx].verb,
- CtrlApiVerbs[idx].info,
- CtrlApiVerbs[idx].callback,
- (void*)&CtrlApiVerbs[idx],
- CtrlApiVerbs[idx].auth, 0);
+ errcount+= afb_api_add_verb(apiHandle,
+ CtrlApiVerbs[idx].verb,
+ CtrlApiVerbs[idx].info,
+ CtrlApiVerbs[idx].callback,
+ (void*)&CtrlApiVerbs[idx],
+ CtrlApiVerbs[idx].auth, 0, 0);
}
return errcount;
@@ -75,7 +75,7 @@ STATIC int CtrlLoadOneApi (void *cbdata, AFB_ApiT apiHandle) {
CtlConfigT *ctrlConfig = (CtlConfigT*) cbdata;
// save closure as api's data context
- afb_dynapi_set_userdata(apiHandle, ctrlConfig);
+ afb_api_set_userdata(apiHandle, ctrlConfig);
// add static controls verbs
int error = CtrlLoadStaticVerbs (apiHandle, CtrlApiVerbs);
@@ -88,7 +88,7 @@ STATIC int CtrlLoadOneApi (void *cbdata, AFB_ApiT apiHandle) {
error= CtlLoadSections(apiHandle, ctrlConfig, ctrlSections);
// declare an event event manager for this API;
- afb_dynapi_on_event(apiHandle, CtrlDispatchApiEvent);
+ afb_api_on_event(apiHandle, CtrlDispatchApiEvent);
// should not seal API as each mixer+stream create a new verb
// afb_dynapi_seal(apiHandle);
@@ -98,7 +98,7 @@ OnErrorExit:
return 1;
}
-PUBLIC int afbBindingEntry(afb_dynapi *apiHandle) {
+PUBLIC int afbBindingEntry(afb_api_t apiHandle) {
AFB_default = apiHandle;
@@ -128,10 +128,12 @@ PUBLIC int afbBindingEntry(afb_dynapi *apiHandle) {
AFB_ApiNotice (apiHandle, "Controller API='%s' info='%s'", ctrlConfig->api, ctrlConfig->info);
// create one API per config file (Pre-V3 return code ToBeChanged)
- int status = afb_dynapi_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, CtrlLoadOneApi, ctrlConfig);
+ afb_api_t handle = afb_api_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, CtrlLoadOneApi, ctrlConfig);
+
+ int status = 0;
// config exec should be done after api init in order to enable onload to use newly defined ctl API.
- if (!status)
+ if (handle)
status = CtlConfigExec (apiHandle, ctrlConfig);
return status;
diff --git a/plugins/alsa/alsa-api-loop.c b/plugins/alsa/alsa-api-loop.c
index c5b2a53..e2d789e 100644
--- a/plugins/alsa/alsa-api-loop.c
+++ b/plugins/alsa/alsa-api-loop.c
@@ -67,6 +67,7 @@ STATIC AlsaLoopSubdevT *ProcessOneSubdev(SoftMixerT *mixer, AlsaSndLoopT *loop,
AlsaDevInfoT loopSubdev;
loopSubdev.devpath=NULL;
loopSubdev.cardid=NULL;
+ loopSubdev.pcmplug_params = NULL;
loopSubdev.cardidx = loop->sndcard->cid.cardidx;
loopSubdev.device = loop->capture;
loopSubdev.subdev = subdev->index;
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index 558a522..c4b016c 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -19,6 +19,8 @@
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
+#include "alsa-bluez.h"
+
#include <string.h>
#include <pthread.h>
@@ -26,7 +28,7 @@
extern Lua2cWrapperT Lua2cWrap;
static void MixerRemoveVerb(AFB_ReqT request) {
- SoftMixerT *mixer = (SoftMixerT*) afb_request_get_vcbdata(request);
+ SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request);
int error;
for (int idx = 0; mixer->streams[idx]->uid; idx++) {
@@ -41,7 +43,7 @@ static void MixerRemoveVerb(AFB_ReqT request) {
goto OnErrorExit;
}
- error = afb_dynapi_sub_verb(mixer->api, stream->uid);
+ error = afb_api_del_verb(mixer->api, stream->uid, (void**)&mixer);
if (error) {
AFB_ReqFailF(request, "internal-error", "Mixer=%s fail to remove verb=%s error=%s", mixer->uid, stream->uid, strerror(error));
goto OnErrorExit;
@@ -481,7 +483,7 @@ OnErrorExit:
STATIC void MixerInfoAction(AFB_ReqT request, json_object * argsJ) {
- SoftMixerT *mixer = (SoftMixerT*) afb_request_get_vcbdata(request);
+ SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request);
int error, verbose = 0;
json_object *streamsJ = NULL, *rampsJ = NULL, *zonesJ = NULL, *capturesJ = NULL, *playbacksJ = NULL;
@@ -534,15 +536,15 @@ STATIC void MixerInfoAction(AFB_ReqT request, json_object * argsJ) {
}
STATIC void MixerInfoVerb(AFB_ReqT request) {
- json_object *argsJ = afb_request_json(request);
+ json_object *argsJ = afb_req_json(request);
MixerInfoAction(request, argsJ);
}
STATIC void MixerAttachVerb(AFB_ReqT request) {
- SoftMixerT *mixer = (SoftMixerT*) afb_request_get_vcbdata(request);
+ SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request);
const char *uid = NULL, *prefix = NULL;
json_object *playbacksJ = NULL, *capturesJ = NULL, *zonesJ = NULL, *streamsJ = NULL, *rampsJ = NULL, *loopsJ = NULL;
- json_object *argsJ = afb_request_json(request);
+ json_object *argsJ = afb_req_json(request);
json_object *responseJ = json_object_new_object();
int error;
@@ -632,6 +634,49 @@ STATIC void MixerAttachVerb(AFB_ReqT request) {
return;
OnErrorExit:
+ AFB_ApiError(mixer->api,"%s FAILED", __func__);
+ return;
+}
+
+
+static void MixerBluezAlsaDevVerb(AFB_ReqT request) {
+ SoftMixerT *mixer = (SoftMixerT*) afb_req_get_vcbdata(request);
+
+ json_object *argsJ = afb_req_json(request);
+ json_object *responseJ = json_object_new_object();
+
+ char * interface = NULL, *device = NULL, *profile = NULL;
+
+ int error;
+
+ error = wrap_json_unpack(argsJ, "{ss,ss,ss !}"
+ , "interface", &interface
+ , "device", &device
+ , "profile", &profile
+ );
+
+ if (error) {
+ AFB_ReqFailF(request,
+ "invalid-syntax",
+ "mixer=%s missing 'interface|device|profile' error=%s args=%s",
+ mixer->uid, wrap_json_get_error_string(error), json_object_get_string(argsJ));
+ goto OnErrorExit;
+ }
+
+ printf("%s: interface %s, device %s, profile %s\n", __func__, interface, device, profile);
+
+ error = alsa_bluez_set_device(interface, device, profile);
+ if (error) {
+ AFB_ReqFailF(request,
+ "runtime error",
+ "Unable to set device , err %d", error);
+ goto OnErrorExit;
+ }
+
+
+ AFB_ReqSuccess(request, responseJ, NULL);
+
+OnErrorExit:
return;
}
@@ -641,6 +686,7 @@ STATIC AFB_ApiVerbs CtrlApiVerbs[] = {
{ .verb = "attach", .callback = MixerAttachVerb, .info = "attach resources to mixer"},
{ .verb = "remove", .callback = MixerRemoveVerb, .info = "remove existing mixer streams, zones, ..."},
{ .verb = "info", .callback = MixerInfoVerb, .info = "list existing mixer streams, zones, ..."},
+ { .verb = "bluezalsa_dev", .callback = MixerBluezAlsaDevVerb, .info = "set bluez alsa device"},
{ .verb = NULL} /* marker for end of the array */
};
@@ -648,7 +694,7 @@ STATIC int LoadStaticVerbs(SoftMixerT *mixer, AFB_ApiVerbs * verbs) {
int errcount = 0;
for (int idx = 0; verbs[idx].verb; idx++) {
- errcount += afb_dynapi_add_verb(mixer->api, CtrlApiVerbs[idx].verb, CtrlApiVerbs[idx].info, CtrlApiVerbs[idx].callback, (void*) mixer, CtrlApiVerbs[idx].auth, 0);
+ errcount += afb_api_add_verb(mixer->api, CtrlApiVerbs[idx].verb, CtrlApiVerbs[idx].info, CtrlApiVerbs[idx].callback, (void*) mixer, CtrlApiVerbs[idx].auth, 0, 0);
}
return errcount;
@@ -757,7 +803,7 @@ CTLP_CAPI(MixerCreate, source, argsJ, responseJ) {
mixer->sdLoop = AFB_GetEventLoop(source->api);
mixer->api = source->api;
- afb_dynapi_set_userdata(source->api, mixer);
+ afb_api_set_userdata(source->api, mixer);
error = LoadStaticVerbs(mixer, CtrlApiVerbs);
if (error) goto OnErrorExit;
@@ -767,3 +813,5 @@ CTLP_CAPI(MixerCreate, source, argsJ, responseJ) {
OnErrorExit:
return -1;
}
+
+
diff --git a/plugins/alsa/alsa-api-pcm.c b/plugins/alsa/alsa-api-pcm.c
index a51bb6f..5b6d3f5 100644
--- a/plugins/alsa/alsa-api-pcm.c
+++ b/plugins/alsa/alsa-api-pcm.c
@@ -232,11 +232,11 @@ OnErrorExit:
}
STATIC void ApiPcmVerbCB(AFB_ReqT request) {
- apiVerbHandleT *handle = (apiVerbHandleT*) afb_request_get_vcbdata(request);
+ apiVerbHandleT *handle = (apiVerbHandleT*) afb_req_get_vcbdata(request);
int error, verbose = 0, doInfo = 0, doToggle = 0, doMute = -1;
json_object *volumeJ = NULL;
json_object *responseJ = NULL;
- json_object *argsJ = afb_request_json(request);
+ json_object *argsJ = afb_req_json(request);
SoftMixerT *mixer = handle->mixer;
AlsaSndCtlT *sndcard = handle->pcm->sndcard;
@@ -432,8 +432,9 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
int error;
pcm->sndcard = (AlsaSndCtlT*) calloc(1, sizeof (AlsaSndCtlT));
- error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?i,s?i,s?o,s?o,s?o !}"
+ error = wrap_json_unpack(argsJ, "{ss,s?s,s?s,s?s,s?i,s?i,s?o,s?o,s?o !}"
, "uid", &pcm->uid
+ , "pcmplug_params", &pcm->sndcard->cid.pcmplug_params
, "path", &pcm->sndcard->cid.devpath
, "cardid", &pcm->sndcard->cid.cardid
, "device", &pcm->sndcard->cid.device
@@ -549,7 +550,7 @@ PUBLIC AlsaSndPcmT * ApiPcmAttachOne(SoftMixerT *mixer, const char *uid, snd_pcm
handle->pcm = pcm;
handle->mixer = mixer;
pcm->verb=apiVerb;
- error = afb_dynapi_add_verb(mixer->api, apiVerb, apiInfo, ApiPcmVerbCB, handle, NULL, 0);
+ error = afb_api_add_verb(mixer->api, apiVerb, apiInfo, ApiPcmVerbCB, handle, NULL, 0, 0);
if (error) {
AFB_ApiError(mixer->api, "ApiPcmAttachOne mixer=%s verb=%s fail to Register Master control ", mixer->uid, apiVerb);
goto OnErrorExit;
diff --git a/plugins/alsa/alsa-api-streams.c b/plugins/alsa/alsa-api-streams.c
index cf54d91..c06a7d4 100644
--- a/plugins/alsa/alsa-api-streams.c
+++ b/plugins/alsa/alsa-api-streams.c
@@ -19,8 +19,11 @@
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
+#include "alsa-bluez.h"
+
#include <string.h>
#include <stdbool.h>
+#include <dlfcn.h>
// Set stream volume control in %
#define VOL_CONTROL_MAX 100
@@ -39,10 +42,10 @@ typedef struct {
} apiHandleT;
STATIC void StreamApiVerbCB(AFB_ReqT request) {
- apiHandleT *handle = (apiHandleT*) afb_request_get_vcbdata(request);
+ apiHandleT *handle = (apiHandleT*) afb_req_get_vcbdata(request);
int error, verbose = 0, doClose = 0, doToggle = 0, doMute = -1, doInfo = 0;
long mute, volume, curvol;
- json_object *volumeJ = NULL, *rampJ = NULL, *argsJ = afb_request_json(request);
+ json_object *volumeJ = NULL, *rampJ = NULL, *argsJ = afb_req_json(request);
json_object *responseJ = NULL;
SoftMixerT *mixer = handle->mixer;
AlsaSndCtlT *sndcard = handle->sndcard;
@@ -194,6 +197,8 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
char *playbackName = NULL;
char *runName = NULL;
char *volName = NULL;
+ int pauseNumid = 0;
+ int volNumid = 0;
AFB_ApiInfo(mixer->api,
"%s, stream %s %s, source %s, sink %s, mute %d",
@@ -224,6 +229,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
captureDev->cardidx = sourceDev->cid.cardidx;
captureDev->device = sourceDev->cid.device;
captureDev->subdev = sourceDev->cid.subdev;
+ captureDev->pcmplug_params = sourceDev->cid.pcmplug_params;
captureCard = sourceDev;
AFB_ApiInfo(mixer->api, "%s found capture %s", __func__, uid);
} else {
@@ -240,6 +246,8 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
capturePcm->mute = stream->mute;
+ AFB_ApiInfo(mixer->api,"%s: PCM opened !\n", __func__);
+
// Registry capturePcm PCM for active/pause event
if (loopDev && loopDev->numid) {
error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_RUN, loopDev->numid);
@@ -281,18 +289,23 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
}
// retrieve channel count from route and push it to stream
+
stream->params->channels = zone->ccount;
// create mute control and Registry it as pause/resume ctl)
if (asprintf(&runName, "pause-%s", stream->uid) == -1)
goto OnErrorExit;
- int pauseNumid = AlsaCtlCreateControl(mixer, captureCard, runName, 1, 0, 1, 1, stream->mute);
+ AFB_ApiInfo(mixer->api,"%s: create mute control !\n", __func__);
+
+ pauseNumid = AlsaCtlCreateControl(mixer, captureCard, runName, 1, 0, 1, 1, stream->mute);
if (pauseNumid <= 0) {
AFB_ApiError(mixer->api, "%s: Failed to create pause control", __func__);
goto OnErrorExit;
}
+ AFB_ApiInfo(mixer->api,"%s: register mute control !", __func__);
+
// Registry stop/play as a pause/resume control
error = AlsaCtlRegister(mixer, captureCard, capturePcm, FONTEND_NUMID_PAUSE, pauseNumid);
if (error) {
@@ -303,6 +316,8 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
if (asprintf(&volName, "vol-%s", stream->uid) == -1)
goto OnErrorExit;
+ AFB_ApiInfo(mixer->api,"%s: create softvol", __func__);
+
// create stream and delay pcm opening until vol control is created
streamPcm = AlsaCreateSoftvol(mixer, stream, volSlaveId, captureCard, volName, VOL_CONTROL_MAX, 0);
if (!streamPcm) {
@@ -310,8 +325,10 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
goto OnErrorExit;
}
+ AFB_ApiInfo(mixer->api,"%s: create softvol control", __func__);
+
// create volume control before softvol pcm is opened
- int volNumid = AlsaCtlCreateControl(mixer,
+ volNumid = AlsaCtlCreateControl(mixer,
captureCard,
volName,
stream->params->channels,
@@ -406,7 +423,7 @@ STATIC int CreateOneStream(SoftMixerT *mixer, const char * uid, AlsaStreamAudioT
stream->volume = volNumid;
stream->mute = pauseNumid;
- error = afb_dynapi_add_verb(mixer->api, stream->verb, stream->info, StreamApiVerbCB, apiHandle, NULL, 0);
+ error = afb_api_add_verb(mixer->api, stream->verb, stream->info, StreamApiVerbCB, apiHandle, NULL, 0, 0);
if (error) {
AFB_ApiError(mixer->api,
"%s mixer=%s fail to Register API verb stream=%s",
@@ -565,5 +582,6 @@ PUBLIC int ApiStreamAttach(SoftMixerT *mixer, AFB_ReqT request, const char *uid,
return 0;
OnErrorExit:
+ printf("%s FAILED\n", __func__);
return -1;
}
diff --git a/plugins/alsa/alsa-bluez.c b/plugins/alsa/alsa-bluez.c
new file mode 100644
index 0000000..bd6bc99
--- /dev/null
+++ b/plugins/alsa/alsa-bluez.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright(C) 2018 "IoT.bzh"
+ * Author Thierry Bultel <thierry.bultel@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 "alsa-bluez.h"
+
+#include <stdbool.h>
+#include <dlfcn.h>
+
+#define ALSA_BLUEZ_PROXY_LIB "/usr/lib/alsa-lib/libasound_module_pcm_bluealsa_proxy.so"
+#define ALSA_BLUEZ_PROXY_SETDEVICE "bluealsa_proxy_set_remote_device"
+
+typedef int (*bluealsa_set_remote_device_ptr) (const char * interface, const char * device, const char * profile);
+
+static bluealsa_set_remote_device_ptr bluealsa_proxy_set_remote_device = NULL;
+
+void alsa_bluez_init() {
+ static bool initialized = false;
+ if (initialized)
+ goto failed;
+
+ char errbuf[256];
+ void * dl = snd_dlopen(ALSA_BLUEZ_PROXY_LIB, RTLD_NOW, errbuf, 256);
+ if (!dl) {
+ printf("Failed to open bluealsa proxy plugin\n");
+ goto failed;
+ }
+
+ void * func = snd_dlsym(dl, ALSA_BLUEZ_PROXY_SETDEVICE, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION));
+ if (!func) {
+ printf("Unable to find %s symbol\n", ALSA_BLUEZ_PROXY_SETDEVICE);
+ goto failed;
+ }
+
+ bluealsa_proxy_set_remote_device = func;
+ initialized = true;
+
+failed:
+ return;
+}
+
+int alsa_bluez_set_device(const char * interface, const char * device, const char * profile) {
+ if (!bluealsa_proxy_set_remote_device)
+ return -1;
+
+ return bluealsa_proxy_set_remote_device(interface,device,profile);
+}
+
+
diff --git a/plugins/alsa/alsa-bluez.h b/plugins/alsa/alsa-bluez.h
new file mode 100644
index 0000000..85d153a
--- /dev/null
+++ b/plugins/alsa/alsa-bluez.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright(C) 2018 "IoT.bzh"
+ * Author Thierry Bultel <thierry.bultel@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.
+ *
+ */
+
+#ifndef __INC_ALSA_BLUEZ_H
+#define __INC_ALSA_BLUEZ_H
+
+#include <alsa/asoundlib.h>
+
+extern void alsa_bluez_init();
+extern int alsa_bluez_set_device(const char * interface, const char * device, const char * profile);
+
+#endif /* __INC_ALSA_BLUEZ_H */
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index d128b6c..01e560f 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -25,6 +25,8 @@ for the specific language governing permissions and
#define _GNU_SOURCE // needed for vasprintf
#include "alsa-softmixer.h"
+#include "alsa-bluez.h"
+
#include <pthread.h>
#include <sys/syscall.h>
@@ -279,12 +281,22 @@ OnErrorExit:
// Clone of AlsaLib snd_card_load2 static function
-PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(SoftMixerT *mixer, const char *cardid) {
+PUBLIC snd_ctl_card_info_t *AlsaCtlGetCardInfo(SoftMixerT *mixer, const char *cardid) {
int error;
snd_ctl_t *ctl;
AFB_ApiNotice(mixer->api, "Looking for card '%s'", cardid);
+ /* "bluealsa" is the name of the control external plugin
+ * (https://www.alsa-project.org/alsa-doc/alsa-lib/ctl_external_plugins.html)
+ */
+ if (strstr(cardid, "bluealsa")) {
+ cardid="bluealsa";
+ alsa_bluez_init();
+ }
+
+ AFB_ApiNotice(mixer->api, "Opening card control '%s'", cardid);
+
if ((error = snd_ctl_open(&ctl, cardid, SND_CTL_READONLY)) < 0) {
cardid = "Not Defined";
goto OnErrorExit;
@@ -627,6 +639,8 @@ OnErrorExit:
PUBLIC int AlsaCtlRegister(SoftMixerT *mixer, AlsaSndCtlT *sndcard, AlsaPcmCtlT *pcmdev, RegistryNumidT type, int numid) {
int index;
+ AFB_ApiInfo(mixer->api,"%s: %d!\n", __func__, numid);
+
for (index = 0; index < sndcard->rcount; index++) {
if (!sndcard->registry[index]) break;
}
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index c1b59b2..50e1384 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -269,7 +269,6 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
};
snd_pcm_sw_params_get_avail_min(pxmSwParams, &pcm->avail_min);
- printf("GET AVAILABLE MIN: %ld\n", n);
int start_delay = 0;
snd_pcm_uframes_t start_threshold;
@@ -362,7 +361,7 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
* hopefully we will have more luck next time */
if (availIn > availInBuf) {
- printf("INCOMING BUFFER TOO SMALL !\n");
+// printf("INCOMING BUFFER TOO SMALL !\n");
availIn = availInBuf;
}
@@ -373,8 +372,8 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
if (r <= 0) {
pthread_mutex_unlock(&pcmCopyHandle->mutex);
- // Wakeup the reader, in case it would be sleeping,
- // that lets it an opportunity to pop.
+ // Wake up the reader, in case it is sleeping,
+ // that lets it an opportunity to pop something.
sem_post(&pcmCopyHandle->sem);
break;
}
@@ -386,6 +385,7 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
pthread_mutex_unlock(&pcmCopyHandle->mutex);
r = snd_pcm_readi(pcmIn, buf, r);
+
if (r == 0) {
break;
}
@@ -469,7 +469,7 @@ static void readResume(AlsaPcmCopyHandleT * pcmCopyHandle) {
static void *readThreadEntry(void *handle) {
-#define LOOP_TIMEOUT_MSEC 10*1000
+#define LOOP_TIMEOUT_MSEC 10*1000 /* 10 seconds */
AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) handle;
pcmCopyHandle->tid = (int) syscall(SYS_gettid);
@@ -478,8 +478,11 @@ static void *readThreadEntry(void *handle) {
"%s :%s/%d Started, muted=%d",
__func__, pcmCopyHandle->info, pcmCopyHandle->tid, pcmCopyHandle->pcmIn->mute);
- struct pollfd * mutePfd = &pcmCopyHandle->pollFds[1];
- mutePfd->events = POLLIN | POLLHUP;
+ struct pollfd * mutePfd = &pcmCopyHandle->pollFds[0];
+ struct pollfd * framePfd = &pcmCopyHandle->pollFds[1];
+
+ mutePfd->events = POLLIN | POLLHUP;
+ framePfd->events = POLLIN | POLLHUP;
bool muted = pcmCopyHandle->pcmIn->mute;
@@ -489,7 +492,7 @@ static void *readThreadEntry(void *handle) {
/* loop until end */
for (;;) {
- int err = poll(pcmCopyHandle->pollFds, 2, LOOP_TIMEOUT_MSEC);
+ int err = poll(pcmCopyHandle->pollFds, pcmCopyHandle->nbPcmFds, LOOP_TIMEOUT_MSEC);
if (err < 0) {
AFB_ApiError(pcmCopyHandle->api, "%s: poll err %s", __func__, strerror(errno));
continue;
@@ -522,7 +525,21 @@ static void *readThreadEntry(void *handle) {
continue;
}
- AlsaPcmReadCB(&pcmCopyHandle->pollFds[0], pcmCopyHandle);
+ unsigned short revents;
+
+ int ret = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, &pcmCopyHandle->pollFds[1], 1, &revents);
+
+ if (ret == -ENODEV) {
+ sleep(1);
+ continue;
+ }
+
+ if (framePfd->revents & POLLHUP) {
+ AFB_ApiNotice(pcmCopyHandle->api, "Frame POLLHUP");
+ continue;
+ }
+
+ AlsaPcmReadCB(&pcmCopyHandle->pollFds[1], pcmCopyHandle);
}
pthread_exit(0);
@@ -631,6 +648,8 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
pcmIn->mixer = mixer;
pcmOut->mixer = mixer;
+ AFB_ApiInfo(mixer->api, "%s: Configure CAPTURE PCM", __func__);
+
// prepare PCM for capture and replay
error = AlsaPcmConf(mixer, pcmIn, SND_PCM_STREAM_CAPTURE);
if (error) {
@@ -703,13 +722,13 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
if (pcmInCount > 1) {
AFB_ApiError(mixer->api,
- "%s: Fail, pcmIn=%s; having more than one FD on capture PCM is not supported",
- __func__, ALSA_PCM_UID(pcmOut->handle, string) );
+ "%s: Fail, pcmIn=%s; having more than one FD on capture PCM is not supported (here, %d)",
+ __func__, ALSA_PCM_UID(pcmOut->handle, string) , pcmInCount);
goto OnErrorExit;
}
- struct pollfd pcmInFds[1];
- if ((error = snd_pcm_poll_descriptors(pcmIn->handle, pcmInFds, 1)) < 0) {
+ struct pollfd pcmInFd;
+ if ((error = snd_pcm_poll_descriptors(pcmIn->handle, &pcmInFd, 1)) < 0) {
AFB_ApiError(mixer->api,
"%s: Fail pcmIn=%s get pollfds error=%s",
__func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
@@ -717,23 +736,27 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
};
// create the mute pipe
- int pFd[2];
- error = pipe(pFd);
+ int pMuteFd[2];
+ error = pipe(pMuteFd);
if (error < 0) {
AFB_ApiError(mixer->api,
"Unable to create the mute signaling pipe\n");
goto OnErrorExit;
}
- struct pollfd pipePoll;
- pipePoll.fd = pFd[0];
- pipePoll.events = POLLIN;
- pipePoll.revents = 0;
+ struct pollfd mutePFd;
+ // read end
+ mutePFd.fd = pMuteFd[0];
+ mutePFd.events = POLLIN;
+ mutePFd.revents = 0;
+
+ // write end
+ pcmIn->muteFd = pMuteFd[1];
- pcmIn->muteFd = pFd[1];
+ cHandle->pollFds[0] = mutePFd;
+ cHandle->pollFds[1] = pcmInFd;
- cHandle->pollFds[0] = pcmInFds[0];
- cHandle->pollFds[1] = pipePoll;
+ cHandle->nbPcmFds = pcmInCount+1;
error = sem_init(&cHandle->sem, 0 , 0);
if (error < 0) {
@@ -754,6 +777,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
__func__, ALSA_PCM_UID(pcmIn->handle, string), error);
}
+
/// start a thread for writing
if ((error = pthread_create(&cHandle->wthread, NULL, &readThreadEntry, cHandle)) < 0) {
AFB_ApiError(mixer->api,
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index 96df8f3..e7218bd 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -86,6 +86,7 @@ typedef struct {
const char *cardid;
const char *name;
const char *longname;
+ const char *pcmplug_params;
int device;
int subdev;
} AlsaDevInfoT;
@@ -135,6 +136,8 @@ typedef struct {
int tid;
char* info;
+
+ int nbPcmFds;
struct pollfd pollFds[2];
sem_t sem;
@@ -279,7 +282,7 @@ PUBLIC snd_ctl_elem_id_t *AlsaCtlGetNameElemId(SoftMixerT *mixer, AlsaSndCtlT *s
PUBLIC snd_ctl_t *AlsaCtlOpenCtl(SoftMixerT *mixer, const char *cardid) ;
PUBLIC int CtlElemIdGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long *value) ;
PUBLIC int CtlElemIdSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, snd_ctl_elem_id_t *elemId, long value) ;
-PUBLIC snd_ctl_card_info_t *AlsaCtlGetInfo(SoftMixerT *mixer, const char *cardid) ;
+PUBLIC snd_ctl_card_info_t *AlsaCtlGetCardInfo(SoftMixerT *mixer, const char *cardid) ;
PUBLIC int AlsaCtlNumidSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid, long value) ;
PUBLIC int AlsaCtlNumidGetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, int numid, long* value) ;
PUBLIC int AlsaCtlNameSetLong(SoftMixerT *mixer, AlsaSndCtlT *sndcard, const char *ctlName, long value) ;
diff --git a/plugins/alsa/alsa-utils-bypath.c b/plugins/alsa/alsa-utils-bypath.c
index 2f22333..4c258c2 100644
--- a/plugins/alsa/alsa-utils-bypath.c
+++ b/plugins/alsa/alsa-utils-bypath.c
@@ -27,6 +27,7 @@
#include <sys/ioctl.h>
#include "alsa-softmixer.h"
+#include "alsa-bluez.h"
// extracted IOCTLs from <alsa/asoundlib.h>
#define _IOR_HACKED(type,nr,size) _IOC(_IOC_READ,(type),(nr),size)
@@ -63,6 +64,10 @@ PUBLIC AlsaPcmCtlT *AlsaByPathOpenPcm(SoftMixerT *mixer, AlsaDevInfoT *pcmDev, s
AlsaPcmCtlT *pcmCtl = calloc(1, sizeof (AlsaPcmCtlT));
char *cardid = NULL;
+ if (pcmDev->pcmplug_params) {
+ pcmDev->cardid = pcmDev->pcmplug_params;
+ }
+
if (!pcmDev->cardid) {
if (pcmDev->subdev) {
if (asprintf(&cardid, "hw:%i,%i,%i", pcmDev->cardidx, pcmDev->device, pcmDev->subdev) == -1)
@@ -115,7 +120,9 @@ PUBLIC snd_ctl_t *AlsaByPathOpenCtl(SoftMixerT *mixer, const char *uid, AlsaSndC
if (dev->cid.devpath)
cardInfo = AlsaByPathInfo(mixer, dev->cid.devpath);
else if (dev->cid.cardid)
- cardInfo = AlsaCtlGetInfo(mixer, dev->cid.cardid);
+ cardInfo = AlsaCtlGetCardInfo(mixer, dev->cid.cardid);
+ else if (dev->cid.pcmplug_params)
+ cardInfo = AlsaCtlGetCardInfo(mixer, dev->cid.pcmplug_params);
if (!cardInfo) {
AFB_ApiError(mixer->api,