/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include <pthread.h> #include <sys/param.h> #include <syslog.h> #include "byte_buffer.h" #include "cras_audio_area.h" #include "cras_config.h" #include "cras_iodev.h" #include "cras_iodev_list.h" #include "cras_types.h" #include "cras_util.h" #include "sfh.h" #include "utlist.h" #define LOOPBACK_BUFFER_SIZE 8192 static const char *loopdev_names[LOOPBACK_NUM_TYPES] = { "Post Mix Pre DSP Loopback", "Post DSP Loopback", }; static size_t loopback_supported_rates[] = { 48000, 0 }; static size_t loopback_supported_channel_counts[] = { 2, 0 }; static snd_pcm_format_t loopback_supported_formats[] = { SND_PCM_FORMAT_S16_LE, 0 }; /* loopack iodev. Keep state of a loopback device. * sample_buffer - Pointer to sample buffer. */ struct loopback_iodev { struct cras_iodev base; enum CRAS_LOOPBACK_TYPE loopback_type; struct timespec last_filled; struct byte_buffer *sample_buffer; }; static int sample_hook(const uint8_t *frames, unsigned int nframes, const struct cras_audio_format *fmt, void *cb_data) { struct loopback_iodev *loopdev = (struct loopback_iodev *)cb_data; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(fmt); unsigned int frames_to_copy, bytes_to_copy; struct cras_iodev *edev = cras_iodev_list_get_first_enabled_iodev( CRAS_STREAM_OUTPUT); /* If there's no active streams, the logic in frames_queued will fill * zeros for loopback capture, do not accept zeros for draining device. */ if (!edev || !edev->streams) return 0; frames_to_copy = MIN(buf_writable_bytes(sbuf) / frame_bytes, nframes); if (!frames_to_copy) return 0; bytes_to_copy = frames_to_copy * frame_bytes; memcpy(buf_write_pointer(sbuf), frames, bytes_to_copy); buf_increment_write(sbuf, bytes_to_copy); clock_gettime(CLOCK_MONOTONIC_RAW, &loopdev->last_filled); return frames_to_copy; } static void register_loopback_hook(enum CRAS_LOOPBACK_TYPE loopback_type, struct cras_iodev *iodev, loopback_hook_t hook, void *cb_data) { if (!iodev) { syslog(LOG_ERR, "Failed to register loopback hook."); return; } if (loopback_type == LOOPBACK_POST_MIX_PRE_DSP) cras_iodev_register_pre_dsp_hook(iodev, hook, cb_data); else if (loopback_type == LOOPBACK_POST_DSP) cras_iodev_register_post_dsp_hook(iodev, hook, cb_data); } static void device_enabled_hook(struct cras_iodev *iodev, int enabled, void *cb_data) { struct loopback_iodev *loopdev = (struct loopback_iodev *)cb_data; struct cras_iodev *edev; if (iodev->direction != CRAS_STREAM_OUTPUT) return; if (!enabled) { /* Unregister loopback hook from disabled iodev. */ register_loopback_hook(loopdev->loopback_type, iodev, NULL, NULL); } else { /* Register loopback hook onto first enabled iodev. */ edev = cras_iodev_list_get_first_enabled_iodev( CRAS_STREAM_OUTPUT); register_loopback_hook(loopdev->loopback_type, edev, sample_hook, cb_data); } } /* * iodev callbacks. */ static int frames_queued(const struct cras_iodev *iodev, struct timespec *hw_tstamp) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(iodev->format); struct cras_iodev *edev = cras_iodev_list_get_first_enabled_iodev( CRAS_STREAM_OUTPUT); if (!edev || !edev->streams) { unsigned int frames_since_last, frames_to_fill, bytes_to_fill; frames_since_last = cras_frames_since_time( &loopdev->last_filled, iodev->format->frame_rate); frames_to_fill = MIN(buf_writable_bytes(sbuf) / frame_bytes, frames_since_last); if (frames_to_fill > 0) { bytes_to_fill = frames_to_fill * frame_bytes; memset(buf_write_pointer(sbuf), 0, bytes_to_fill); buf_increment_write(sbuf, bytes_to_fill); clock_gettime(CLOCK_MONOTONIC_RAW, &loopdev->last_filled); } } *hw_tstamp = loopdev->last_filled; return buf_queued_bytes(sbuf) / frame_bytes; } static int delay_frames(const struct cras_iodev *iodev) { struct timespec tstamp; return frames_queued(iodev, &tstamp); } static int close_record_dev(struct cras_iodev *iodev) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; struct cras_iodev *edev; cras_iodev_free_format(iodev); cras_iodev_free_audio_area(iodev); buf_reset(sbuf); edev = cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT); register_loopback_hook(loopdev->loopback_type, edev, NULL, NULL); cras_iodev_list_set_device_enabled_callback(NULL, NULL); return 0; } static int open_record_dev(struct cras_iodev *iodev) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct cras_iodev *edev; cras_iodev_init_audio_area(iodev, iodev->format->num_channels); clock_gettime(CLOCK_MONOTONIC_RAW, &loopdev->last_filled); edev = cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT); register_loopback_hook(loopdev->loopback_type, edev, sample_hook, (void *)iodev); cras_iodev_list_set_device_enabled_callback(device_enabled_hook, (void *)iodev); return 0; } static int get_record_buffer(struct cras_iodev *iodev, struct cras_audio_area **area, unsigned *frames) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(iodev->format); unsigned int avail_frames = buf_readable_bytes(sbuf) / frame_bytes; *frames = MIN(avail_frames, *frames); iodev->area->frames = *frames; cras_audio_area_config_buf_pointers(iodev->area, iodev->format, buf_read_pointer(sbuf)); *area = iodev->area; return 0; } static int put_record_buffer(struct cras_iodev *iodev, unsigned nframes) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int frame_bytes = cras_get_format_bytes(iodev->format); buf_increment_read(sbuf, nframes * frame_bytes); return 0; } static int flush_record_buffer(struct cras_iodev *iodev) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; unsigned int queued_bytes = buf_queued_bytes(sbuf); buf_increment_read(sbuf, queued_bytes); return 0; } static void update_active_node(struct cras_iodev *iodev, unsigned node_idx, unsigned dev_enabled) { } static struct cras_iodev *create_loopback_iodev(enum CRAS_LOOPBACK_TYPE type) { struct loopback_iodev *loopback_iodev; struct cras_iodev *iodev; loopback_iodev = calloc(1, sizeof(*loopback_iodev)); if (loopback_iodev == NULL) return NULL; loopback_iodev->sample_buffer = byte_buffer_create(1024*16*4); if (loopback_iodev->sample_buffer == NULL) { free(loopback_iodev); return NULL; } loopback_iodev->loopback_type = type; iodev = &loopback_iodev->base; iodev->direction = CRAS_STREAM_INPUT; snprintf(iodev->info.name, ARRAY_SIZE(iodev->info.name), "%s", loopdev_names[type]); iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0'; iodev->info.stable_id = SuperFastHash(iodev->info.name, strlen(iodev->info.name), strlen(iodev->info.name)); iodev->info.stable_id_new = iodev->info.stable_id; iodev->supported_rates = loopback_supported_rates; iodev->supported_channel_counts = loopback_supported_channel_counts; iodev->supported_formats = loopback_supported_formats; iodev->buffer_size = LOOPBACK_BUFFER_SIZE; iodev->frames_queued = frames_queued; iodev->delay_frames = delay_frames; iodev->update_active_node = update_active_node; iodev->open_dev = open_record_dev; iodev->close_dev = close_record_dev; iodev->get_buffer = get_record_buffer; iodev->put_buffer = put_record_buffer; iodev->flush_buffer = flush_record_buffer; return iodev; } /* * Exported Interface. */ struct cras_iodev *loopback_iodev_create(enum CRAS_LOOPBACK_TYPE type) { struct cras_iodev *iodev; struct cras_ionode *node; enum CRAS_NODE_TYPE node_type; switch (type) { case LOOPBACK_POST_MIX_PRE_DSP: node_type = CRAS_NODE_TYPE_POST_MIX_PRE_DSP; break; case LOOPBACK_POST_DSP: node_type = CRAS_NODE_TYPE_POST_DSP; break; default: return NULL; } iodev = create_loopback_iodev(type); if (iodev == NULL) return NULL; /* Create a dummy ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = node_type; node->plugged = 1; node->volume = 100; node->stable_id = iodev->info.stable_id; node->stable_id_new = iodev->info.stable_id_new; node->software_volume_needed = 0; node->max_software_gain = 0; strcpy(node->name, loopdev_names[type]); cras_iodev_add_node(iodev, node); cras_iodev_set_active_node(iodev, node); cras_iodev_list_add_input(iodev); return iodev; } void loopback_iodev_destroy(struct cras_iodev *iodev) { struct loopback_iodev *loopdev = (struct loopback_iodev *)iodev; struct byte_buffer *sbuf = loopdev->sample_buffer; cras_iodev_list_rm_input(iodev); byte_buffer_destroy(sbuf); free(loopdev); }