+++ /dev/null
-/*
-* Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
-*
-* 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 <dlog.h>
-#include <speex/speex.h>
-#include <speex/speex_preprocess.h>
-#include <speex/speex_echo.h>
-#include "algo_speex.h"
-
-//#define __DEBUG__
-
-#ifdef __DEBUG__
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-static int fdrec, fdref, fdout;
-#endif
-
-static int speex_init(int nframes, int rate, int channels);
-static int speex_process(unsigned char *rec, unsigned char *ref, unsigned char *out);
-static int speex_deinit();
-
-static struct ec_speex {
- SpeexEchoState *echo_state;
- SpeexPreprocessState *preprocess;
- int nframes;
- int filter_frames;
- int channels;
- int rate;
-} sp;
-
-static ec_operation_t op = {
- .init = speex_init,
- .process = speex_process,
- .deinit = speex_deinit,
-};
-
-ec_operation_t *get_speex_instance()
-{
- return &op;
-}
-
-static int speex_init(int nframes, int rate, int channels)
-{
- spx_int32_t value = 1;
- sp.nframes = nframes;
- sp.filter_frames = nframes; /* TODO:It takes much time when 10 times of nframe */
- sp.channels = channels;
- sp.rate = rate;
-
- LOGI("nframes(%d), filter_frames(%d) rate(%d), channels(%d)",
- sp.nframes, sp.filter_frames, sp.rate, sp.channels);
-
- sp.echo_state = speex_echo_state_init_mc(sp.nframes, sp.filter_frames,
- sp.channels, sp.channels);
- if (!sp.echo_state) {
- LOGE("speex_echo_state_init_mc failed\n");
- return -1;
- }
-
- sp.preprocess = speex_preprocess_state_init(sp.nframes, sp.rate);
- if (!sp.preprocess) {
- LOGE("speex_echo_state_init_mc failed\n");
- return -1;
- }
-
- if (speex_echo_ctl(sp.echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &sp.rate)) {
- LOGE("speex_echo_ctl SET_SAMPLING_RATE failed\n");
- return -1;
- }
-
- if (speex_preprocess_ctl(sp.preprocess, SPEEX_PREPROCESS_SET_AGC, &value)) {
- LOGE("speex_echo_ctl SPEEX_PREPROCESS_SET_AGC failed\n");
- return -1;
- }
-
- if (speex_preprocess_ctl(sp.preprocess, SPEEX_PREPROCESS_SET_DENOISE, &value)) {
- LOGE("speex_echo_ctl SPEEX_PREPROCESS_SET_DENOISE failed\n");
- return -1;
- }
-
- if (speex_preprocess_ctl(sp.preprocess, SPEEX_PREPROCESS_SET_DEREVERB, &value)) {
- LOGE("speex_echo_ctl SPEEX_PREPROCESS_SET_DEREVERB failed\n");
- return -1;
- }
-
- if (speex_preprocess_ctl(sp.preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, sp.echo_state)) {
- LOGE("speex_echo_ctl SET_ECHO_STATE failed\n");
- return -1;
- }
-
-#ifdef __DEBUG__
- unlink("/tmp/rec.raw");
- unlink("/tmp/ref.raw");
- unlink("/tmp/out.raw");
-
- fdrec = open("/tmp/rec.raw", O_RDWR | O_CREAT | O_TRUNC, 777);
- fdref = open("/tmp/ref.raw", O_RDWR | O_CREAT | O_TRUNC, 777);
- fdout = open("/tmp/out.raw", O_RDWR | O_CREAT | O_TRUNC, 777);
-#endif
-
- return 0;
-}
-
-static int speex_process(unsigned char *rec, unsigned char *ref, unsigned char *out)
-{
-#ifdef __DEBUG__
- struct timeval before,after;
- write(fdrec, rec, sp.nframes * 4);
- write(fdref, ref, sp.nframes * 4);
- gettimeofday(&before,NULL);
-#endif
-
- speex_echo_cancellation(sp.echo_state,
- (const spx_int16_t *)rec,
- (const spx_int16_t *)ref,
- (spx_int16_t *)out);
- speex_preprocess_run(sp.preprocess, (spx_int16_t *)out);
-
- rec += sp.nframes * sp.channels;
- ref += sp.nframes * sp.channels;
- out += sp.nframes * sp.channels;
-
- speex_echo_cancellation(sp.echo_state,
- (const spx_int16_t *)rec,
- (const spx_int16_t *)ref,
- (spx_int16_t *)out);
- speex_preprocess_run(sp.preprocess, (spx_int16_t *)out);
-
-#ifdef __DEBUG__
- gettimeofday(&after,NULL);
- LOGE("It takes time(%ld)",
- 1000*(after.tv_sec-before.tv_sec) + (after.tv_usec-before.tv_usec)/1000);
- write(fdout, out, sp.nframes * 4);
-#endif
-
- return 0;
-}
-
-static int speex_deinit()
-{
- if (sp.echo_state)
- speex_echo_state_destroy(sp.echo_state);
- if (sp.preprocess)
- speex_preprocess_state_destroy(sp.preprocess);
-
-#ifdef __DEBUG__
- if (fdrec)
- close(fdrec);
- if (fdref)
- close(fdref);
- if (fdout)
- close(fdout);
-#endif
-
- return 0;
-}
-
+++ /dev/null
-/*
-* Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
-*
-* 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 <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <assert.h>
-#include <pthread.h>
-#include <alsa/asoundlib.h>
-#include <dlog.h>
-
-#include "loopback.h"
-#include "static_queue.h"
-
-#define FIXME_FORMAT_SIZE 2
-#define CTL_LOOPBACK_DEVICE "hw:Loopback"
-#define PCM_MASTER_ACTIVE "PCM Master Active"
-#define PCM_SLAVE_ACTIVE "PCM Slave Active"
-
-enum {
- CAPTURE = 0,
- PLAYBACK,
- DEV_MAX
-};
-
-enum {
- POLL_WAKEUP = 0,
- POLL_CTRL,
- POLL_CAPTURE,
- POLL_PLAYBACK,
- POLL_MAX
-};
-
-enum {
- STATE_STOPPED,
- STATE_RUNNING,
-};
-
-struct loopback_dev {
- snd_pcm_t *handle;
- char *name;
- int mode;
- int rate;
- int channels;
- int format;
- unsigned int buffer_size;
- unsigned int buffer_time;
- unsigned int period_size;
- unsigned int buffer_frame;
- unsigned int period_time;
-};
-
-struct loopback {
- char *name;
- struct loopback_dev *dev[DEV_MAX];
- struct pollfd pfds[POLL_MAX];
- int poll_cnt;
- int wakeup[2];
- pthread_t t;
- volatile int state;
- volatile bool quit;
-
- char *empty_buf;
- int ec_frame;
- int rate;
- int channels;
-
- /* loopback ctl */
- snd_ctl_t *ctl;
- int ctl_id;
- int ctl_subid;
- snd_ctl_elem_value_t *elm_active;
-
- /* related to reference */
- ec_operation_t *op;
-
- sq *q;
-
- /* reference queue */
- loopback_t *master;
- sq *refq; /* recording consumption */
- sq *master_refq; /* playback feeding */
-
- int logcnt;
-};
-
-static int __open_loopback_controls(loopback_t *loopback, const char *dev, const char *elm);
-static int __check_ctl_active_event(loopback_t *loopback, int *status);
-static int __handle_revents(loopback_t *loopback, int mode, unsigned short revents);
-static void *__loop_thread(void *userdata);
-
-static void __close_dev(loopback_t *loopback);
-static int __open_dev(loopback_t *loopback, int index, struct pollfd *pfd);
-static int __xrun_recovery(loopback_dev_t *dev, int err);
-static int __print_buffer(loopback_t *loopback);
-
-loopback_dev_t *loopback_alloc_device(const char *name, int mode, int rate,
- int channels, int format, int buffer_frame)
-{
- loopback_dev_t *dev = (loopback_dev_t *)calloc(1, sizeof(struct loopback_dev));
- if (!dev) {
- LOGE("Failed to alloc memory");
- return NULL;
- }
-
- dev->name = strdup(name);
- dev->mode = mode;
- dev->rate = rate;
- dev->channels = channels;
- dev->format = format;
- dev->buffer_frame = buffer_frame;
-
- return dev;
-}
-
-int loopback_free_device(loopback_dev_t *dev)
-{
- if (!dev)
- return -1;
-
- if (dev->name) {
- free(dev->name);
- dev->name = NULL;
- }
-
- return 0;
-}
-
-static int __open_loopback_controls(loopback_t *loopback, const char *dev, const char *elm)
-{
- int k, err;
- char ctl_name[64];
- const char *elm_name = elm;
-
- loopback->ctl_id = dev[12] - '0';
- loopback->ctl_subid = dev[14] - '0';
-
- for (k = 0; *dev != ',' && *dev != '\0'; dev++)
- ctl_name[k++] = *dev;
- ctl_name[k] = '\0';
-
- err = snd_ctl_open(&loopback->ctl, ctl_name, SND_CTL_NONBLOCK);
- if (err < 0) {
- LOGE("Failed to open capture control. name(%s)", ctl_name);
- goto fail;
- }
-
- err = snd_ctl_subscribe_events(loopback->ctl, 1);
- if (err < 0) {
- LOGE("Failed to subscribe ctrl event");
- goto fail;
- }
-
- if (snd_ctl_elem_value_malloc(&loopback->elm_active) < 0) {
- LOGE("Failed to alloc elem");
- goto fail;
- }
-
- snd_ctl_elem_value_set_interface(loopback->elm_active, SND_CTL_ELEM_IFACE_PCM);
- snd_ctl_elem_value_set_device(loopback->elm_active, loopback->ctl_id);
- snd_ctl_elem_value_set_subdevice(loopback->elm_active, loopback->ctl_subid);
- snd_ctl_elem_value_set_name(loopback->elm_active, elm_name);
-
- LOGD("loopback ctrl opened. name(%s) id(%d) subid(%d) elm_name(%s)",
- ctl_name, loopback->ctl_id, loopback->ctl_subid, elm_name);
-
- return 0;
-
-fail:
- if (loopback->ctl) {
- snd_ctl_close(loopback->ctl);
- loopback->ctl = NULL;
- }
- if (loopback->elm_active) {
- snd_ctl_elem_value_free(loopback->elm_active);
- loopback->elm_active = NULL;
- }
-
- return -1;
-}
-
-loopback_t *loopback_create(const char *name,
- loopback_dev_t *capture,
- loopback_dev_t *playback,
- int process_frame, int nbuffer)
-{
- const char *elm_name;
- char *dev_loopback;
- loopback_t *loopback;
- int bytes, len;
-
- if (capture->rate != playback->rate ||
- capture->channels != playback->channels ||
- capture->format != playback->format) {
- /* TODO: support resample or remap */
- LOGE("capture and playback info is not matched."
- "rate(%d:%d), channels(%d:%d), format(%d:%d)",
- capture->rate, playback->rate,
- capture->channels, playback->channels,
- capture->format, playback->format);
- return NULL;
- }
-
- len = strlen(CTL_LOOPBACK_DEVICE);
- if (!strncmp(capture->name, CTL_LOOPBACK_DEVICE, len))
- dev_loopback = capture->name;
- else if (!strncmp(playback->name, CTL_LOOPBACK_DEVICE, len))
- dev_loopback = playback->name;
- else {
- LOGE("not support");
- return NULL;
- }
-
- loopback = (loopback_t *)calloc(1, sizeof(struct loopback));
- if (!loopback) {
- LOGE("Failed to alloc memory");
- return NULL;
- }
- loopback->name = name ? strdup(name) : strdup("noname");
- loopback->dev[CAPTURE] = capture;
- loopback->dev[PLAYBACK] = playback;
- loopback->state = STATE_STOPPED;
-
- /* TODO: Support resample */
- loopback->channels = capture->channels;
- loopback->rate = capture->rate;
-
- elm_name = dev_loopback == capture->name ? PCM_SLAVE_ACTIVE : PCM_MASTER_ACTIVE;
- if (__open_loopback_controls(loopback, dev_loopback, elm_name)) {
- LOGE("Failed to open loopback control. name(%s)", elm_name);
- goto exit;
- }
-
- loopback->ec_frame = process_frame;
- bytes = loopback->ec_frame * capture->channels * FIXME_FORMAT_SIZE;
-
- // TEMP: padding for speex
- bytes += bytes >> 1;
-
- loopback->q = create_static_queue(bytes, nbuffer);
- if (!loopback->q) {
- LOGE("Failed to create sq");
- goto exit;
- }
- LOGI("created static-queue bytes(%d), n(%d)", bytes, nbuffer);
-
- loopback->empty_buf = (char *)calloc(1, bytes);
- if (!loopback->empty_buf) {
- LOGE("Failed to alloc empty_buf\n");
- goto exit;
- }
-
- if (pipe(loopback->wakeup)) {
- LOGE("Failed to create pipe");
- goto exit;
- }
-
- return loopback;
-
-exit:
- if (loopback->ctl)
- snd_ctl_close(loopback->ctl);
- if (loopback->elm_active)
- snd_ctl_elem_value_free(loopback->elm_active);
- if (loopback->q)
- destory_static_queue(loopback->q);
- if (loopback->empty_buf)
- free(loopback->empty_buf);
- if (loopback)
- free(loopback);
-
- return NULL;
-}
-
-int loopback_start(loopback_t *loopback)
-{
- if (!loopback->q) {
- LOGE("loopback is not ready\n");
- return -1;
- }
-
- loopback->pfds[POLL_WAKEUP].fd = loopback->wakeup[0];
- loopback->pfds[POLL_WAKEUP].events = POLLIN;
-
- /* poll fds */
- if (snd_ctl_poll_descriptors(loopback->ctl, &(loopback->pfds[POLL_CTRL]), 1) <= 0) {
- LOGE("cannot get ctrl descriptor\n");
- return -1;
- }
-
- loopback->poll_cnt = POLL_CTRL + 1;
-
- if (loopback->op) {
- if (loopback->op->init(loopback->ec_frame, loopback->rate, loopback->channels)) {
- LOGE("ec init failed\n");
- return -1;
- }
- }
-
- if (pthread_create(&loopback->t, NULL, __loop_thread, (void *)loopback) < 0) {
- LOGE("failed to create thread\nn");
- return -1;
- }
-
- return 0;
-}
-
-int loopback_stop(loopback_t *loopback)
-{
- ssize_t n;
- int status;
- char dummy = 'c';
-
- if (!loopback)
- return -1;
-
- loopback->quit = 1;
- n = write(loopback->wakeup[1], &dummy, 1);
- if (n <= 0) {
- LOGE("Failed to wake up thread");
- return -1;
- }
-
- if (pthread_join(loopback->t, (void **)&status)) {
- LOGE("thread join failed\nn");
- return -1;
- }
-
- loopback->quit = 0;
-
- return 0;
-}
-
-int loopback_destroy(loopback_t *loopback)
-{
- if (!loopback)
- return -1;
-
- if (loopback->state == STATE_RUNNING) {
- LOGE("Failed to destroy loopback. state running");
- return -1;
- }
-
- if (loopback->q)
- destory_static_queue(loopback->q);
-
- if (loopback->refq)
- destory_static_queue(loopback->refq);
-
- if (loopback->name) {
- free(loopback->name);
- loopback->name = NULL;
- }
-
- if (loopback->empty_buf) {
- free(loopback->empty_buf);
- loopback->empty_buf = NULL;
- }
-
- if (loopback->elm_active) {
- snd_ctl_elem_value_free(loopback->elm_active);
- loopback->elm_active = NULL;
- }
-
- if (loopback->ctl) {
- snd_ctl_close(loopback->ctl);
- loopback->ctl = NULL;
- }
-
- __close_dev(loopback);
-
- free(loopback);
-
- return 0;
-}
-
-int loopback_bind_reference(loopback_t *loopback, loopback_t *ref, ec_operation_t *op)
-{
- if (loopback->state == STATE_RUNNING) {
- LOGE("Failed to bind. state(%d)", loopback->state);
- return -1;
- }
-
- if (loopback->refq) {
- LOGE("Failed to bind. already bound.");
- return -1;
- }
-
- loopback->op = op;
- loopback->refq = create_static_queue(0, 0);
-
- ref->master = loopback;
- ref->master_refq = loopback->refq;
-
- LOGI("bind loopback master(%s), refernece(%s)", loopback->name, ref->name);
-
- return 0;
-}
-
-static int __handle_revents(struct loopback *loopback, int mode, unsigned short revents)
-{
- int err;
- struct loopback_dev *dev = loopback->dev[mode];
- snd_pcm_sframes_t frames = loopback->ec_frame;
- snd_pcm_sframes_t avail;
-
- if (revents & POLLIN) {
- unsigned char *recbuf;
- sqbuffer *rec;
-
- avail = snd_pcm_avail_update(dev->handle);
- if (avail < 0) {
- LOGW("%s: Can't get avail: %s", dev->name, snd_strerror(avail));
-
- if (__xrun_recovery(dev, avail)) {
- LOGE("%s: AEC will be stopped.", dev->name);
- return 0;
- }
-
- } else if (avail < frames) {
- LOGW("%s: not enough avail(%ld) < frames(%ld)\n", dev->name, avail, frames);
- return 0;
- }
-
- rec = sq_get_node_lock(loopback->q);
- if (!rec) {
- LOGE("%s: Failed to get valid sq node. maybe overflow", dev->name);
- return 0;
- }
-
- recbuf = sq_get_buffer(rec);
- if ((err = snd_pcm_readi(dev->handle, recbuf, frames)) < 0) {
- LOGE("%s: Failed to read: %s(%d)", dev->name, snd_strerror(err), err);
-
- sq_put_node_lock(rec);
- __xrun_recovery(dev, err);
-
- return 0;
- }
-
- sq_enqueue_node(loopback->q, rec);
-
- } else if (revents & POLLOUT) {
- unsigned char *playbuf;
- sqbuffer *play;
-
- avail = snd_pcm_avail_update(dev->handle);
- if (avail < 0) {
- LOGW("%s: Can't get avail. Try to recover: %s", dev->name, snd_strerror(avail));
-
- if (__xrun_recovery(dev, avail))
- LOGE("%s: Failed to recover. AEC will be stopped.", dev->name);
-
- return 0;
-
- } else if (avail < frames) {
- LOGW("%s: not enough avail(%ld) < frames(%ld)\n", dev->name, avail, frames);
- return 0;
- }
-
- play = sq_dequeue_node(loopback->q);
- if (!play) {
- if ((err = snd_pcm_writei(dev->handle, loopback->empty_buf, frames)) < 0)
- LOGE("%s: Failed to write. frame(%ld): %s(%d)",
- dev->name, frames, snd_strerror(err), err);
- return 0;
- }
-
- playbuf = sq_get_buffer(play);
-
- /* only recording thread */
- if (loopback->refq && !sq_is_empty_lock(loopback->refq)) {
- sqbuffer *ref = sq_dequeue_node_lock(loopback->refq);
- sqbuffer *out = sq_get_node_lock(loopback->q);
- unsigned char *refbuf = sq_get_buffer(ref);
- unsigned char *outbuf = sq_get_buffer(out);
-
- loopback->op->process(playbuf, refbuf, outbuf);
-
- sq_put_node_lock(play);
- sq_put_node_lock(ref);
-
- play = out;
- playbuf = outbuf;
- }
-
-
- if ((err = snd_pcm_writei(dev->handle, playbuf, frames)) < 0) {
- LOGE("%s: Failed to write: %s", dev->name, snd_strerror(err));
- if (__xrun_recovery(dev, err))
- sq_put_node_lock(play);
- return 0;
- }
-
- /* only reference thread */
- if (loopback->master && loopback->master->state == STATE_RUNNING) {
- sq_enqueue_node_lock(loopback->master_refq, play);
- return 0;
- }
-
- sq_put_node_lock(play);
- } else
- LOGD("unknown event %x\n", revents);
-
- return 0;
-}
-
-static int __check_ctl_active_event(loopback_t *loopback, int *status)
-{
- int err;
- unsigned int event_mask;
- snd_ctl_event_t *ev;
- snd_ctl_elem_id_t *id1, *id2;
-
- snd_ctl_event_alloca(&ev);
- snd_ctl_elem_id_alloca(&id1);
- snd_ctl_elem_id_alloca(&id2);
-
- while ((err = snd_ctl_read(loopback->ctl, ev)) != 0 && err != -EAGAIN);
-
- snd_ctl_elem_value_get_id(loopback->elm_active, id1);
- snd_ctl_event_elem_get_id(ev, id2);
-
- event_mask = snd_ctl_event_elem_get_mask(ev);
-
- if (event_mask == SND_CTL_EVENT_MASK_REMOVE || !(event_mask & SND_CTL_EVENT_MASK_VALUE))
- return 0;
-
- /*
- LOGD("intf(%d:%d) id(%d:%d) subid(%d:%d) elem(%s:%s) index(%d:%d)\n",
- snd_ctl_elem_id_get_interface(id1), snd_ctl_elem_id_get_interface(id2),
- snd_ctl_elem_id_get_device(id1), snd_ctl_elem_id_get_device(id2),
- snd_ctl_elem_id_get_subdevice(id1), snd_ctl_elem_id_get_subdevice(id2),
- snd_ctl_elem_id_get_name(id1), snd_ctl_elem_id_get_name(id2),
- snd_ctl_elem_id_get_index(id1), snd_ctl_elem_id_get_index(id2));
- */
-
- if (snd_ctl_elem_id_get_interface(id1) != snd_ctl_elem_id_get_interface(id2))
- return 0;
-
- if (snd_ctl_elem_id_get_device(id1) != snd_ctl_elem_id_get_device(id2))
- return 0;
-
- if (snd_ctl_elem_id_get_subdevice(id1) != snd_ctl_elem_id_get_subdevice(id2))
- return 0;
-
- if (strcmp(snd_ctl_elem_id_get_name(id1), snd_ctl_elem_id_get_name(id2)) != 0)
- return 0;
-
- if (snd_ctl_elem_id_get_index(id1) != snd_ctl_elem_id_get_index(id2))
- return 0;
-
- err = snd_ctl_elem_read(loopback->ctl, loopback->elm_active);
- if (err < 0) {
- LOGE("Failed to read ctl elem\n");
- return 0;
- }
-
- err = snd_ctl_elem_value_get_boolean(loopback->elm_active, 0);
-
- *status = err;
-
- LOGE("dev:subid:elm(%d:%d:%s) is changed to (%d)\n",
- snd_ctl_elem_id_get_device(id2), snd_ctl_elem_id_get_subdevice(id2),
- snd_ctl_elem_id_get_name(id2), *status);
-
- return 1;
-}
-
-static void *__loop_thread(void *userdata)
-{
- int err;
- unsigned short revents;
- struct loopback *loopback = (struct loopback *)userdata;
- struct loopback_dev **dev = loopback->dev;
-
- LOGI("start thread. name(%s), capture(%s) playback(%s), frame(%d), poll_cnt(%d)\n",
- loopback->name, dev[CAPTURE]->name, dev[PLAYBACK]->name,
- loopback->ec_frame, loopback->poll_cnt);
-
- while (1) {
- if (poll(loopback->pfds, loopback->poll_cnt, -1) < 0) {
- LOGE("poll err %s\n", strerror(errno));
- continue;
- }
-
- if (loopback->quit) {
- if (loopback->op)
- loopback->op->deinit();
-
- __close_dev(loopback);
- loopback->state = STATE_STOPPED;
-
- LOGE("thread(%s) exit\n", loopback->name);
-
- pthread_exit(0);
- }
-
- revents = 0;
- if (!snd_ctl_poll_descriptors_revents(loopback->ctl, &loopback->pfds[POLL_CTRL], 1, &revents)) {
- int onoff;
-
- if (__check_ctl_active_event(loopback, &onoff)) {
- if (onoff) { /* Turn on */
- if (__open_dev(loopback, PLAYBACK, loopback->pfds+POLL_PLAYBACK)) {
- LOGE("failed to open playback dev\n");
- pthread_exit(0);
- }
-
- if (__open_dev(loopback, CAPTURE, loopback->pfds+POLL_CAPTURE)) {
- LOGE("failed to open capture dev\n");
- pthread_exit(0);
- }
-
- if ((err = snd_pcm_start(dev[CAPTURE]->handle)) < 0)
- LOGE("capture dev start failed. %s\n", snd_strerror(err));
-
- loopback->state = STATE_RUNNING;
-
- } else { /* Turn off */
- loopback->state = STATE_STOPPED;
-
- __close_dev(loopback);
-
- sq_flush(loopback->q);
- sq_flush_lock(loopback->refq);
-
- LOGI("sleep...\n");
-
- continue;
- }
- }
- }
-
- if (loopback->state != STATE_RUNNING)
- continue;
-
- revents = 0;
- if (!snd_pcm_poll_descriptors_revents(dev[CAPTURE]->handle,
- &loopback->pfds[POLL_CAPTURE], 1, &revents)) {
- if (revents != 0)
- __handle_revents(loopback, CAPTURE, revents);
- }
- revents = 0;
- if (!snd_pcm_poll_descriptors_revents(dev[PLAYBACK]->handle,
- &loopback->pfds[POLL_PLAYBACK], 1, &revents)) {
- if (revents != 0)
- __handle_revents(loopback, PLAYBACK, revents);
- }
-
- if (loopback->logcnt++ > 100) {
- loopback->logcnt = 0;
- __print_buffer(loopback);
- }
- }
-}
-
-static void __close_dev(loopback_t *loopback)
-{
- if (loopback->dev[PLAYBACK]->handle) {
- snd_pcm_close(loopback->dev[PLAYBACK]->handle);
- loopback->dev[PLAYBACK]->handle = NULL;
- }
- loopback->poll_cnt--;
-
- if (loopback->dev[CAPTURE]->handle) {
- snd_pcm_close(loopback->dev[CAPTURE]->handle);
- loopback->dev[CAPTURE]->handle = NULL;
- }
- loopback->poll_cnt--;
-}
-
-static int __xrun_recovery(struct loopback_dev *dev, int err)
-{
- snd_pcm_state_t state;
-
- state = snd_pcm_state(dev->handle);
-
- if (state != SND_PCM_STATE_XRUN) {
- LOGW("Invalid state(%d)", state);
- return -1;
- }
-
- LOGE("%s: Try to recover. state(%d)\n", dev->name, state);
-
- if (err == -EPIPE) { /* underrun or overrun */
- err = snd_pcm_prepare(dev->handle);
- if (err < 0) {
- LOGE("XRUN: Failed to prepare pcm\n");
- return -1;
- }
- } else if (err == -ESTRPIPE) { /* suspended */
- while ((err = snd_pcm_resume(dev->handle)) == -EAGAIN)
- usleep(50000);
-
- if (err) {
- LOGE("XRUN: Failed to resume pcm\n");
- return -1;
- }
-
- err = snd_pcm_prepare(dev->handle);
- if (err < 0) {
- LOGE("XRUN: Failed to prepare pcm\n");
- return -1;
- }
- } else {
- LOGE("XRUN: Unknown error\n");
- return -1;
- }
-
- // TODO: add device close open
- LOGE("XRUN: recovered");
-
- return 0;
-}
-
-static int __open_dev(loopback_t *loopback, int index, struct pollfd *pfd)
-{
- int err;
- int flags = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_FORMAT;
- unsigned int buffer_time;
-
- const char *name = loopback->dev[index]->name;
- int mode = loopback->dev[index]->mode;
- unsigned int rate = loopback->dev[index]->rate;
- unsigned int _rate = rate;
- int channels = loopback->dev[index]->channels;
- //int format = dev->format;
- snd_pcm_uframes_t buffer_frame = loopback->dev[index]->buffer_frame;
- struct loopback_dev *dev = loopback->dev[index];
-
- snd_pcm_stream_t _mode = (mode == CAPTURE) ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
- snd_pcm_sw_params_t *sw_params = NULL;
- snd_pcm_hw_params_t *hw_params = NULL;
- snd_pcm_uframes_t buffer_size, period_size;
-
- if ((err = snd_pcm_open(&dev->handle, name, _mode, flags)) < 0) {
- LOGE("%s: cannot open audio device (%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
- LOGE("%s: cannot allocate hardware parameter structure(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_any(dev->handle, hw_params)) < 0) {
- LOGE("%s: cannot initialize hardware parameter structure(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_set_rate_resample(dev->handle, hw_params, 0)) < 0) {
- LOGE("%s: cannot set sample SAMPLE_RATE(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_set_access(dev->handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
- LOGE("%s: cannot set access type(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_set_format(dev->handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
- LOGE("%s: cannot set sample format(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_set_channels(dev->handle, hw_params, channels)) < 0) {
- unsigned int ch;
-
- LOGE("%s: cannot set channel. try near. channels(%d). %s\n",
- name, channels, snd_strerror(err));
-
- if ((err = snd_pcm_hw_params_set_channels_near(dev->handle, hw_params, &ch)) < 0) {
- LOGE("%s: cannot set channel. %s\n", name, snd_strerror(err));
- goto fail;
- }
- dev->channels = ch;
- }
-
- if ((err = snd_pcm_hw_params_set_rate_near(dev->handle, hw_params, &_rate, NULL)) < 0) {
- LOGE("%s: cannot set sample SAMPLE_RATE(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if (_rate != rate) {
- LOGE("%s: rate doesn't matched rate(%d) _rate(%d)\n", name, rate, _rate);
- return -1;
- }
-
- if ((err = snd_pcm_hw_params_set_buffer_size_near(dev->handle, hw_params, &buffer_frame)) < 0) {
- LOGE("%s: snd_pcm_hw_params_set_buffer_size_near(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size)) < 0) {
- LOGE("%s: failed to get buffer size (%s)\n", name, snd_strerror(err));
- goto fail;
- }
- if ((err = snd_pcm_hw_params_get_buffer_time(hw_params, &buffer_time, NULL)) < 0) {
- LOGE("%s: failed to get buffer time (%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- unsigned int period_time = buffer_time / 4;
- if ((err = snd_pcm_hw_params_set_period_time_near(dev->handle, hw_params, &period_time, NULL))) {
- LOGE("%s: hw set period time near %d)\n", name, period_time);
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, NULL))) {
- LOGE("%s: hw set period time near %d)\n", name, period_time);
- goto fail;
- }
-
- if ((err = snd_pcm_hw_params(dev->handle, hw_params)) < 0) {
- LOGE("%s: cannot set parameters(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- /* sw params */
- if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
- LOGE("%s: cannot allocate software parameters structure(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- err = snd_pcm_sw_params_current(dev->handle, sw_params);
- if (err < 0) {
- LOGE("%s: cannot initialize software parameters structure(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- err = snd_pcm_sw_params_set_start_threshold(dev->handle, sw_params, period_size);
- if (err < 0) {
- LOGE("Unable to set start threshold mode for %s", snd_strerror(err));
- goto fail;
- }
-
- if ((err = snd_pcm_sw_params_set_avail_min(dev->handle, sw_params, period_size)) < 0) {
- LOGE("%s: cannot set minimum available count(%s)\n", name, snd_strerror(err));
- goto fail;
- }
-
- err = snd_pcm_sw_params(dev->handle, sw_params);
- if (err < 0) {
- LOGE("Unable to set sw params for %s", snd_strerror(err));
- goto fail;
- }
-
- dev->period_size = period_size;
- dev->period_time = period_time;
- dev->buffer_size = buffer_size;
- dev->buffer_time = buffer_time;
-
- if (pfd) {
- if (snd_pcm_poll_descriptors(dev->handle, pfd, 1) <= 0) {
- LOGE("cannot get capture descriptor\n");
- goto fail;
- }
- }
-
- snd_pcm_sw_params_free(sw_params);
- snd_pcm_hw_params_free(hw_params);
-
- loopback->poll_cnt++;
-
- LOGI("%s dev(%s) opened successfully. rate(%d), ch(%d), request buffer_frame(%d)\n",
- mode ? "playback" : "capture", name, dev->rate, dev->channels, dev->buffer_frame);
- LOGI("\tbuffer size : %ld frames, %d usec\n", buffer_size, buffer_time);
- LOGI("\tperiod size : %ld frames, %d usec\n", period_size, period_time);
- LOGI("\tthread_hold : %ld, avail_min %ld\n", period_size, period_size);
- LOGI("\tloopback process frame %d", loopback->ec_frame);
-
- return 0;
-
-fail:
- if (dev->handle)
- snd_pcm_close(dev->handle);
- if (hw_params)
- snd_pcm_hw_params_free(hw_params);
- if (sw_params)
- snd_pcm_sw_params_free(sw_params);
-
- return -1;
-}
-
-static int __print_buffer(loopback_t *loopback)
-{
- struct loopback_dev **dev = loopback->dev;
- snd_pcm_sframes_t c_avail, c_delay;
- snd_pcm_sframes_t p_avail, p_delay;
- float capture;
- float playback;
-
- snd_pcm_avail_delay(dev[CAPTURE]->handle, &c_avail, &c_delay);
- snd_pcm_avail_delay(dev[PLAYBACK]->handle, &p_avail, &p_delay);
-
- capture = (float)c_avail / (float)(dev[CAPTURE]->buffer_size) * 100;
- playback = (float)p_avail / (float)(dev[PLAYBACK]->buffer_size) * 100;
-
- LOGD("name(%s) capture avail(%.2f%%), playback avail(%.2f%%) buffer delay(%d)",
- loopback->name, capture, playback, sq_get_work_node_count(loopback->q));
-
- if (loopback->refq)
- LOGD("refq delay(%d)", sq_get_work_node_count(loopback->refq));
-
- return 0;
-}
-