aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/alsa
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/alsa')
-rw-r--r--plugins/alsa/alsa-api-mixer.c2
-rw-r--r--plugins/alsa/alsa-core-ctl.c8
-rw-r--r--plugins/alsa/alsa-core-pcm.c543
-rw-r--r--plugins/alsa/alsa-softmixer.h25
4 files changed, 324 insertions, 254 deletions
diff --git a/plugins/alsa/alsa-api-mixer.c b/plugins/alsa/alsa-api-mixer.c
index ebb3f10..558a522 100644
--- a/plugins/alsa/alsa-api-mixer.c
+++ b/plugins/alsa/alsa-api-mixer.c
@@ -35,7 +35,7 @@ static void MixerRemoveVerb(AFB_ReqT request) {
AFB_ApiNotice(mixer->api, "cleaning mixer=%s stream=%s", mixer->uid, stream->uid);
- error = pthread_cancel(stream->copy->thread);
+ error = pthread_cancel(stream->copy->rthread);
if (error) {
AFB_ReqFailF(request, "internal-error", "Fail to kill audio-stream threads mixer=%s", mixer->uid);
goto OnErrorExit;
diff --git a/plugins/alsa/alsa-core-ctl.c b/plugins/alsa/alsa-core-ctl.c
index dee8c0b..d128b6c 100644
--- a/plugins/alsa/alsa-core-ctl.c
+++ b/plugins/alsa/alsa-core-ctl.c
@@ -521,20 +521,22 @@ STATIC int CtlSubscribeEventCB(sd_event_source* src, int fd, uint32_t revents, v
for (index = 0; sndcard->registry[index]; index++) {
RegistryEntryPcmT * reg = sndcard->registry[index];
+ snd_pcm_t * pcm = reg->pcm->handle;
if (reg->numid == numid) {
int ret;
switch (reg->type) {
case FONTEND_NUMID_RUN:
- reg->pcm->mute = !value;
- ret = snd_pcm_pause(reg->pcm->handle, (int) (!value));
+ AlsaPcmCopyMuteSignal(mixer, reg->pcm, !value);
+ ret = snd_pcm_pause(pcm, (int) (!value));
AFB_ApiNotice(mixer->api, "%s:%s numid=%d name=%s active=%ld ret %d",
__func__, sHandle->uid, numid, name, value, ret);
if (ret < 0) {
AFB_ApiNotice(mixer->api, "%s error: %s", __func__, snd_strerror(ret));
}
+
break;
case FONTEND_NUMID_PAUSE:
- reg->pcm->mute = value;
+ AlsaPcmCopyMuteSignal(mixer, reg->pcm, value);
ret = snd_pcm_pause(reg->pcm->handle, (int) value);
AFB_ApiNotice(mixer->api, "%s:%s numid=%d name=%s pause=%ld ret %d",
__func__, sHandle->uid, numid, name, value, ret);
diff --git a/plugins/alsa/alsa-core-pcm.c b/plugins/alsa/alsa-core-pcm.c
index e672429..1637949 100644
--- a/plugins/alsa/alsa-core-pcm.c
+++ b/plugins/alsa/alsa-core-pcm.c
@@ -29,13 +29,10 @@ for the specific language governing permissions and
#include <sys/syscall.h>
#include <sched.h>
-static int xrun(snd_pcm_t * pcm);
-static int suspend(snd_pcm_t * pcm);
+#include "time_utils.h"
-static inline snd_pcm_uframes_t buf_avail(AlsaPcmCopyHandleT * chandle)
-{
- return chandle->buf_size - chandle->buf_count;
-}
+static int xrun(snd_pcm_t * pcm, int error);
+static int suspend(snd_pcm_t * pcm, int error);
STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) {
@@ -55,6 +52,13 @@ STATIC int AlsaPeriodSize(snd_pcm_format_t pcmFormat) {
pcmSampleSize = 2;
break;
+ case SND_PCM_FORMAT_U24_LE:
+ case SND_PCM_FORMAT_U24_BE:
+ case SND_PCM_FORMAT_S24_LE:
+ case SND_PCM_FORMAT_S24_BE:
+ pcmSampleSize = 3;
+ break;
+
case SND_PCM_FORMAT_U32_LE:
case SND_PCM_FORMAT_U32_BE:
case SND_PCM_FORMAT_S32_LE:
@@ -78,9 +82,11 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
AlsaPcmHwInfoT * opts = pcm->params;
+ const char * modeS = mode==SND_PCM_STREAM_PLAYBACK?"PLAYBACK":"CAPTURE";
+
AFB_ApiInfo(mixer->api,
- "%s: mixer info %s uid %s , pcm %s, mode %d",
- __func__, mixer->info, mixer->uid, pcm->cid.cardid, mode);
+ "%s: mixer info %s uid %s , pcm %s, mode %s",
+ __func__, mixer->info, mixer->uid, pcm->cid.cardid, modeS);
// retrieve hardware config from PCM
snd_pcm_hw_params_alloca(&pxmHwParams);
@@ -159,6 +165,8 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
error = snd_pcm_hw_params_get_buffer_time_max(pxmHwParams, &buffer_time, 0);
+ printf("HW_BUFFER_TIME MAX is %d\n", buffer_time);
+
if (buffer_time > 500000)
buffer_time = 500000;
@@ -169,10 +177,14 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
period_frames = buffer_frames / 4;
}
- if (period_time > 0)
+ if (period_time > 0) {
+ printf("SET PERIOD TIME to %d\n", period_time);
error = snd_pcm_hw_params_set_period_time_near(pcm->handle, pxmHwParams, &period_time, 0);
- else
+ }
+ else {
+ printf("SET PERIOD SIZE\n");
error = snd_pcm_hw_params_set_period_size_near(pcm->handle, pxmHwParams, &period_frames, 0);
+ }
if (error < 0) {
AFB_ApiError(mixer->api,
@@ -182,8 +194,10 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
}
if (buffer_time > 0) {
+ printf("SET BUFFER TIME to %d\n", buffer_time);
error = snd_pcm_hw_params_set_buffer_time_near(pcm->handle, pxmHwParams, &buffer_time, 0);
} else {
+ printf("SET BUFFER SIZE\n");
error = snd_pcm_hw_params_set_buffer_size_near(pcm->handle, pxmHwParams, &buffer_frames);
}
@@ -244,6 +258,9 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
snd_pcm_sw_params_alloca(&pxmSwParams);
snd_pcm_sw_params_current(pcm->handle, pxmSwParams);
+ // available_min is the minimum number of frames available to read,
+ // when the call to poll returns. Assume this is the same for playback (not checked that)
+
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",
@@ -251,9 +268,11 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
goto OnErrorExit;
};
+ snd_pcm_sw_params_get_avail_min(pxmSwParams, &pcm->avail_min);
+ printf("GET AVAILABLE MIN: %ld\n", n);
+
int start_delay = 0;
- int stop_delay = 0;
- snd_pcm_uframes_t start_threshold, stop_threshold;
+ snd_pcm_uframes_t start_threshold;
/* round up to closest transfer boundary */
n = buffer_size;
@@ -264,32 +283,23 @@ PUBLIC int AlsaPcmConf(SoftMixerT *mixer, AlsaPcmCtlT *pcm, int mode) {
if (start_threshold < 1)
start_threshold = 1;
- if (start_threshold > n)
- start_threshold = n;
+ if (start_threshold > n/2)
+ start_threshold = n/2;
- AFB_ApiInfo(mixer->api, "Set start threshold to %ld", start_threshold);
+ printf("CALCULATED START THRESHOLD: %ld\n", 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));
- goto OnErrorExit;
- }
-
- if (stop_delay <= 0)
- stop_threshold = buffer_size + (size_t)((double)rate * stop_delay / 1000000);
- else
- stop_threshold = (size_t)((double)rate * stop_delay / 1000000);
+ if (mode == SND_PCM_STREAM_PLAYBACK) {
+ start_threshold = 1;
+ }
- AFB_ApiInfo(mixer->api, "Set stop threshold to %ld", stop_threshold);
- error = snd_pcm_sw_params_set_stop_threshold(pcm->handle, pxmSwParams, stop_threshold);
- if (error < 0) {
- AFB_ApiError(mixer->api,
- "%s: mixer=%s cardid=%s failed set stop_threshold, error=%s",
- __func__, mixer->uid, pcm->cid.cardid, snd_strerror(error));
- goto OnErrorExit;
- }
+ AFB_ApiInfo(mixer->api, "%s: Set start threshold to %ld", 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));
+ goto OnErrorExit;
+ }
// push software params into PCM
if ((error = snd_pcm_sw_params(pcm->handle, pxmSwParams)) < 0) {
@@ -311,11 +321,12 @@ OnErrorExit:
STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandle) {
char string[32];
- snd_pcm_sframes_t availIn, availOut, availInBuf;
- int err;
-
+ snd_pcm_sframes_t availIn;
snd_pcm_t * pcmIn = pcmCopyHandle->pcmIn->handle;
- snd_pcm_t * pcmOut= pcmCopyHandle->pcmOut->handle;
+ alsa_ringbuf_t * rbuf = pcmCopyHandle->rbuf;
+ snd_pcm_uframes_t bufSize = alsa_ringbuf_buffer_size(rbuf);
+
+ int err;
// PCM has was closed
if ((pfd->revents & POLLHUP) != 0) {
@@ -325,152 +336,109 @@ STATIC int AlsaPcmReadCB( struct pollfd * pfd, AlsaPcmCopyHandleT * pcmCopyHandl
goto ExitOnSuccess;
}
- // ignore any non input events
+ // ignore any non input events. This is not supposed to happen ever
if ((pfd->revents & EPOLLIN) == 0) {
goto ExitOnSuccess;
}
- // do we have waiting frame
+ // do we have waiting frames ?
availIn = snd_pcm_avail_update(pcmIn);
if (availIn <= 0) {
if (availIn == -EPIPE) {
- int ret = xrun(pcmIn);
- printf("XXX read EPIPE (recov=%d)\n", ret);
+ int ret = xrun(pcmIn, (int)availIn);
+ printf("XXX read EPIPE (recov=%d) !\n", ret);
+
+ // For some (undocumented...) reason, a start is mandatory.
+ snd_pcm_start(pcmIn);
}
goto ExitOnSuccess;
}
- availInBuf = buf_avail(pcmCopyHandle);
+ pthread_mutex_lock(&pcmCopyHandle->mutex);
+ snd_pcm_sframes_t availInBuf = alsa_ringbuf_frames_free(rbuf);
+ pthread_mutex_unlock(&pcmCopyHandle->mutex);
/* we get too much data, take what we can now,
* hopefully we will have more luck next time */
- if (availIn > availInBuf)
+ if (availIn > availInBuf) {
+ printf("INCOMING BUFFER TOO SMALL !\n");
availIn = availInBuf;
+ }
+
+ while (true) {
- snd_pcm_sframes_t totalRead = 0;
- snd_pcm_sframes_t totalWrite = 0;
+ pthread_mutex_lock(&pcmCopyHandle->mutex);
+ snd_pcm_sframes_t r = alsa_ringbuf_frames_free(rbuf);
+
+ if (r <= 0) {
+ pthread_mutex_unlock(&pcmCopyHandle->mutex);
+ // Wakeup the reader, in case it would be sleeping,
+ // that lets it an opportunity to pop.
+ sem_post(&pcmCopyHandle->sem);
+ break;
+ }
- while (availIn > 0) {
- snd_pcm_sframes_t r = buf_avail(pcmCopyHandle);
- if (r + pcmCopyHandle->buf_pos > pcmCopyHandle->buf_size)
- r = pcmCopyHandle->buf_size - pcmCopyHandle->buf_pos;
- if (r > availIn)
+ if (r < availIn)
r = availIn;
- r = snd_pcm_readi(pcmIn,
- pcmCopyHandle->buf +
- pcmCopyHandle->buf_pos *
- pcmCopyHandle->frame_size, r);
- if (r == 0)
- goto ExitOnSuccess;
+
+ char buf[r*pcmCopyHandle->frame_size];
+ pthread_mutex_unlock(&pcmCopyHandle->mutex);
+
+ r = snd_pcm_readi(pcmIn, buf, r);
+ if (r == 0) {
+ break;
+ }
if (r < 0) {
if (r == -EPIPE) {
- printf("read EPIPE (%d), recov %d\n", ++pcmCopyHandle->read_err_count, xrun(pcmIn));
+ err = xrun(pcmIn, (int)r);
+ printf("read EPIPE (%d), recov %d\n", ++pcmCopyHandle->read_err_count, err);
goto ExitOnSuccess;
} else if (r == -ESTRPIPE) {
printf("read ESTRPIPE\n");
- if ((err = suspend(pcmIn)) < 0)
+ if ((err = suspend(pcmIn, (int)r)) < 0)
goto ExitOnSuccess;
r = 0;
} else {
- goto ExitOnSuccess;;
+ goto ExitOnSuccess;
}
}
-
- totalRead += r;
-
- pcmCopyHandle->buf_count += r;
- pcmCopyHandle->buf_pos += r;
- pcmCopyHandle->buf_pos %= pcmCopyHandle->buf_size;
- availIn -= r;
- }
-
- // do we have space to push frame
- availOut = snd_pcm_avail_update(pcmOut);
- if (availOut < 0) {
- if (availOut == -EPIPE) {
- printf("write update EPIPE\n");
- xrun(pcmOut);
- goto ExitOnSuccess;
+ pthread_mutex_lock(&pcmCopyHandle->mutex);
+ alsa_ringbuf_frames_push(rbuf, buf, r);
+ snd_pcm_uframes_t used = alsa_ringbuf_frames_used(rbuf);
+ pthread_mutex_unlock(&pcmCopyHandle->mutex);
+
+ // Wait for having the buffer full enough before waking up the playback
+ // else it will starve immediately.
+ if (used > 0.8 * (double)bufSize) {
+ sem_post(&pcmCopyHandle->sem);
}
- if (availOut == -ESTRPIPE) {
- printf("write update ESTRPIPE\n");
- suspend(pcmOut);
- goto ExitOnSuccess;
- }
- }
-
- if (availOut == 0) {
- goto ExitOnSuccess;
- }
- while (pcmCopyHandle->buf_count > 0) {
- // In/Out frames transfer through buffer copy
- snd_pcm_sframes_t r = pcmCopyHandle->buf_count;
-
- if (r + pcmCopyHandle->buf_pos > pcmCopyHandle->buf_size)
- r = pcmCopyHandle->buf_size - pcmCopyHandle->buf_pos;
- if (r > availOut)
- r = availOut;
-
- r = snd_pcm_writei(
- pcmOut,
- pcmCopyHandle->buf +
- pcmCopyHandle->buf_pos *
- pcmCopyHandle->frame_size, r);
- if (r <= 0) {
- if (r == -EPIPE) {
- printf("XXX write EPIPE (%d), recov %d\n", ++pcmCopyHandle->write_err_count , xrun(pcmOut));
+ availIn -= r;
- continue;
- } else if (r == -ESTRPIPE) {
- printf("XXX write ESTRPIPE\n");
- goto ExitOnSuccess;
- }
- printf("Unhandled error %s\n", strerror(errno));
- goto ExitOnSuccess;
+ // completed, we have read everything
+ if (availIn <= 0) {
+ break;
}
- totalWrite += r;
-
- pcmCopyHandle->buf_count -= r;
- pcmCopyHandle->buf_pos += r;
- pcmCopyHandle->buf_pos %= pcmCopyHandle->buf_size;
-
- /* We evaluate the available space on device again,
- * because it may have grown since the measured the value
- * 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(pcmOut);
-
- if (newAvailOut == 0) {
- snd_pcm_wait(pcmOut, 5);
- } else if (newAvailOut > 0) {
- availOut = newAvailOut;
- }
}
- return 0;
-
- // Cannot handle error in callback
ExitOnSuccess:
return 0;
}
-static int xrun( snd_pcm_t * pcm)
+static int xrun( snd_pcm_t * pcm, int error)
{
int err;
- if ((err = snd_pcm_prepare(pcm)) < 0) {
+ if ((err = snd_pcm_recover(pcm, error, 1)) < 0) {
return err;
}
-
return 0;
}
-static int suspend( snd_pcm_t * pcm)
+static int suspend( snd_pcm_t * pcm, int error)
{
int err;
@@ -478,107 +446,50 @@ static int suspend( snd_pcm_t * pcm)
usleep(1);
}
if (err < 0)
- return xrun(pcm);
+ return xrun(pcm, error);
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;
+static void readSuspend(AlsaPcmCopyHandleT * pcmCopyHandle) {
- int pcmInCount = snd_pcm_poll_descriptors_count(pcmCopyHandle->pcmIn->handle);
- pcmInFds = malloc(sizeof (*pcmInFds) * pcmInCount);
+ // will be deaf
+ pcmCopyHandle->saveFd = pcmCopyHandle->pollFds[0].fd;
+ pcmCopyHandle->pollFds[0].fd = -1;
- 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;
+ AFB_ApiNotice(pcmCopyHandle->api, "capture muted");
+}
- res = 0;
+static void readResume(AlsaPcmCopyHandleT * pcmCopyHandle) {
-OnErrorExit:
- return res;
+ // undeaf it
+ pcmCopyHandle->pollFds[0].fd = pcmCopyHandle->saveFd;
+ AFB_ApiNotice(pcmCopyHandle->api, "capture unmuted");
}
-static void *LoopInThread(void *handle) {
+
+static void *readThreadEntry(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, mute %d",
+ "%s :%s/%d Started, muted=%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;
- }
+ struct pollfd * mutePfd = &pcmCopyHandle->pollFds[1];
+ mutePfd->events = POLLIN | POLLHUP;
- bool muted = false;
+ bool muted = pcmCopyHandle->pcmIn->mute;
+
+ if (muted)
+ readSuspend(pcmCopyHandle);
/* loop until end */
for (;;) {
- 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;
-
- muted = false;
- }
-
- int err = poll(pcmCopyHandle->pollFds, pcmCopyHandle->pcmInCount, LOOP_TIMEOUT_MSEC);
+ int err = poll(pcmCopyHandle->pollFds, 2, LOOP_TIMEOUT_MSEC);
if (err < 0) {
AFB_ApiError(pcmCopyHandle->api, "%s: poll err %s", __func__, strerror(errno));
continue;
@@ -590,23 +501,117 @@ static void *LoopInThread(void *handle) {
continue;
}
- for (int ix=0;ix<pcmCopyHandle->pcmInCount; ix++)
- AlsaPcmReadCB(&pcmCopyHandle->pollFds[ix], pcmCopyHandle);
+ // 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;
+
+ if (mute == muted)
+ continue;
+
+ muted = mute;
+
+ if (muted) {
+ readSuspend(pcmCopyHandle);
+ } else {
+ readResume(pcmCopyHandle);
+ }
+ continue;
}
-OnErrorExit:
- pthread_exit(0);
+ AlsaPcmReadCB(&pcmCopyHandle->pollFds[0], pcmCopyHandle);
+ }
+
+ pthread_exit(0);
+ return NULL;
}
-static inline snd_pcm_uframes_t time_to_frames(unsigned int rate,
- unsigned long long time)
-{
- return (time * rate) / 1000000ULL;
+
+static void *writeThreadEntry(void *handle) {
+ AlsaPcmCopyHandleT *pcmCopyHandle = (AlsaPcmCopyHandleT*) handle;
+ snd_pcm_t * pcmOut = pcmCopyHandle->pcmOut->handle;
+
+ alsa_ringbuf_t * rbuf = pcmCopyHandle->rbuf;
+
+ const snd_pcm_sframes_t threshold = 1000;
+
+ for (;;) {
+
+ sem_wait(&pcmCopyHandle->sem);
+
+ while (true) {
+ snd_pcm_sframes_t r;
+ snd_pcm_sframes_t availOut = snd_pcm_avail(pcmOut);
+
+ if (availOut < 0) {
+ if (availOut == -EPIPE) {
+ printf("write update EPIPE\n");
+ xrun(pcmOut, (int)availOut);
+ continue;
+ }
+ if (availOut == -ESTRPIPE) {
+ printf("write update ESTRPIPE\n");
+ suspend(pcmOut, (int)availOut);
+ continue;
+ }
+ }
+
+ // no space for output
+ if (availOut <= threshold) {
+ usleep(500);
+ continue;
+ }
+
+ pthread_mutex_lock(&pcmCopyHandle->mutex);
+ r = alsa_ringbuf_frames_used(rbuf);
+ if (r <= 0) {
+ pthread_mutex_unlock(&pcmCopyHandle->mutex);
+ break; // will wait again
+ }
+
+ if (r > availOut)
+ r = availOut;
+
+ char buf[r*pcmCopyHandle->frame_size];
+ alsa_ringbuf_frames_pop(rbuf, buf, r);
+ pthread_mutex_unlock(&pcmCopyHandle->mutex);
+
+ r = snd_pcm_writei( pcmOut, buf, r);
+ if (r <= 0) {
+ if (r == -EPIPE) {
+ int err = xrun(pcmOut, (int)r);
+ printf("XXX write EPIPE (%d), recov %d\n", ++pcmCopyHandle->write_err_count , err);
+
+ continue;
+ } else if (r == -ESTRPIPE) {
+ printf("XXX write ESTRPIPE\n");
+ break;
+ }
+ printf("Unhandled error %s\n", strerror(errno));
+ break;
+ }
+
+ }
+
+ }
+
+ pthread_exit(0);
+ return NULL;
+}
+
+
+PUBLIC int AlsaPcmCopyMuteSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn, bool mute) {
+ ssize_t ret = write(pcmIn->muteFd, &mute, sizeof(mute));
+ (void) ret;
+ return 0;
}
+
PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts) {
char string[32];
- struct pollfd *pcmInFds;
int error;
// Fulup need to check https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___direct.html
@@ -674,23 +679,18 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
cHandle->api = mixer->api;
cHandle->channels = opts->channels;
- cHandle->latency_reqtime = 10000;
- cHandle->latency = time_to_frames(opts->rate, cHandle->latency_reqtime);
- AFB_ApiInfo(mixer->api, "%s: Latency = %ld", __func__, cHandle->latency);
-
cHandle->frame_size = (snd_pcm_format_physical_width(opts->format) / 8) * opts->channels;
AFB_ApiInfo(mixer->api, "%s: Frame size is %zu", __func__, cHandle->frame_size);
- size_t size = cHandle->latency * 2;
- cHandle->buf = calloc(1, size*cHandle->frame_size);
- cHandle->buf_count = 0;
- cHandle->buf_pos = 0;
- cHandle->buf_size = size;
+ snd_pcm_uframes_t nbFrames = 2 * opts->rate; // Exactly 2 second of buffer
+
+ cHandle->rbuf = alsa_ringbuf_new(nbFrames, cHandle->frame_size);
+
cHandle->read_err_count = 0;
cHandle->write_err_count = 0;
- AFB_ApiInfo(mixer->api, "%s Copy buf size is %zu", __func__, size);
+ AFB_ApiInfo(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);
@@ -701,34 +701,93 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
goto OnErrorExit;
};
- pcmInFds = malloc(sizeof (*pcmInFds) * pcmInCount);
- if ((error = snd_pcm_poll_descriptors(pcmIn->handle, pcmInFds, pcmInCount)) < 0) {
+ 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) );
+ goto OnErrorExit;
+ }
+
+ struct pollfd pcmInFds[1];
+ if ((error = snd_pcm_poll_descriptors(pcmIn->handle, pcmInFds, 1)) < 0) {
AFB_ApiError(mixer->api,
"%s: Fail pcmIn=%s get pollfds error=%s",
__func__, ALSA_PCM_UID(pcmOut->handle, string), snd_strerror(error));
goto OnErrorExit;
};
- cHandle->pollFds = pcmInFds;
- cHandle->pcmInCount = pcmInCount;
+ // create the mute pipe
+ int pFd[2];
+ error = pipe(pFd);
+ 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;
+
+ pcmIn->muteFd = pFd[1];
+
+ cHandle->pollFds[0] = pcmInFds[0];
+ cHandle->pollFds[1] = pipePoll;
+
+ error = sem_init(&cHandle->sem, 0 , 0);
+ if (error < 0) {
+ AFB_ApiError(mixer->api,
+ "%s Fail initialize loop semaphore pcmIn=%s err=%d",
+ __func__, ALSA_PCM_UID(pcmIn->handle, string), error);
+ goto OnErrorExit;
+ }
+
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
+
+ error = pthread_mutex_init(&cHandle->mutex, &attr);
+ if (error < 0) {
+ AFB_ApiError(mixer->api,
+ "%s Fail initialize loop mutex pcmIn=%s err=%d",
+ __func__, ALSA_PCM_UID(pcmIn->handle, string), error);
+ }
- // start a thread with a mainloop to monitor Audio-Agent
- if ((error = pthread_create(&cHandle->thread, NULL, &LoopInThread, cHandle)) < 0) {
+ /// start a thread for writing
+ if ((error = pthread_create(&cHandle->wthread, NULL, &readThreadEntry, cHandle)) < 0) {
AFB_ApiError(mixer->api,
"%s Fail create waiting thread pcmIn=%s err=%d",
__func__, ALSA_PCM_UID(pcmIn->handle, string), error);
goto OnErrorExit;
}
-
+
+ // start a thread for reading
+ if ((error = pthread_create(&cHandle->rthread, NULL, &writeThreadEntry, cHandle)) < 0) {
+ AFB_ApiError(mixer->api,
+ "%s Fail create waiting thread pcmIn=%s err=%d",
+ __func__, ALSA_PCM_UID(pcmIn->handle, string), error);
+ goto OnErrorExit;
+ }
+
// request a higher priority for each audio stream thread
struct sched_param params;
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
- error= pthread_setschedparam(cHandle->thread, SCHED_FIFO, &params);
+
+ error= pthread_setschedparam(cHandle->rthread, SCHED_FIFO, &params);
+ if (error) {
+ AFB_ApiWarning(mixer->api,
+ "%s: Failed to increase stream read thread priority pcmIn=%s err=%s",
+ __func__, ALSA_PCM_UID(pcmIn->handle, string), strerror(error));
+ }
+
+ error= pthread_setschedparam(cHandle->wthread, SCHED_FIFO, &params);
if (error) {
AFB_ApiWarning(mixer->api,
- "%s: Failed to increase stream thread priority pcmIn=%s err=%s",
+ "%s: Failed to increase stream write thread priority pcmIn=%s err=%s",
__func__, ALSA_PCM_UID(pcmIn->handle, string), strerror(error));
}
+
return 0;
OnErrorExit:
diff --git a/plugins/alsa/alsa-softmixer.h b/plugins/alsa/alsa-softmixer.h
index 8a8ad40..96df8f3 100644
--- a/plugins/alsa/alsa-softmixer.h
+++ b/plugins/alsa/alsa-softmixer.h
@@ -29,10 +29,12 @@
#include <alsa/asoundlib.h>
#include <stdbool.h>
#include <systemd/sd-event.h>
+#include <semaphore.h>
#include "ctl-plugin.h"
#include "wrap-json.h"
+#include "alsa-ringbuf.h"
#ifndef PUBLIC
#define PUBLIC
@@ -100,11 +102,14 @@ typedef struct {
typedef struct {
int ccount;
bool mute;
+ int muteFd;
AlsaDevInfoT cid;
snd_pcm_t *handle;
AlsaPcmHwInfoT *params;
void * mixer;
+
+ snd_pcm_uframes_t avail_min;
} AlsaPcmCtlT;
typedef struct {
@@ -115,24 +120,27 @@ typedef struct {
size_t frame_size;
snd_pcm_uframes_t latency; /* final latency in frames */
- unsigned int latency_reqtime; /* in us */
// IO Job
- void * buf;
- snd_pcm_uframes_t buf_count; /* filled samples */
- snd_pcm_uframes_t buf_pos; /* begin of data */
- snd_pcm_uframes_t buf_size; /* buffer size in frames */
+ alsa_ringbuf_t * rbuf;
+
uint32_t write_err_count;
uint32_t read_err_count;
unsigned int channels;
sd_event *sdLoop;
- pthread_t thread;
+
+ pthread_t rthread;
+ pthread_t wthread;
+
int tid;
char* info;
- struct pollfd * pollFds;
- int pcmInCount;
+ struct pollfd pollFds[2];
+
+ sem_t sem;
+ pthread_mutex_t mutex;
+ int saveFd;
} AlsaPcmCopyHandleT;
@@ -287,6 +295,7 @@ PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *stream, AlsaPcmCtlT
// alsa-plug-*.c _snd_pcm_PLUGIN_open_ see macro ALSA_PLUG_PROTO(plugin)
PUBLIC int AlsaPcmCopy(SoftMixerT *mixer, AlsaStreamAudioT *streamAudio, AlsaPcmCtlT *pcmIn, AlsaPcmCtlT *pcmOut, AlsaPcmHwInfoT * opts);
+PUBLIC int AlsaPcmCopyMuteSignal(SoftMixerT *mixer, AlsaPcmCtlT *pcmIn, bool mute);
PUBLIC AlsaPcmCtlT* AlsaCreateSoftvol(SoftMixerT *mixer, AlsaStreamAudioT *stream, char *slaveid, AlsaSndCtlT *sndcard, char* ctlName, int max, int open);
PUBLIC AlsaPcmCtlT* AlsaCreateRoute(SoftMixerT *mixer, AlsaSndZoneT *zone, int open);
PUBLIC AlsaPcmCtlT* AlsaCreateRate(SoftMixerT *mixer, const char* pcmName, AlsaPcmCtlT *pcmSlave, AlsaPcmHwInfoT *params, int open);