diff options
Diffstat (limited to 'cras/src/alsa_plugin/pcm_cras.c')
-rw-r--r-- | cras/src/alsa_plugin/pcm_cras.c | 405 |
1 files changed, 0 insertions, 405 deletions
diff --git a/cras/src/alsa_plugin/pcm_cras.c b/cras/src/alsa_plugin/pcm_cras.c deleted file mode 100644 index 7bc960bc..00000000 --- a/cras/src/alsa_plugin/pcm_cras.c +++ /dev/null @@ -1,405 +0,0 @@ -/* 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 <alsa/asoundlib.h> -#include <alsa/pcm_external.h> -#include <cras_client.h> -#include <sys/socket.h> - -/* Holds configuration for the alsa plugin. - * io - ALSA ioplug object. - * fd - Wakes users with polled io. - * stream_playing - Indicates if the stream is playing/capturing. - * hw_ptr - Current read or write position. - * channels - Number of channels. - * stream_id - CRAS ID of the playing/capturing stream. - * bytes_per_frame - number of bytes in an audio frame. - * direction - input or output. - * areas - ALSA areas used to read from/write to. - * client - CRAS client object. - * capture_sample_index - The sample tracked for capture latency calculation. - * playback_sample_index - The sample tracked for playback latency calculation. - * capture_sample_time - The time when capture_sample_index was captured. - * playback_sample_time - The time when playback_sample_index was captured. - */ -struct snd_pcm_cras { - snd_pcm_ioplug_t io; - int fd; - int stream_playing; - unsigned int hw_ptr; - unsigned int channels; - cras_stream_id_t stream_id; - size_t bytes_per_frame; - enum CRAS_STREAM_DIRECTION direction; - snd_pcm_channel_area_t *areas; - struct cras_client *client; - int capture_sample_index; - int playback_sample_index; - struct timespec capture_sample_time; - struct timespec playback_sample_time; -}; - -/* Frees all resources allocated during use. */ -static void snd_pcm_cras_free(struct snd_pcm_cras *pcm_cras) -{ - if (pcm_cras == NULL) - return; - assert(!pcm_cras->stream_playing); - if (pcm_cras->fd >= 0) - close(pcm_cras->fd); - if (pcm_cras->io.poll_fd >= 0) - close(pcm_cras->io.poll_fd); - cras_client_destroy(pcm_cras->client); - free(pcm_cras->areas); - free(pcm_cras); -} - -/* Stops a playing or capturing CRAS plugin. */ -static int snd_pcm_cras_stop(snd_pcm_ioplug_t *io) -{ - struct snd_pcm_cras *pcm_cras = io->private_data; - - if (pcm_cras->stream_playing) { - cras_client_rm_stream(pcm_cras->client, pcm_cras->stream_id); - cras_client_stop(pcm_cras->client); - pcm_cras->stream_playing = 0; - } - return 0; -} - -/* Close a CRAS plugin opened with snd_pcm_cras_open. */ -static int snd_pcm_cras_close(snd_pcm_ioplug_t *io) -{ - struct snd_pcm_cras *pcm_cras = io->private_data; - - if (pcm_cras->stream_playing) - snd_pcm_cras_stop(io); - snd_pcm_cras_free(pcm_cras); - return 0; -} - -/* Poll callback used to wait for data ready (playback) or space available - * (capture). */ -static int snd_pcm_cras_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfds, - unsigned int nfds, unsigned short *revents) -{ - static char buf[1]; - int rc; - - if (pfds == NULL || nfds != 1 || revents == NULL) - return -EINVAL; - rc = read(pfds[0].fd, buf, 1); - if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN) { - fprintf(stderr, "%s read failed %d\n", __func__, errno); - return errno; - } - *revents = pfds[0].revents & ~(POLLIN | POLLOUT); - if (pfds[0].revents & POLLIN) - *revents |= (io->stream == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : - POLLIN; - return 0; -} - -/* Callback to return the location of the write (playback) or read (capture) - * pointer. */ -static snd_pcm_sframes_t snd_pcm_cras_pointer(snd_pcm_ioplug_t *io) -{ - struct snd_pcm_cras *pcm_cras = io->private_data; - return pcm_cras->hw_ptr; -} - -/* Main callback for processing audio. This is called by CRAS when more samples - * are needed (playback) or ready (capture). Copies bytes between ALSA and CRAS - * buffers. */ -static int pcm_cras_process_cb(struct cras_client *client, - cras_stream_id_t stream_id, - uint8_t *capture_samples, - uint8_t *playback_samples, unsigned int nframes, - const struct timespec *capture_ts, - const struct timespec *playback_ts, void *arg) -{ - snd_pcm_ioplug_t *io; - struct snd_pcm_cras *pcm_cras; - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t copied_frames; - char empty_byte; - size_t chan, frame_bytes, sample_bytes; - int rc; - uint8_t *samples; - const struct timespec *sample_time; - - samples = capture_samples ?: playback_samples; - sample_time = capture_ts ?: playback_ts; - - io = (snd_pcm_ioplug_t *)arg; - pcm_cras = (struct snd_pcm_cras *)io->private_data; - frame_bytes = pcm_cras->bytes_per_frame; - sample_bytes = snd_pcm_format_physical_width(io->format) / 8; - - if (io->stream == SND_PCM_STREAM_PLAYBACK) { - if (io->state != SND_PCM_STATE_RUNNING && - io->state != SND_PCM_STATE_DRAINING) { - memset(samples, 0, nframes * frame_bytes); - return nframes; - } - /* Only take one period of data at a time. */ - if (nframes > io->period_size) - nframes = io->period_size; - - /* Keep track of the first transmitted sample index and the time - * it will be played. */ - pcm_cras->playback_sample_index = io->hw_ptr; - pcm_cras->playback_sample_time = *sample_time; - } else { - /* Keep track of the first read sample index and the time it - * was captured. */ - pcm_cras->capture_sample_index = io->hw_ptr; - pcm_cras->capture_sample_time = *sample_time; - } - - /* CRAS always takes interleaved samples. */ - for (chan = 0; chan < io->channels; chan++) { - pcm_cras->areas[chan].addr = samples + chan * sample_bytes; - pcm_cras->areas[chan].first = 0; - pcm_cras->areas[chan].step = - snd_pcm_format_physical_width(io->format) * - io->channels; - } - - areas = snd_pcm_ioplug_mmap_areas(io); - - copied_frames = 0; - while (copied_frames < nframes) { - snd_pcm_uframes_t frames = nframes - copied_frames; - snd_pcm_uframes_t remain = io->buffer_size - pcm_cras->hw_ptr; - - if (frames > remain) - frames = remain; - - for (chan = 0; chan < io->channels; chan++) - if (io->stream == SND_PCM_STREAM_PLAYBACK) - snd_pcm_area_copy(&pcm_cras->areas[chan], - copied_frames, &areas[chan], - pcm_cras->hw_ptr, frames, - io->format); - else - snd_pcm_area_copy(&areas[chan], - pcm_cras->hw_ptr, - &pcm_cras->areas[chan], - copied_frames, frames, - io->format); - - pcm_cras->hw_ptr += frames; - pcm_cras->hw_ptr %= io->buffer_size; - copied_frames += frames; - } - - rc = write(pcm_cras->fd, &empty_byte, 1); /* Wake up polling clients. */ - if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN) - fprintf(stderr, "%s write failed %d\n", __func__, errno); - - return nframes; -} - -/* Callback from CRAS for stream errors. */ -static int pcm_cras_error_cb(struct cras_client *client, - cras_stream_id_t stream_id, int err, void *arg) -{ - fprintf(stderr, "Stream error %d\n", err); - return 0; -} - -/* ALSA calls this automatically when the stream enters the - * SND_PCM_STATE_PREPARED state. */ -static int snd_pcm_cras_prepare(snd_pcm_ioplug_t *io) -{ - struct snd_pcm_cras *pcm_cras = io->private_data; - - return cras_client_connect(pcm_cras->client); -} - -/* Called when an ALSA stream is started. */ -static int snd_pcm_cras_start(snd_pcm_ioplug_t *io) -{ - struct snd_pcm_cras *pcm_cras = io->private_data; - struct cras_stream_params *params; - struct cras_audio_format *audio_format; - int rc; - - audio_format = - cras_audio_format_create(io->format, io->rate, io->channels); - if (audio_format == NULL) - return -ENOMEM; - - params = cras_client_unified_params_create( - pcm_cras->direction, io->period_size, 0, 0, io, - pcm_cras_process_cb, pcm_cras_error_cb, audio_format); - if (params == NULL) { - rc = -ENOMEM; - goto error_out; - } - - cras_client_stream_params_set_client_type(params, CRAS_CLIENT_TYPE_PCM); - - rc = cras_client_run_thread(pcm_cras->client); - if (rc < 0) - goto error_out; - - pcm_cras->bytes_per_frame = - cras_client_format_bytes_per_frame(audio_format); - - rc = cras_client_add_stream(pcm_cras->client, &pcm_cras->stream_id, - params); - if (rc < 0) { - fprintf(stderr, "CRAS add failed\n"); - goto error_out; - } - pcm_cras->stream_playing = 1; - -error_out: - cras_audio_format_destroy(audio_format); - cras_client_stream_params_destroy(params); - return rc; -} - -static snd_pcm_ioplug_callback_t cras_pcm_callback = { - .close = snd_pcm_cras_close, - .start = snd_pcm_cras_start, - .stop = snd_pcm_cras_stop, - .pointer = snd_pcm_cras_pointer, - .prepare = snd_pcm_cras_prepare, - .poll_revents = snd_pcm_cras_poll_revents, -}; - -/* Set constraints for hw_params. This lists the handled formats, sample rates, - * access patters, and buffer/period sizes. These are enforce in - * snd_pcm_set_params(). */ -static int set_hw_constraints(struct snd_pcm_cras *pcm_cras) -{ - // clang-format off - static const unsigned int access_list[] = { - SND_PCM_ACCESS_MMAP_INTERLEAVED, - SND_PCM_ACCESS_MMAP_NONINTERLEAVED, - SND_PCM_ACCESS_RW_INTERLEAVED, - SND_PCM_ACCESS_RW_NONINTERLEAVED - }; - static const unsigned int format_list[] = { - SND_PCM_FORMAT_U8, - SND_PCM_FORMAT_S16_LE, - SND_PCM_FORMAT_S24_LE, - SND_PCM_FORMAT_S32_LE, - SND_PCM_FORMAT_S24_3LE, - }; - // clang-format on - int rc; - - rc = snd_pcm_ioplug_set_param_list(&pcm_cras->io, - SND_PCM_IOPLUG_HW_ACCESS, - ARRAY_SIZE(access_list), - access_list); - if (rc < 0) - return rc; - rc = snd_pcm_ioplug_set_param_list(&pcm_cras->io, - SND_PCM_IOPLUG_HW_FORMAT, - ARRAY_SIZE(format_list), - format_list); - if (rc < 0) - return rc; - rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io, - SND_PCM_IOPLUG_HW_CHANNELS, 1, - pcm_cras->channels); - if (rc < 0) - return rc; - rc = snd_pcm_ioplug_set_param_minmax( - &pcm_cras->io, SND_PCM_IOPLUG_HW_RATE, 8000, 48000); - if (rc < 0) - return rc; - rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io, - SND_PCM_IOPLUG_HW_BUFFER_BYTES, 64, - 2 * 1024 * 1024); - if (rc < 0) - return rc; - rc = snd_pcm_ioplug_set_param_minmax(&pcm_cras->io, - SND_PCM_IOPLUG_HW_PERIOD_BYTES, 64, - 2 * 1024 * 1024); - if (rc < 0) - return rc; - rc = snd_pcm_ioplug_set_param_minmax( - &pcm_cras->io, SND_PCM_IOPLUG_HW_PERIODS, 1, 2048); - return rc; -} - -/* Called by snd_pcm_open(). Creates a CRAS client and an ioplug plugin. */ -static int snd_pcm_cras_open(snd_pcm_t **pcmp, const char *name, - snd_pcm_stream_t stream, int mode) -{ - struct snd_pcm_cras *pcm_cras; - int rc; - int fd[2]; - - assert(pcmp); - pcm_cras = calloc(1, sizeof(*pcm_cras)); - if (!pcm_cras) - return -ENOMEM; - - pcm_cras->fd = -1; - pcm_cras->io.poll_fd = -1; - pcm_cras->channels = 2; - pcm_cras->direction = (stream == SND_PCM_STREAM_PLAYBACK) ? - CRAS_STREAM_OUTPUT : - CRAS_STREAM_INPUT; - - rc = cras_client_create(&pcm_cras->client); - if (rc != 0 || pcm_cras->client == NULL) { - fprintf(stderr, "Couldn't create CRAS client\n"); - free(pcm_cras); - return rc; - } - - pcm_cras->areas = - calloc(pcm_cras->channels, sizeof(snd_pcm_channel_area_t)); - if (pcm_cras->areas == NULL) { - snd_pcm_cras_free(pcm_cras); - return -ENOMEM; - } - - socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); - - cras_make_fd_nonblocking(fd[0]); - cras_make_fd_nonblocking(fd[1]); - - pcm_cras->fd = fd[0]; - - pcm_cras->io.version = SND_PCM_IOPLUG_VERSION; - pcm_cras->io.name = "ALSA to CRAS Plugin"; - pcm_cras->io.callback = &cras_pcm_callback; - pcm_cras->io.private_data = pcm_cras; - pcm_cras->io.poll_fd = fd[1]; - pcm_cras->io.poll_events = POLLIN; - pcm_cras->io.mmap_rw = 1; - - rc = snd_pcm_ioplug_create(&pcm_cras->io, name, stream, mode); - if (rc < 0) { - snd_pcm_cras_free(pcm_cras); - return rc; - } - - rc = set_hw_constraints(pcm_cras); - if (rc < 0) { - snd_pcm_ioplug_delete(&pcm_cras->io); - return rc; - } - - *pcmp = pcm_cras->io.pcm; - - return 0; -} - -SND_PCM_PLUGIN_DEFINE_FUNC(cras) -{ - return snd_pcm_cras_open(pcmp, name, stream, mode); -} - -SND_PCM_PLUGIN_SYMBOL(cras); |