/* 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 #include #include #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 (32 * 1024) #define MAX_EMPTY_FRAME_SIZE 8 #define EMPTY_FRAMES (EMPTY_BUFFER_SIZE / MAX_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; uint8_t *audio_buffer; uint64_t read_frames, written_frames; struct timespec dev_start_time; }; /* * Current level of the audio buffer. This is made up based on what has been * read/written and how long it has been since the start. 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; uint64_t frames_since_start, nframes; if (iodev->active_node->type == CRAS_NODE_TYPE_HOTWORD) return 0; frames_since_start = cras_frames_since_time( &empty_iodev->dev_start_time, iodev->format->frame_rate); if (iodev->direction == CRAS_STREAM_INPUT) { nframes = frames_since_start - empty_iodev->read_frames; return MIN(nframes, EMPTY_FRAMES); } /* output */ if (empty_iodev->written_frames <= frames_since_start) return 0; return empty_iodev->written_frames - frames_since_start; } /* * 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; free(empty_iodev->audio_buffer); empty_iodev->audio_buffer = NULL; cras_iodev_free_audio_area(iodev); return 0; } static int configure_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->audio_buffer = calloc(1, EMPTY_BUFFER_SIZE); empty_iodev->read_frames = 0; empty_iodev->written_frames = 0; clock_gettime(CLOCK_MONOTONIC_RAW, &empty_iodev->dev_start_time); 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; } /* * Returns -EPIPE if there are not enough frames or spaces in device buffer. * It matches other alsa-based devices. */ static int put_buffer(struct cras_iodev *iodev, unsigned frames) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; if (iodev->direction == CRAS_STREAM_INPUT) { if (current_level(iodev) < frames) return -EPIPE; empty_iodev->read_frames += frames; } else { if (EMPTY_FRAMES - current_level(iodev) < frames) return -EPIPE; empty_iodev->written_frames += frames; } return 0; } static int flush_buffer(struct cras_iodev *iodev) { struct empty_iodev *empty_iodev = (struct empty_iodev *)iodev; if (iodev->direction == CRAS_STREAM_INPUT) empty_iodev->read_frames = 0; else empty_iodev->written_frames = 0; clock_gettime(CLOCK_MONOTONIC_RAW, &empty_iodev->dev_start_time); 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, enum CRAS_NODE_TYPE node_type) { 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->configure_dev = configure_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 an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = node_type; node->volume = 100; node->ui_gain_scaler = 1.0f; 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) { if (node->type == CRAS_NODE_TYPE_HOTWORD) { snprintf(iodev->info.name, ARRAY_SIZE(iodev->info.name), "Silent hotword device."); iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = '\0'; iodev->info.idx = SILENT_HOTWORD_DEVICE; } else { 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; } /* * Record max supported channels into cras_iodev_info. * The value is the max of empty_supported_channel_counts. */ iodev->info.max_supported_channels = 2; 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); }