From e0f57e523112e1bc73a04e8615d7a21355f0ce0e Mon Sep 17 00:00:00 2001 From: Thierry Bultel Date: Tue, 4 Dec 2018 23:11:20 +0100 Subject: Add support for bluetooth telephony This adds support for bluetooth telephony. A big rework in the softmixer internals has been mandatory, in order to support dynamic streams creation and deletions. Bluetooth telephony relies on the recent evolutions of bluez-alsa, the most important one being the support of HFP over Ofono. The softmixer opens PCM ioplugs provided by bluez-alsa. Bluetooth SCO needs 2 streams, one for listening and the other for talking. These streams are created upon requests sent by the hal-manager. The hal manager subscribes for bluez-alsa events and request the list of availalble transports. For each "attach" transaction verb, the softmixer maintains a list of the all created objects (sources, sinks, zones, ramps, streams, and more) Additionnally, it creates a new verb when the attach succeeds, that verb is typically something like "sco_XX:XX:XX:XX:XX:XX", and the only supported action at the present time is {"action":"remove"}, that performs all the cleanup of the registered objects. Change-Id: I1b119e6c079e60daf771e63c083a1ef33a39f379 Signed-off-by: Thierry Bultel --- plugins/alsa/alsa-core-pcm.c | 307 +++++++++++++++++++++++++++---------------- 1 file changed, 192 insertions(+), 115 deletions(-) (limited to 'plugins/alsa/alsa-core-pcm.c') diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c index 25872ae..adceebc 100644 --- a/plugins/alsa/alsa-core-pcm.c +++ b/plugins/alsa/alsa-core-pcm.c @@ -34,6 +34,16 @@ for the specific language governing permissions and static int xrun(snd_pcm_t * pcm, int error); static int suspend(snd_pcm_t * pcm, int error); +typedef enum { + PCM_COPY_MUTE, + PCM_COPY_UNMUTE, + PCM_COPY_END, + PCM_COPY_LAST // Do not put anything after +} PcmCopyEventType; + +typedef struct { + PcmCopyEventType eventType; +} PcmCopyEvent; STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) { int pcmSampleSize; @@ -81,12 +91,13 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { snd_pcm_access_t access; AlsaPcmHwInfoT * opts = pcm->params; + const char * card = pcm->cid.cardid; const char * modeS = mode==SND_PCM_STREAM_PLAYBACK?"PLAYBACK":"CAPTURE"; - AFB_ApiInfo(mixer->api, - "%s: mixer info %s uid %s , pcm %s, mode %s", - __func__, mixer->info, mixer->uid, pcm->cid.cardid, modeS); + AFB_ApiDebug(mixer->api, + "%s: mixer info %s uid %s, pcm %s, mode %s", + __func__, mixer->info, mixer->uid, card, modeS); // retrieve hardware config from PCM snd_pcm_hw_params_alloca(&pxmHwParams); @@ -96,7 +107,7 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { goto OnErrorExit; } - AFB_ApiDebug(mixer->api, "PARAMS before:\n"); + AFB_ApiDebug(mixer->api, "(%s): PARAMS before:", card); AlsaDumpPcmParams(mixer, pxmHwParams); if (!opts->access) @@ -106,8 +117,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { error = snd_pcm_hw_params_set_access(pcm->handle, pxmHwParams, opts->access); if (error) { AFB_ApiError(mixer->api, - "%s set_access failed (ignore this error): mixer=%s cardid=%s access=%d Fail current=%d mode error=%s", - __func__, mixer->uid, pcm->cid.cardid, opts->access, access, snd_strerror(error)); + "%s (%s) set_access failed (ignore this error): mixer=%s access=%d Fail current=%d mode error=%s", + __func__, card, mixer->uid, opts->access, access, snd_strerror(error)); //Fulup goto OnErrorExit; }; @@ -115,8 +126,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { snd_pcm_hw_params_get_format(pxmHwParams, &format); if ((error = snd_pcm_hw_params_set_format(pcm->handle, pxmHwParams, opts->format)) < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Set_Format=%s (%d) FAILED current=%d error=%s", - __func__, mixer->uid, pcm->cid.cardid, opts->formatS, opts->format, format, snd_strerror(error)); + "%s (%s) mixer=%s Set_Format=%s (%d) FAILED current=%d error=%s", + __func__, card, mixer->uid, opts->formatS, opts->format, format, snd_strerror(error)); AlsaDumpFormats(mixer, pcm->handle); goto OnErrorExit; } @@ -124,22 +135,22 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { if (opts->rate > 0 ) { - AFB_ApiInfo(mixer->api," %s: set rate to %d", __func__, opts->rate); + AFB_ApiDebug(mixer->api,"%s (%s): set rate to %d", __func__, card, opts->rate); unsigned int pcmRate = opts->rate; /* Attempt to set the rate. Failing on a capture dev is acceptable */ error = snd_pcm_hw_params_set_rate_near(pcm->handle, pxmHwParams, &opts->rate, 0); if ( mode == SND_PCM_STREAM_PLAYBACK && error < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s FailSet_Rate=%d error=%s", - __func__, mixer->uid, pcm->cid.cardid, opts->rate, snd_strerror(error)); + "%s (%s): mixer=%s FailSet_Rate=%d error=%s", + __func__, card, mixer->uid, opts->rate, snd_strerror(error)); goto OnErrorExit; } // check we got requested rate if (mode == SND_PCM_STREAM_PLAYBACK && opts->rate != pcmRate) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Set_Rate Fail ask=%dHz get=%dHz", - __func__, mixer->uid, pcm->cid.cardid,pcmRate, opts->rate); + "%s (%s): mixer=%s Set_Rate Fail ask=%dHz get=%dHz", + __func__, card, mixer->uid, pcmRate, opts->rate); goto OnErrorExit; } } @@ -147,8 +158,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { if (opts->channels) { if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, opts->channels)) < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Set_Channels=%d Fail error=%s", - __func__, mixer->uid, pcm->cid.cardid, opts->channels, snd_strerror(error)); + "%s (%s): mixer=%s Set_Channels=%d Fail error=%s", + __func__, card, mixer->uid, opts->channels, snd_strerror(error)); goto OnErrorExit; }; } @@ -165,7 +176,7 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { error = snd_pcm_hw_params_get_buffer_time_max(pxmHwParams, &buffer_time, 0); - AFB_ApiInfo(mixer->api, "HW_BUFFER_TIME MAX is %d\n", buffer_time); + AFB_ApiDebug(mixer->api, "(%s) HW_BUFFER_TIME MAX is %d", card, buffer_time); if (buffer_time > 500000) buffer_time = 500000; @@ -178,45 +189,45 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { } if (period_time > 0) { - AFB_ApiInfo(mixer->api, "SET PERIOD TIME to %d", period_time); + AFB_ApiDebug(mixer->api, "(%s) SET PERIOD TIME to %d", card, period_time); error = snd_pcm_hw_params_set_period_time_near(pcm->handle, pxmHwParams, &period_time, 0); } else { - AFB_ApiInfo(mixer->api, "SET PERIOD SIZE..."); + AFB_ApiDebug(mixer->api, "(%s) SET PERIOD SIZE...", card); error = snd_pcm_hw_params_set_period_size_near(pcm->handle, pxmHwParams, &period_frames, 0); } if (error < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Fail to set period in hwparams error=%s", - __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error)); + "%s (%s): mixer=%s Fail to set period in hwparams error=%s", + __func__, card, mixer->uid, snd_strerror(error)); goto OnErrorExit; } if (buffer_time > 0) { - AFB_ApiInfo(mixer->api, "SET BUFFER TIME to %d", buffer_time); + AFB_ApiDebug(mixer->api, "(%s) SET BUFFER TIME to %d", card, buffer_time); error = snd_pcm_hw_params_set_buffer_time_near(pcm->handle, pxmHwParams, &buffer_time, 0); } else { - AFB_ApiInfo(mixer->api, "SET BUFFER SIZE..."); + AFB_ApiDebug(mixer->api, "(%s) SET BUFFER SIZE to %ld...", card, buffer_frames); error = snd_pcm_hw_params_set_buffer_size_near(pcm->handle, pxmHwParams, &buffer_frames); } if (error < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Fail to set buffer in hwparams error=%s", - __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error)); + "%s (%s): mixer=%s Fail to set buffer in hwparams error=%s", + __func__, card, mixer->uid, snd_strerror(error)); goto OnErrorExit; } // store selected values if ((error = snd_pcm_hw_params(pcm->handle, pxmHwParams)) < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Fail to apply hwparams error=%s", - __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error)); + "%s (%s): mixer=%s Fail to apply hwparams error=%s", + __func__, card, mixer->uid, snd_strerror(error)); goto OnErrorExit; } - AFB_ApiDebug(mixer->api, "PARAMS after:\n"); + AFB_ApiDebug(mixer->api, "(%s) PARAMS after:", card); AlsaDumpPcmParams(mixer, pxmHwParams); // check we effective hw params after optional format change @@ -224,13 +235,13 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { snd_pcm_hw_params_get_format(pxmHwParams, &opts->format); snd_pcm_hw_params_get_rate(pxmHwParams, &opts->rate, 0); - AFB_ApiInfo(mixer->api, "rate is %d", opts->rate); + AFB_ApiDebug(mixer->api, "(%s) rate is %d", card, opts->rate); opts->sampleSize = AlsaPeriodSize(opts->format); if (opts->sampleSize == 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Fail unsupported format format=%d", - __func__, mixer->uid, pcm->cid.cardid, opts->format); + "%s (%s): mixer=%s Fail unsupported format format=%d", + __func__, card, mixer->uid, opts->format); goto OnErrorExit; } @@ -240,8 +251,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { snd_pcm_hw_params_get_buffer_size(pxmHwParams, &buffer_size); if (chunk_size == buffer_size) { AFB_ApiError(mixer->api, - "Can't use period equal to buffer size (%lu == %lu)", - chunk_size, buffer_size); + "(%s) Can't use period equal to buffer size (%lu == %lu)", + card, chunk_size, buffer_size); goto OnErrorExit; } @@ -263,8 +274,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { if ((error = snd_pcm_sw_params_set_avail_min(pcm->handle, pxmSwParams, n)) < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Fail set_buffersize error=%s", - __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error)); + "%s (%s): mixer=%s Fail set_buffersize error=%s", + __func__, card, mixer->uid, snd_strerror(error)); goto OnErrorExit; }; @@ -285,39 +296,39 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) { if (start_threshold > n/2) start_threshold = n/2; - AFB_ApiInfo(mixer->api, "CALCULATED START THRESHOLD: %ld", start_threshold); + AFB_ApiDebug(mixer->api, "(%s) CALCULATED START THRESHOLD: %ld", card, start_threshold); if (mode == SND_PCM_STREAM_PLAYBACK) { start_threshold = 1; } - AFB_ApiInfo(mixer->api, "%s: Set start threshold to %ld", modeS, start_threshold); + AFB_ApiDebug(mixer->api, "(%s) %s: Set start threshold to %ld", card, modeS, start_threshold); error = snd_pcm_sw_params_set_start_threshold(pcm->handle, pxmSwParams, start_threshold); if (error < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s failed set start_threshold, error=%s", - __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error)); + "%s (%s): mixer=%s failed set start_threshold, error=%s", + __func__, card, mixer->uid, snd_strerror(error)); goto OnErrorExit; } // push software params into PCM if ((error = snd_pcm_sw_params(pcm->handle, pxmSwParams)) < 0) { AFB_ApiError(mixer->api, - "%s: mixer=%s cardid=%s Fail to push SW params error=%s", - __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error)); + "%s (%s): mixer=%s Fail to push SW params error=%s", + __func__, card, mixer->uid, snd_strerror(error)); goto OnErrorExit; }; AFB_ApiNotice(mixer->api, - "%s: mixer=%s cardid=%s Done channels=%d rate=%d format=%d access=%d ... done !", - __func__, mixer->uid, pcm->cid.cardid, opts->channels, opts->rate, opts->format, opts->access); + "%s (%s): mixer=%s Done channels=%d rate=%d format=%d access=%d ... DONE !", + __func__, card, mixer->uid, opts->channels, opts->rate, opts->format, opts->access); return 0; OnErrorExit: return -1; } -STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandle) { +STATIC int AlsaPcmReadCB( AlsaPcmCopyHandleT * pcmCopyHandle) { char string[32]; snd_pcm_sframes_t availIn; @@ -327,19 +338,6 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl int err; - // PCM has was closed - if ((pfd->revents & POLLHUP) != 0) { - AFB_ApiNotice(pcmCopyHandle->api, - "%s PCM=%s hanghup/disconnected", - __func__, ALSA_PCM_UID(pcmIn, string)); - goto ExitOnSuccess; - } - - // ignore any non input events. This is not supposed to happen ever - if ((pfd->revents & EPOLLIN) == 0) { - goto ExitOnSuccess; - } - // do we have waiting frames ? availIn = snd_pcm_avail_update(pcmIn); if (availIn <= 0) { @@ -465,16 +463,20 @@ static void *readThreadEntry(void *handle) { AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) handle; pcmCopyHandle->tid = (int) syscall(SYS_gettid); + int ix; AFB_ApiNotice(pcmCopyHandle->api, "%s :%s/%d Started, muted=%d", __func__, pcmCopyHandle->info, pcmCopyHandle->tid, pcmCopyHandle->pcmIn->mute); - struct pollfd * mutePfd = &pcmCopyHandle->pollFds[0]; - struct pollfd * framePfd = &pcmCopyHandle->pollFds[1]; + struct pollfd * eventFd = &pcmCopyHandle->pollFds[0]; + struct pollfd * framePfds = &pcmCopyHandle->pollFds[1]; - mutePfd->events = POLLIN | POLLHUP; - framePfd->events = POLLIN | POLLHUP; + eventFd->events = POLLIN | POLLHUP; + + for (ix = 0; ix nbPcmFds-1; ix++) { + framePfds[ix].events = POLLIN | POLLHUP; + } bool muted = pcmCopyHandle->pcmIn->mute; @@ -492,48 +494,60 @@ static void *readThreadEntry(void *handle) { if (err == 0) { /* timeout */ - AFB_ApiDebug(pcmCopyHandle->api, "%s(%s) alive, mute %d", __func__, pcmCopyHandle->pcmIn->cid.cardid, muted ); +// AFB_ApiDebug(pcmCopyHandle->api, "%s(%s) alive, mute %d", __func__, pcmCopyHandle->pcmIn->cid.cardid, muted ); continue; } - // handle the un/mute order - if ((mutePfd->revents & EPOLLIN) != 0) { - bool mute; - - size_t ret = read(mutePfd->fd, &mute, sizeof(mute)); - if (ret <= 0) - continue; + // handle the incoming events/mute order + if ((eventFd->revents & EPOLLIN) != 0) { + PcmCopyEvent event; - if (mute == muted) - continue; - - muted = mute; + size_t ret = read(eventFd->fd, &event, sizeof(event)); + if (ret <= 0) + continue; - if (muted) { - readSuspend(pcmCopyHandle); - } else { - readResume(pcmCopyHandle); + switch (event.eventType) { + case PCM_COPY_MUTE: + if (!muted) { + readSuspend(pcmCopyHandle); + muted = true; + } + break; + case PCM_COPY_UNMUTE: + if (muted) { + readResume(pcmCopyHandle); + muted = false; + }; + break; + case PCM_COPY_END: + AFB_ApiDebug(pcmCopyHandle->api, "%s ending -> EXIT", __func__); + goto done; + break; + case PCM_COPY_LAST: + default: + AFB_ApiError(pcmCopyHandle->api, "%s: Unexpected event 0x%x", __func__, event.eventType); + break; } continue; } unsigned short revents; - int ret = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, &pcmCopyHandle->pollFds[1], 1, &revents); + int res = snd_pcm_poll_descriptors_revents(pcmCopyHandle->pcmIn->handle, framePfds, pcmCopyHandle->nbPcmFds, &revents); - if (ret == -ENODEV) { + if (res == -ENODEV) { sleep(1); continue; } - if (framePfd->revents & POLLHUP) { + if (revents & POLLHUP) { AFB_ApiNotice(pcmCopyHandle->api, "Frame POLLHUP"); continue; } - AlsaPcmReadCB(&pcmCopyHandle->pollFds[1], pcmCopyHandle); + AlsaPcmReadCB(pcmCopyHandle); } - +done: pthread_exit(0); return NULL; } @@ -554,6 +568,8 @@ static void *writeThreadEntry(void *handle) { snd_pcm_status(pcmOut, pcmOutStatus); pcmOutSize = snd_pcm_status_get_avail_max(pcmOutStatus); + const char * cardid = pcmCopyHandle->pcmOut->cid.cardid; + /* This threshold is the expected space available in the hw output buffer * The aim is to wait to have a significant amount of space, in order to * avoid to write to the device too often, or take a very small amount of @@ -566,17 +582,23 @@ static void *writeThreadEntry(void *handle) { sem_wait(&pcmCopyHandle->sem); while (true) { + + if (pcmCopyHandle->ending) { + AFB_ApiDebug(pcmCopyHandle->api, "%s: ending -> EXIT", __func__); + goto done; + } + snd_pcm_sframes_t used, nbWritten; snd_pcm_sframes_t availOut = snd_pcm_avail(pcmOut); if (availOut < 0) { if (availOut == -EPIPE) { - AFB_ApiDebug(pcmCopyHandle->api, "write update EPIPE"); + AFB_ApiDebug(pcmCopyHandle->api, "%s: write update EPIPE", cardid); xrun(pcmOut, (int)availOut); continue; } if (availOut == -ESTRPIPE) { - AFB_ApiDebug(pcmCopyHandle->api, "write update ESTRPIPE"); + AFB_ApiDebug(pcmCopyHandle->api, "%s: write update ESTRPIPE", cardid); suspend(pcmOut, (int)availOut); continue; } @@ -606,7 +628,8 @@ static void *writeThreadEntry(void *handle) { if (nbWritten <= 0) { if (nbWritten == -EPIPE) { int err = xrun(pcmOut, (int)nbWritten); - AFB_ApiDebug(pcmCopyHandle->api, "XXX write EPIPE (%d), recov %d", ++pcmCopyHandle->write_err_count , err); + AFB_ApiDebug(pcmCopyHandle->api, "XXX %s write EPIPE (%d), recov %d", + pcmCopyHandle->pcmOut->cid.cardid, ++pcmCopyHandle->write_err_count , err); continue; } else if (nbWritten == -ESTRPIPE) { @@ -620,30 +643,70 @@ static void *writeThreadEntry(void *handle) { } } - +done: pthread_exit(0); return NULL; } PUBLIC int AlsaPcmCopyMuteSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn, bool mute) { - ssize_t ret = write(pcmIn->muteFd, &mute, sizeof(mute)); + + PcmCopyEvent event; + event.eventType = mute?PCM_COPY_MUTE:PCM_COPY_UNMUTE; + ssize_t ret = write(pcmIn->eventFd, &event, sizeof(event)); (void) ret; return 0; } +static int AlsaPcmCopyEndSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn) { + PcmCopyEvent event; + event.eventType = PCM_COPY_END; + ssize_t ret = write(pcmIn->eventFd, &event, sizeof(event)); + (void) ret; + return 0; +} + +static int AlsaPcmCopyEnd(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) { + + handle->ending = true; + // wake up the reader through the eventfd + AlsaPcmCopyEndSignal(mixer, handle->pcmIn); + // wake up the writer through the wait semaphore + sem_post(&handle->sem); -PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts) { + return 0; +} + +PUBLIC int AlsaPcmCopyStop(SoftMixerT *mixer, AlsaPcmCopyHandleT * handle) { + + AFB_ApiDebug(mixer->api, "%s: Stopping copy threads of %s", __func__, handle->stream->uid); + + AlsaPcmCopyEnd(mixer, handle); + + if (pthread_join(handle->wthread, NULL) != 0) + AFB_ApiDebug(mixer->api, "%s: Failed to join write thread", __func__); + + if (pthread_join(handle->rthread, NULL) != 0) + AFB_ApiDebug(mixer->api, "%s: Failed to join read thread", __func__); + + sem_destroy(&handle->sem); + + AFB_ApiDebug(mixer->api, "%s: Copy threads of %s are STOPPED", __func__, handle->stream->uid); + + return 0; + +} + +PUBLIC int AlsaPcmCopyStart(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts) { char string[32]; int error; + AlsaPcmCopyHandleT *cHandle=NULL; // Fulup need to check https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___direct.html AlsaDumpPcmInfo(mixer,"PcmIn",pcmIn->handle); AlsaDumpPcmInfo(mixer,"PcmOut",pcmOut->handle); - AFB_ApiInfo(mixer->api, "%s: Configure CAPTURE PCM", __func__); - /* remember configuration of capture */ pcmIn->params = (AlsaPcmHwInfoT*)malloc(sizeof(AlsaPcmHwInfoT)); memcpy(pcmIn->params, opts, sizeof(AlsaPcmHwInfoT)); @@ -654,7 +717,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT pcmIn->mixer = mixer; pcmOut->mixer = mixer; - AFB_ApiInfo(mixer->api, "%s: Configure CAPTURE PCM", __func__); + AFB_ApiDebug(mixer->api, "%s: Configure CAPTURE PCM", __func__); // prepare PCM for capture and replay error = AlsaPcmConf(mixer, pcmIn, SND_PCM_STREAM_CAPTURE); @@ -663,7 +726,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT goto OnErrorExit; } - AFB_ApiInfo(mixer->api, "%s: Configure PLAYBACK PCM", __func__); + AFB_ApiDebug(mixer->api, "%s: Configure PLAYBACK PCM", __func__); // input and output should match error = AlsaPcmConf(mixer, pcmOut, SND_PCM_STREAM_PLAYBACK); @@ -696,26 +759,35 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT goto OnErrorExit; }; - AlsaPcmCopyHandleT *cHandle= calloc(1, sizeof(AlsaPcmCopyHandleT)); + cHandle = calloc(1, sizeof(AlsaPcmCopyHandleT)); + if (cHandle == NULL) { + SOFTMIXER_NOMEM(mixer->api); + goto OnErrorExit; + } cHandle->info = "pcmCpy"; cHandle->pcmIn = pcmIn; cHandle->pcmOut = pcmOut; cHandle->api = mixer->api; cHandle->channels = opts->channels; + cHandle->stream = stream; cHandle->frame_size = (snd_pcm_format_physical_width(opts->format) / 8) * opts->channels; + AFB_ApiDebug(mixer->api, "%s: Frame size is %zu", __func__, cHandle->frame_size); + AFB_ApiDebug(mixer->api, "%s: Buffer delay is %d ms", __func__, stream->delayms); - AFB_ApiInfo(mixer->api, "%s: Frame size is %zu", __func__, cHandle->frame_size); - - snd_pcm_uframes_t nbFrames = 2 * opts->rate; // Exactly 2 second of buffer + snd_pcm_uframes_t nbFrames = (stream->delayms * opts->rate)/1000; cHandle->rbuf = alsa_ringbuf_new(nbFrames, cHandle->frame_size); + if (!cHandle->rbuf) { + SOFTMIXER_NOMEM(mixer->api); + goto OnErrorExit; + } cHandle->read_err_count = 0; cHandle->write_err_count = 0; - AFB_ApiInfo(mixer->api, "%s Copy buffer nbframes is %zu", __func__, nbFrames); + AFB_ApiDebug(mixer->api, "%s Copy buffer: nbframes is %zu", __func__, nbFrames); // get FD poll descriptor for capture PCM int pcmInCount = snd_pcm_poll_descriptors_count(pcmIn->handle); @@ -724,17 +796,16 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT "%s: Fail pcmIn=%s get fds count error=%s", __func__, ALSA_PCM_UID(pcmIn->handle, string), snd_strerror(error)); goto OnErrorExit; - }; + } - if (pcmInCount > 1) { - AFB_ApiError(mixer->api, - "%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); + cHandle->nbPcmFds = pcmInCount+1; + cHandle->pollFds = (struct pollfd *) malloc((cHandle->nbPcmFds)*sizeof(struct pollfd)); + if (cHandle->pollFds == NULL){ + SOFTMIXER_NOMEM(mixer->api); goto OnErrorExit; } - struct pollfd pcmInFd; - if ((error = snd_pcm_poll_descriptors(pcmIn->handle, &pcmInFd, 1)) < 0) { + if ((error = snd_pcm_poll_descriptors(pcmIn->handle, cHandle->pollFds+1, pcmInCount)) < 0) { AFB_ApiError(mixer->api, "%s: Fail pcmIn=%s get pollfds error=%s", __func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error)); @@ -742,27 +813,22 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT }; // create the mute pipe - int pMuteFd[2]; - error = pipe(pMuteFd); + int eventFdPipe[2]; + error = pipe(eventFdPipe); if (error < 0) { AFB_ApiError(mixer->api, "Unable to create the mute signaling pipe"); goto OnErrorExit; } - struct pollfd mutePFd; + struct pollfd * eventPollFd = &cHandle->pollFds[0]; // read end - mutePFd.fd = pMuteFd[0]; - mutePFd.events = POLLIN; - mutePFd.revents = 0; + eventPollFd->fd = eventFdPipe[0]; + eventPollFd->events = POLLIN; + eventPollFd->revents = 0; // write end - pcmIn->muteFd = pMuteFd[1]; - - cHandle->pollFds[0] = mutePFd; - cHandle->pollFds[1] = pcmInFd; - - cHandle->nbPcmFds = pcmInCount+1; + pcmIn->eventFd = eventFdPipe[1]; error = sem_init(&cHandle->sem, 0 , 0); if (error < 0) { @@ -818,11 +884,22 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT __func__, ALSA_PCM_UID(pcmOut->handle, string), strerror(error)); } + stream->copy = cHandle; + return 0; OnErrorExit: AFB_ApiError(mixer->api, "%s: - pcmIn=%s" , __func__, ALSA_PCM_UID(pcmIn->handle, string)); AFB_ApiError(mixer->api, "%s: - pcmOut=%s", __func__, ALSA_PCM_UID(pcmOut->handle, string)); + + if (cHandle &&cHandle->pollFds) { + free (cHandle->pollFds); + cHandle->pollFds = NULL; + } + + if (cHandle) + free(cHandle); + return -1; } -- cgit 1.2.3-korg