1 /* Copyright 2016 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <getopt.h>
8 #include <pthread.h>
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/param.h>
14 #include <unistd.h>
15
16 #include "cras_client.h"
17 #include "cras_types.h"
18 #include "cras_util.h"
19 #include "cras_version.h"
20
21 #define PLAYBACK_BUFFERED_TIME_IN_NS (5000000)
22
23 #define BUF_SIZE 32768
24
25 static int keep_looping = 1;
26 static int pipefd[2];
27 struct cras_audio_format *aud_format;
28
terminate_stream_loop(void)29 static int terminate_stream_loop(void)
30 {
31 keep_looping = 0;
32 return write(pipefd[1], "1", 1);
33 }
34
get_block_size(uint64_t buffer_time_in_ns,size_t rate)35 static size_t get_block_size(uint64_t buffer_time_in_ns, size_t rate)
36 {
37 static struct timespec t;
38
39 t.tv_nsec = buffer_time_in_ns;
40 t.tv_sec = 0;
41 return (size_t)cras_time_to_frames(&t, rate);
42 }
43
44 /* Run from callback thread. */
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)45 static int got_samples(struct cras_client *client, cras_stream_id_t stream_id,
46 uint8_t *captured_samples, uint8_t *playback_samples,
47 unsigned int frames,
48 const struct timespec *captured_time,
49 const struct timespec *playback_time, void *user_arg)
50 {
51 int *fd = (int *)user_arg;
52 int ret;
53 int write_size;
54 int frame_bytes;
55
56 frame_bytes = cras_client_format_bytes_per_frame(aud_format);
57 write_size = frames * frame_bytes;
58 ret = write(*fd, captured_samples, write_size);
59 if (ret != write_size)
60 printf("Error writing file\n");
61 return frames;
62 }
63
64 /* Run from callback thread. */
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)65 static int put_samples(struct cras_client *client, cras_stream_id_t stream_id,
66 uint8_t *captured_samples, uint8_t *playback_samples,
67 unsigned int frames,
68 const struct timespec *captured_time,
69 const struct timespec *playback_time, void *user_arg)
70 {
71 uint32_t frame_bytes = cras_client_format_bytes_per_frame(aud_format);
72 int fd = *(int *)user_arg;
73 uint8_t buff[BUF_SIZE];
74 int nread;
75
76 nread = read(fd, buff, MIN(frames * frame_bytes, BUF_SIZE));
77 if (nread <= 0) {
78 terminate_stream_loop();
79 return nread;
80 }
81
82 memcpy(playback_samples, buff, nread);
83 return nread / frame_bytes;
84 }
85
stream_error(struct cras_client * client,cras_stream_id_t stream_id,int err,void * arg)86 static int stream_error(struct cras_client *client, cras_stream_id_t stream_id,
87 int err, void *arg)
88 {
89 printf("Stream error %d\n", err);
90 terminate_stream_loop();
91 return 0;
92 }
93
start_stream(struct cras_client * client,cras_stream_id_t * stream_id,struct cras_stream_params * params,float stream_volume)94 static int start_stream(struct cras_client *client, cras_stream_id_t *stream_id,
95 struct cras_stream_params *params, float stream_volume)
96 {
97 int rc;
98
99 rc = cras_client_add_stream(client, stream_id, params);
100 if (rc < 0) {
101 fprintf(stderr, "adding a stream %d\n", rc);
102 return rc;
103 }
104 return cras_client_set_stream_volume(client, *stream_id, stream_volume);
105 }
106
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)107 static int run_file_io_stream(struct cras_client *client, int fd, int loop_fd,
108 enum CRAS_STREAM_DIRECTION direction,
109 size_t block_size, size_t rate,
110 size_t num_channels)
111 {
112 struct cras_stream_params *params;
113 cras_stream_id_t stream_id = 0;
114 int stream_playing = 0;
115 int *pfd = malloc(sizeof(*pfd));
116 *pfd = fd;
117 float volume_scaler = 1.0;
118
119 if (pipe(pipefd) == -1) {
120 perror("failed to open pipe");
121 return -errno;
122 }
123 aud_format = cras_audio_format_create(SND_PCM_FORMAT_S16_LE, rate,
124 num_channels);
125 if (aud_format == NULL)
126 return -ENOMEM;
127
128 params = cras_client_unified_params_create(direction, block_size, 0, 0,
129 pfd, got_samples,
130 stream_error, aud_format);
131 if (params == NULL)
132 return -ENOMEM;
133
134 cras_client_run_thread(client);
135 stream_playing =
136 start_stream(client, &stream_id, params, volume_scaler) == 0;
137 if (!stream_playing)
138 return -EINVAL;
139
140 int *pfd1 = malloc(sizeof(*pfd1));
141 *pfd1 = loop_fd;
142 struct cras_stream_params *loop_params;
143 cras_stream_id_t loop_stream_id = 0;
144
145 direction = CRAS_STREAM_OUTPUT;
146
147 loop_params =
148 cras_client_unified_params_create(direction, block_size, 0, 0,
149 pfd1, put_samples,
150 stream_error, aud_format);
151 stream_playing = start_stream(client, &loop_stream_id, loop_params,
152 volume_scaler) == 0;
153 if (!stream_playing)
154 return -EINVAL;
155
156 fd_set poll_set;
157
158 FD_ZERO(&poll_set);
159 FD_SET(pipefd[0], &poll_set);
160 pselect(pipefd[0] + 1, &poll_set, NULL, NULL, NULL, NULL);
161 cras_client_stop(client);
162 cras_audio_format_destroy(aud_format);
163 cras_client_stream_params_destroy(params);
164 free(pfd);
165
166 close(pipefd[0]);
167 close(pipefd[1]);
168
169 return 0;
170 }
171
172 static struct option long_options[] = { { "help", no_argument, 0, 'h' },
173 { "rate", required_argument, 0, 'r' },
174 { 0, 0, 0, 0 } };
175
show_usage(void)176 static void show_usage(void)
177 {
178 printf("--help - shows this message and exits\n");
179 printf("--rate <N> - desired sample rate\n\n");
180 printf("Running cras_router will run a loop through ");
181 printf("from the currently set input to the currently set output.\n");
182 printf("Use cras_test_client --dump_s to see all avaiable nodes and");
183 printf(" cras_test_client --set_input/output to set a node.\n");
184 }
185
main(int argc,char ** argv)186 int main(int argc, char **argv)
187 {
188 struct cras_client *client;
189 size_t rate = 44100;
190 size_t num_channels = 2;
191 size_t block_size;
192 int rc = 0;
193 int c, option_index;
194
195 option_index = 0;
196
197 rc = cras_client_create(&client);
198 if (rc < 0) {
199 fprintf(stderr, "Couldn't create client.\n");
200 return rc;
201 }
202
203 rc = cras_client_connect(client);
204 if (rc) {
205 fprintf(stderr, "Couldn't connect to server.\n");
206 goto destroy_exit;
207 }
208
209 while (1) {
210 c = getopt_long(argc, argv, "hr:", long_options, &option_index);
211 if (c == -1)
212 break;
213 switch (c) {
214 case 'h':
215 show_usage();
216 goto destroy_exit;
217 case 'r':
218 rate = atoi(optarg);
219 break;
220 default:
221 break;
222 }
223 }
224
225 block_size = get_block_size(PLAYBACK_BUFFERED_TIME_IN_NS, rate);
226
227 /* Run loopthrough */
228 int pfd[2];
229
230 rc = pipe(pfd);
231 if (rc < 0) {
232 fprintf(stderr, "Couldn't create loopthrough pipe.\n");
233 return rc;
234 }
235 run_file_io_stream(client, pfd[1], pfd[0], CRAS_STREAM_INPUT,
236 block_size, rate, num_channels);
237
238 destroy_exit:
239 cras_client_destroy(client);
240 return rc;
241 }
242