| /* Copyright 2016 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 <fcntl.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/param.h> |
| #include <unistd.h> |
| |
| #include "cras_client.h" |
| #include "cras_types.h" |
| #include "cras_util.h" |
| #include "cras_version.h" |
| |
| #define PLAYBACK_BUFFERED_TIME_IN_NS (5000000) |
| |
| #define BUF_SIZE 32768 |
| |
| static int keep_looping = 1; |
| static int pipefd[2]; |
| struct cras_audio_format *aud_format; |
| |
| static int terminate_stream_loop(void) |
| { |
| keep_looping = 0; |
| return write(pipefd[1], "1", 1); |
| } |
| |
| static size_t get_block_size(uint64_t buffer_time_in_ns, size_t rate) |
| { |
| static struct timespec t; |
| |
| t.tv_nsec = buffer_time_in_ns; |
| t.tv_sec = 0; |
| return (size_t)cras_time_to_frames(&t, rate); |
| } |
| |
| /* Run from callback thread. */ |
| static int got_samples(struct cras_client *client, cras_stream_id_t stream_id, |
| uint8_t *captured_samples, uint8_t *playback_samples, |
| unsigned int frames, |
| const struct timespec *captured_time, |
| const struct timespec *playback_time, void *user_arg) |
| { |
| int *fd = (int *)user_arg; |
| int ret; |
| int write_size; |
| int frame_bytes; |
| |
| frame_bytes = cras_client_format_bytes_per_frame(aud_format); |
| write_size = frames * frame_bytes; |
| ret = write(*fd, captured_samples, write_size); |
| if (ret != write_size) |
| printf("Error writing file\n"); |
| return frames; |
| } |
| |
| /* Run from callback thread. */ |
| static int put_samples(struct cras_client *client, cras_stream_id_t stream_id, |
| uint8_t *captured_samples, uint8_t *playback_samples, |
| unsigned int frames, |
| const struct timespec *captured_time, |
| const struct timespec *playback_time, void *user_arg) |
| { |
| uint32_t frame_bytes = cras_client_format_bytes_per_frame(aud_format); |
| int fd = *(int *)user_arg; |
| uint8_t buff[BUF_SIZE]; |
| int nread; |
| |
| nread = read(fd, buff, MIN(frames * frame_bytes, BUF_SIZE)); |
| if (nread <= 0) { |
| terminate_stream_loop(); |
| return nread; |
| } |
| |
| memcpy(playback_samples, buff, nread); |
| return nread / frame_bytes; |
| } |
| |
| static int stream_error(struct cras_client *client, cras_stream_id_t stream_id, |
| int err, void *arg) |
| { |
| printf("Stream error %d\n", err); |
| terminate_stream_loop(); |
| return 0; |
| } |
| |
| static int start_stream(struct cras_client *client, cras_stream_id_t *stream_id, |
| struct cras_stream_params *params, float stream_volume) |
| { |
| int rc; |
| |
| rc = cras_client_add_stream(client, stream_id, params); |
| if (rc < 0) { |
| fprintf(stderr, "adding a stream %d\n", rc); |
| return rc; |
| } |
| return cras_client_set_stream_volume(client, *stream_id, stream_volume); |
| } |
| |
| static int run_file_io_stream(struct cras_client *client, int fd, int loop_fd, |
| enum CRAS_STREAM_DIRECTION direction, |
| size_t block_size, size_t rate, |
| size_t num_channels) |
| { |
| struct cras_stream_params *params; |
| cras_stream_id_t stream_id = 0; |
| int stream_playing = 0; |
| int *pfd = malloc(sizeof(*pfd)); |
| *pfd = fd; |
| float volume_scaler = 1.0; |
| |
| if (pipe(pipefd) == -1) { |
| perror("failed to open pipe"); |
| return -errno; |
| } |
| aud_format = cras_audio_format_create(SND_PCM_FORMAT_S16_LE, rate, |
| num_channels); |
| if (aud_format == NULL) |
| return -ENOMEM; |
| |
| params = cras_client_unified_params_create(direction, block_size, 0, 0, |
| pfd, got_samples, |
| stream_error, aud_format); |
| if (params == NULL) |
| return -ENOMEM; |
| |
| cras_client_run_thread(client); |
| stream_playing = |
| start_stream(client, &stream_id, params, volume_scaler) == 0; |
| if (!stream_playing) |
| return -EINVAL; |
| |
| int *pfd1 = malloc(sizeof(*pfd1)); |
| *pfd1 = loop_fd; |
| struct cras_stream_params *loop_params; |
| cras_stream_id_t loop_stream_id = 0; |
| |
| direction = CRAS_STREAM_OUTPUT; |
| |
| loop_params = |
| cras_client_unified_params_create(direction, block_size, 0, 0, |
| pfd1, put_samples, |
| stream_error, aud_format); |
| stream_playing = start_stream(client, &loop_stream_id, loop_params, |
| volume_scaler) == 0; |
| if (!stream_playing) |
| return -EINVAL; |
| |
| fd_set poll_set; |
| |
| FD_ZERO(&poll_set); |
| FD_SET(pipefd[0], &poll_set); |
| pselect(pipefd[0] + 1, &poll_set, NULL, NULL, NULL, NULL); |
| cras_client_stop(client); |
| cras_audio_format_destroy(aud_format); |
| cras_client_stream_params_destroy(params); |
| free(pfd); |
| |
| close(pipefd[0]); |
| close(pipefd[1]); |
| |
| return 0; |
| } |
| |
| static struct option long_options[] = { { "help", no_argument, 0, 'h' }, |
| { "rate", required_argument, 0, 'r' }, |
| { 0, 0, 0, 0 } }; |
| |
| static void show_usage(void) |
| { |
| printf("--help - shows this message and exits\n"); |
| printf("--rate <N> - desired sample rate\n\n"); |
| printf("Running cras_router will run a loop through "); |
| printf("from the currently set input to the currently set output.\n"); |
| printf("Use cras_test_client --dump_s to see all avaiable nodes and"); |
| printf(" cras_test_client --set_input/output to set a node.\n"); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| struct cras_client *client; |
| size_t rate = 44100; |
| size_t num_channels = 2; |
| size_t block_size; |
| int rc = 0; |
| int c, option_index; |
| |
| option_index = 0; |
| |
| rc = cras_client_create(&client); |
| if (rc < 0) { |
| fprintf(stderr, "Couldn't create client.\n"); |
| return rc; |
| } |
| |
| rc = cras_client_connect(client); |
| if (rc) { |
| fprintf(stderr, "Couldn't connect to server.\n"); |
| goto destroy_exit; |
| } |
| |
| while (1) { |
| c = getopt_long(argc, argv, "hr:", long_options, &option_index); |
| if (c == -1) |
| break; |
| switch (c) { |
| case 'h': |
| show_usage(); |
| goto destroy_exit; |
| case 'r': |
| rate = atoi(optarg); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| block_size = get_block_size(PLAYBACK_BUFFERED_TIME_IN_NS, rate); |
| |
| /* Run loopthrough */ |
| int pfd[2]; |
| |
| rc = pipe(pfd); |
| if (rc < 0) { |
| fprintf(stderr, "Couldn't create loopthrough pipe.\n"); |
| return rc; |
| } |
| run_file_io_stream(client, pfd[1], pfd[0], CRAS_STREAM_INPUT, |
| block_size, rate, num_channels); |
| |
| destroy_exit: |
| cras_client_destroy(client); |
| return rc; |
| } |