/* Copyright (c) 2012 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 "cras_audio_area.h" #include "cras_config.h" #include "cras_iodev.h" #include "cras_iodev_list.h" #include "cras_rstream.h" #include "cras_types.h" #include "utlist.h" #define EMPTY_BUFFER_SIZE (16 * 1024) #define EMPTY_FRAME_SIZE 4 #define EMPTY_FRAMES (EMPTY_BUFFER_SIZE / EMPTY_FRAME_SIZE) static size_t empty_supported_rates[] = { 44100, 48000, 0 }; static size_t empty_supported_channel_counts[] = { 1, 2, 0 }; static snd_pcm_format_t empty_supported_formats[] = { SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S24_3LE, 0 }; struct empty_iodev { struct cras_iodev base; int open; uint8_t *audio_buffer; unsigned int buffer_level; struct timespec last_buffer_access; }; /* Current level of the audio buffer. This is made up based on what has been * read/written and how long it has been since then. Simulates audio hardware * running at the given sample rate. */ static unsigned int current_level(const struct cras_iodev *iodev) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; unsigned int frames, frames_since_last; frames = empty_iodev->buffer_level; frames_since_last = cras_frames_since_time( &empty_iodev->last_buffer_access, iodev->format->frame_rate); if (iodev->direction == CRAS_STREAM_INPUT) return (frames + frames_since_last) % EMPTY_FRAMES; /* output */ if (frames <= frames_since_last) return 0; return frames - frames_since_last; } /* * iodev callbacks. */ static int frames_queued(const struct cras_iodev *iodev, struct timespec *tstamp) { clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); return current_level(iodev); } static int delay_frames(const struct cras_iodev *iodev) { return 0; } static int close_dev(struct cras_iodev *iodev) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; empty_iodev->open = 0; free(empty_iodev->audio_buffer); empty_iodev->audio_buffer = NULL; cras_iodev_free_audio_area(iodev); return 0; } static int open_dev(struct cras_iodev *iodev) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; if (iodev->format == NULL) return -EINVAL; cras_iodev_init_audio_area(iodev, iodev->format->num_channels); empty_iodev->open = 1; empty_iodev->audio_buffer = calloc(1, EMPTY_BUFFER_SIZE); empty_iodev->buffer_level = 0; clock_gettime(CLOCK_MONOTONIC_RAW, &empty_iodev->last_buffer_access); return 0; } static int get_buffer(struct cras_iodev *iodev, struct cras_audio_area **area, unsigned *frames) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; unsigned int avail, current; if (iodev->direction == CRAS_STREAM_OUTPUT) { avail = EMPTY_FRAMES - current_level(iodev); *frames = MIN(*frames, avail); } else { current = current_level(iodev); *frames = MIN(*frames, current); } iodev->area->frames = *frames; cras_audio_area_config_buf_pointers(iodev->area, iodev->format, empty_iodev->audio_buffer); *area = iodev->area; return 0; } static int put_buffer(struct cras_iodev *iodev, unsigned frames) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; empty_iodev->buffer_level = current_level(iodev); clock_gettime(CLOCK_MONOTONIC_RAW, &empty_iodev->last_buffer_access); if (iodev->direction == CRAS_STREAM_OUTPUT) { empty_iodev->buffer_level += frames; empty_iodev->buffer_level %= EMPTY_FRAMES; } else { /* Input */ if (empty_iodev->buffer_level > frames) empty_iodev->buffer_level -= frames; else empty_iodev->buffer_level = 0; } return 0; } static int flush_buffer(struct cras_iodev *iodev) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; empty_iodev->buffer_level = current_level(iodev); if (iodev->direction == CRAS_STREAM_INPUT) { empty_iodev->buffer_level = 0; clock_gettime(CLOCK_MONOTONIC_RAW, &empty_iodev->last_buffer_access); } return 0; } static void update_active_node(struct cras_iodev *iodev, unsigned node_idx, unsigned dev_enabled) { } /* * Exported Interface. */ struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction) { struct empty_iodev *empty_iodev; struct cras_iodev *iodev; struct cras_ionode *node; if (direction != CRAS_STREAM_INPUT && direction != CRAS_STREAM_OUTPUT) return NULL; empty_iodev = calloc(1, sizeof(*empty_iodev)); if (empty_iodev == NULL) return NULL; iodev = &empty_iodev->base; iodev->direction = direction; iodev->supported_rates = empty_supported_rates; iodev->supported_channel_counts = empty_supported_channel_counts; iodev->supported_formats = empty_supported_formats; iodev->buffer_size = EMPTY_FRAMES; iodev->open_dev = open_dev; iodev->close_dev = close_dev; iodev->frames_queued = frames_queued; iodev->delay_frames = delay_frames; iodev->get_buffer = get_buffer; iodev->put_buffer = put_buffer; iodev->flush_buffer = flush_buffer; iodev->update_active_node = update_active_node; iodev->no_stream = cras_iodev_default_no_stream_playback; /* Create a dummy ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = CRAS_NODE_TYPE_UNKNOWN; node->volume = 100; strcpy(node->name, "(default)"); cras_iodev_add_node(iodev, node); cras_iodev_set_active_node(iodev, node); /* Finally add it to the appropriate iodev list. */ if (direction == CRAS_STREAM_INPUT) { snprintf(iodev->info.name, ARRAY_SIZE(iodev->info.name), "Silent record device."); iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0'; iodev->info.idx = SILENT_RECORD_DEVICE; } else { snprintf(iodev->info.name, ARRAY_SIZE(iodev->info.name), "Silent playback device."); iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0'; iodev->info.idx = SILENT_PLAYBACK_DEVICE; } return iodev; } void empty_iodev_destroy(struct cras_iodev *iodev) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; if (iodev->direction == CRAS_STREAM_INPUT) cras_iodev_list_rm_input(iodev); else cras_iodev_list_rm_output(iodev); free(iodev->active_node); cras_iodev_free_resources(iodev); free(empty_iodev); }