summaryrefslogtreecommitdiffstats
path: root/plugins/alsa/alsa-core-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/alsa/alsa-core-pcm.c')
-rw-r--r--plugins/alsa/alsa-core-pcm.c170
1 files changed, 138 insertions, 32 deletions
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index cc3d2f6..885d2bf 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -69,13 +69,15 @@ STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) {
return pcmSampleSize;
}
-PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, AlsaPcmHwInfoT *opts, int mode) {
+PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
int error;
snd_pcm_hw_params_t *pxmHwParams;
snd_pcm_sw_params_t *pxmSwParams;
snd_pcm_format_t format;
snd_pcm_access_t access;
+ AlsaPcmHwInfoT * opts = pcm->params;
+
AFB_ApiInfo(mixer->api,
"%s: mixer info %s uid %s , pcm %s, mode %d",
__func__, mixer->info, mixer->uid, pcm->cid.cardid, mode);
@@ -114,23 +116,33 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, AlsaPcmHwInfoT *opts
}
}
- if (opts->rate > 0) {
+ if (opts->rate > 0 ) {
+
+ AFB_ApiInfo(mixer->api," %s: set rate to %d", __func__, opts->rate);
unsigned int pcmRate = opts->rate;
- if ((error = snd_pcm_hw_params_set_rate_near(pcm->handle, pxmHwParams, &opts->rate, 0)) < 0) {
- AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s FailSet_Rate=%d error=%s", mixer->uid, pcm->cid.cardid, opts->rate, snd_strerror(error));
+ /* 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));
goto OnErrorExit;
}
// check we got requested rate
- if (opts->rate != pcmRate) {
- AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Set_Rate Fail ask=%dHz get=%dHz", mixer->uid, pcm->cid.cardid,pcmRate, opts->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);
goto OnErrorExit;
}
}
if (opts->channels) {
if ((error = snd_pcm_hw_params_set_channels(pcm->handle, pxmHwParams, opts->channels)) < 0) {
- AFB_ApiError(mixer->api, "AlsaPcmConf: mixer=%s cardid=%s Set_Channels=%d Fail error=%s",mixer->uid, pcm->cid.cardid, opts->channels, snd_strerror(error));
+ 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));
goto OnErrorExit;
};
}
@@ -223,11 +235,14 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
snd_pcm_sframes_t availIn, availOut, availInBuf;
int err;
+ snd_pcm_t * pcmIn = pcmCopyHandle->pcmIn->handle;
+ snd_pcm_t * pcmOut= pcmCopyHandle->pcmOut->handle;
+
// PCM has was closed
if ((pfd->revents & POLLHUP) != 0) {
AFB_ApiNotice(pcmCopyHandle->api,
"%s PCM=%s hanghup/disconnected",
- __func__, ALSA_PCM_UID(pcmCopyHandle->pcmIn, string));
+ __func__, ALSA_PCM_UID(pcmIn, string));
goto ExitOnSuccess;
}
@@ -237,11 +252,11 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
}
// do we have waiting frame
- availIn = snd_pcm_avail_update(pcmCopyHandle->pcmIn);
+ availIn = snd_pcm_avail_update(pcmIn);
if (availIn <= 0) {
if (availIn == -EPIPE) {
- printf("XXX read EPIPE\n");
- xrun(pcmCopyHandle->pcmIn);
+ int ret = xrun(pcmIn);
+ printf("XXX read EPIPE (recov=%d)\n", ret);
}
goto ExitOnSuccess;
}
@@ -263,7 +278,7 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
r = pcmCopyHandle->buf_size - pcmCopyHandle->buf_pos;
if (r > availIn)
r = availIn;
- r = snd_pcm_readi(pcmCopyHandle->pcmIn,
+ r = snd_pcm_readi(pcmIn,
pcmCopyHandle->buf +
pcmCopyHandle->buf_pos *
pcmCopyHandle->frame_size, r);
@@ -271,12 +286,11 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
goto ExitOnSuccess;
if (r < 0) {
if (r == -EPIPE) {
- printf("read EPIPE (%d)\n", ++pcmCopyHandle->read_err_count);
- err = xrun(pcmCopyHandle->pcmIn);
+ printf("read EPIPE (%d), recov %d\n", ++pcmCopyHandle->read_err_count, xrun(pcmIn));
goto ExitOnSuccess;
} else if (r == -ESTRPIPE) {
printf("read ESTRPIPE\n");
- if ((err = suspend(pcmCopyHandle->pcmIn)) < 0)
+ if ((err = suspend(pcmIn)) < 0)
goto ExitOnSuccess;
r = 0;
} else {
@@ -293,16 +307,16 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
}
// do we have space to push frame
- availOut = snd_pcm_avail_update(pcmCopyHandle->pcmOut);
+ availOut = snd_pcm_avail_update(pcmOut);
if (availOut < 0) {
if (availOut == -EPIPE) {
printf("write update EPIPE\n");
- xrun(pcmCopyHandle->pcmOut);
+ xrun(pcmOut);
goto ExitOnSuccess;
}
if (availOut == -ESTRPIPE) {
printf("write update ESTRPIPE\n");
- suspend(pcmCopyHandle->pcmOut);
+ suspend(pcmOut);
goto ExitOnSuccess;
}
}
@@ -322,14 +336,14 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
r = availOut;
r = snd_pcm_writei(
- pcmCopyHandle->pcmOut,
+ pcmOut,
pcmCopyHandle->buf +
pcmCopyHandle->buf_pos *
pcmCopyHandle->frame_size, r);
if (r <= 0) {
if (r == -EPIPE) {
- printf("XXX write EPIPE (%d)\n", ++pcmCopyHandle->write_err_count );
- err = xrun(pcmCopyHandle->pcmOut);
+ printf("XXX write EPIPE (%d), recov %d\n", ++pcmCopyHandle->write_err_count , xrun(pcmOut));
+
continue;
} else if (r == -ESTRPIPE) {
printf("XXX write ESTRPIPE\n");
@@ -350,10 +364,10 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
* before the loop. If we ignore a new bigger value, we will keep
* writing small chunks, keep in tight loops and stave the CPU */
- snd_pcm_sframes_t newAvailOut = snd_pcm_avail(pcmCopyHandle->pcmOut);
+ snd_pcm_sframes_t newAvailOut = snd_pcm_avail(pcmOut);
if (newAvailOut == 0) {
- snd_pcm_wait(pcmCopyHandle->pcmOut, 5);
+ snd_pcm_wait(pcmOut, 5);
} else if (newAvailOut > 0) {
availOut = newAvailOut;
}
@@ -390,30 +404,111 @@ static int suspend( snd_pcm_t * pcm)
return 0;
}
+static int capturePcmReopen(AlsaPcmCopyHandleT * pcmCopyHandle) {
+ int res = -1;
+ int err;
+ char string[32];
+
+ err = snd_pcm_open(&pcmCopyHandle->pcmIn->handle,
+ pcmCopyHandle->pcmIn->cid.cardid,
+ SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+
+ if (err < 0) {
+ AFB_ApiError(pcmCopyHandle->api, "%s: failed to re-open pcm", __func__);
+ goto OnErrorExit;
+ };
+
+ // prepare PCM for capture
+ err = AlsaPcmConf(pcmCopyHandle->pcmIn->mixer, pcmCopyHandle->pcmIn, SND_PCM_STREAM_CAPTURE);
+ if (err) {
+ AFB_ApiError(pcmCopyHandle->api, "%s: PCM configuration failed", __func__);
+ goto OnErrorExit;
+ }
+
+ err = snd_pcm_prepare(pcmCopyHandle->pcmIn->handle);
+ if (err < 0) {
+ AFB_ApiError(pcmCopyHandle->api, "%s: failed to prepare PCM, %s", __func__, snd_strerror(err));
+ goto OnErrorExit;
+ };
+
+ err = snd_pcm_start(pcmCopyHandle->pcmIn->handle);
+ if (err < 0) {
+ AFB_ApiError(pcmCopyHandle->api, "%s: failed start capture PCM: %s",__func__, snd_strerror(err));
+ goto OnErrorExit;
+ };
+
+ struct pollfd * pcmInFds;
+
+ int pcmInCount = snd_pcm_poll_descriptors_count(pcmCopyHandle->pcmIn->handle);
+ pcmInFds = malloc(sizeof (*pcmInFds) * pcmInCount);
+
+ if ((err = snd_pcm_poll_descriptors(pcmCopyHandle->pcmIn->handle, pcmInFds, pcmInCount)) < 0) {
+ AFB_ApiError(pcmCopyHandle->api, "%s: Fail pcmIn=%s get pollfds error=%s",
+ __func__, ALSA_PCM_UID(pcmCopyHandle->pcmIn->handle, string), snd_strerror(err));
+ goto OnErrorExit;
+ };
+
+ /* free old descriptors */
+ free(pcmCopyHandle->pollFds);
+ pcmCopyHandle->pollFds = pcmInFds;
+ pcmCopyHandle->pcmInCount = pcmInCount;
+
+ res = 0;
+
+OnErrorExit:
+ return res;
+}
static void *LoopInThread(void *handle) {
+#define LOOP_TIMEOUT_MSEC 10*1000
+
AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) handle;
pcmCopyHandle->tid = (int) syscall(SYS_gettid);
AFB_ApiNotice(pcmCopyHandle->api,
- "%s :%s/%d Started",
- __func__, pcmCopyHandle->info, pcmCopyHandle->tid);
+ "%s :%s/%d Started, mute %d",
+ __func__, pcmCopyHandle->info, pcmCopyHandle->tid, pcmCopyHandle->pcmIn->mute);
for (int ix=0; ix<pcmCopyHandle->pcmInCount; ix++) {
struct pollfd * pfd = &pcmCopyHandle->pollFds[ix];
pfd->events = POLLIN | POLLHUP;
}
+ bool muted = false;
+
/* loop until end */
for (;;) {
- int err = poll(pcmCopyHandle->pollFds, pcmCopyHandle->pcmInCount, 5000); // TODO
+ if (pcmCopyHandle->pcmIn->mute) {
+ if (!muted) {
+ int err;
+ muted = true;
+
+ err = snd_pcm_close(pcmCopyHandle->pcmIn->handle);
+ if (err < 0) AFB_ApiNotice(pcmCopyHandle->api, "failed to close capture fd\n");
+
+ AFB_ApiNotice(pcmCopyHandle->api, "capture muted");
+ }
+ sleep(1);
+ continue;
+ }
+
+ if (muted) {
+ if (capturePcmReopen(pcmCopyHandle) < 0)
+ goto OnErrorExit;
- if (err < 0)
+ muted = false;
+ }
+
+ int err = poll(pcmCopyHandle->pollFds, pcmCopyHandle->pcmInCount, LOOP_TIMEOUT_MSEC);
+ if (err < 0) {
+ AFB_ApiError(pcmCopyHandle->api, "%s: poll err %s", __func__, strerror(errno));
continue;
+ }
if (err == 0) {
/* timeout */
+ AFB_ApiInfo(pcmCopyHandle->api, "%s(%s) alive", __func__, pcmCopyHandle->pcmIn->cid.cardid );
continue;
}
@@ -421,6 +516,7 @@ static void *LoopInThread(void *handle) {
AlsaPcmReadCB(&pcmCopyHandle->pollFds[ix], pcmCopyHandle);
}
+OnErrorExit:
pthread_exit(0);
}
@@ -442,8 +538,18 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
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));
+
+ pcmOut->params = (AlsaPcmHwInfoT*)malloc(sizeof(AlsaPcmHwInfoT));
+ memcpy(pcmOut->params, opts, sizeof(AlsaPcmHwInfoT));
+
+ pcmIn->mixer = mixer;
+ pcmOut->mixer = mixer;
+
// prepare PCM for capture and replay
- error = AlsaPcmConf(mixer, pcmIn, opts, SND_PCM_STREAM_CAPTURE);
+ error = AlsaPcmConf(mixer, pcmIn, SND_PCM_STREAM_CAPTURE);
if (error) {
AFB_ApiError(mixer->api, "%s: PCM configuration for capture failed", __func__);
goto OnErrorExit;
@@ -452,7 +558,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
AFB_ApiInfo(mixer->api, "%s: Configure PLAYBACK PCM", __func__);
// input and output should match
- error = AlsaPcmConf(mixer, pcmOut, opts, SND_PCM_STREAM_PLAYBACK);
+ error = AlsaPcmConf(mixer, pcmOut, SND_PCM_STREAM_PLAYBACK);
if (error) {
AFB_ApiError(mixer->api, "%s: PCM configuration for playback failed", __func__);
goto OnErrorExit;
@@ -485,8 +591,8 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
AlsaPcmCopyHandleT *cHandle= calloc(1, sizeof(AlsaPcmCopyHandleT));
cHandle->info = "pcmCpy";
- cHandle->pcmIn = pcmIn->handle;
- cHandle->pcmOut = pcmOut->handle;
+ cHandle->pcmIn = pcmIn;
+ cHandle->pcmOut = pcmOut;
cHandle->api = mixer->api;
cHandle->channels = opts->channels;
@@ -509,7 +615,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
AFB_ApiInfo(mixer->api, "%s Copy buf size is %zu", __func__, size);
// get FD poll descriptor for capture PCM
- int pcmInCount = snd_pcm_poll_descriptors_count(cHandle->pcmIn);
+ int pcmInCount = snd_pcm_poll_descriptors_count(pcmIn->handle);
if (pcmInCount <= 0) {
AFB_ApiError(mixer->api,
"%s: Fail pcmIn=%s get fds count error=%s",