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,
46 cras_stream_id_t stream_id,
47 uint8_t *captured_samples,
48 uint8_t *playback_samples,
49 unsigned int frames,
50 const struct timespec *captured_time,
51 const struct timespec *playback_time,
52 void *user_arg)
53 {
54 int *fd = (int *)user_arg;
55 int ret;
56 int write_size;
57 int frame_bytes;
58
59 frame_bytes = cras_client_format_bytes_per_frame(aud_format);
60 write_size = frames * frame_bytes;
61 ret = write(*fd, captured_samples, write_size);
62 if (ret != write_size)
63 printf("Error writing file\n");
64 return frames;
65 }
66
67 /* 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)68 static int put_samples(struct cras_client *client,
69 cras_stream_id_t stream_id,
70 uint8_t *captured_samples,
71 uint8_t *playback_samples,
72 unsigned int frames,
73 const struct timespec *captured_time,
74 const struct timespec *playback_time,
75 void *user_arg)
76 {
77 uint32_t frame_bytes = cras_client_format_bytes_per_frame(aud_format);
78 int fd = *(int *)user_arg;
79 uint8_t buff[BUF_SIZE];
80 int nread;
81
82 nread = read(fd, buff, MIN(frames * frame_bytes, BUF_SIZE));
83 if (nread <= 0) {
84 terminate_stream_loop();
85 return nread;
86 }
87
88 memcpy(playback_samples, buff, nread);
89 return nread / frame_bytes;
90 }
91
stream_error(struct cras_client * client,cras_stream_id_t stream_id,int err,void * arg)92 static int stream_error(struct cras_client *client,
93 cras_stream_id_t stream_id,
94 int err,
95 void *arg)
96 {
97 printf("Stream error %d\n", err);
98 terminate_stream_loop();
99 return 0;
100 }
101
start_stream(struct cras_client * client,cras_stream_id_t * stream_id,struct cras_stream_params * params,float stream_volume)102 static int start_stream(struct cras_client *client,
103 cras_stream_id_t *stream_id,
104 struct cras_stream_params *params,
105 float stream_volume)
106 {
107 int rc;
108
109 rc = cras_client_add_stream(client, stream_id, params);
110 if (rc < 0) {
111 fprintf(stderr, "adding a stream %d\n", rc);
112 return rc;
113 }
114 return cras_client_set_stream_volume(client,
115 *stream_id,
116 stream_volume);
117 }
118
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)119 static int run_file_io_stream(struct cras_client *client,
120 int fd,
121 int loop_fd,
122 enum CRAS_STREAM_DIRECTION direction,
123 size_t block_size,
124 size_t rate,
125 size_t num_channels)
126 {
127 struct cras_stream_params *params;
128 cras_stream_id_t stream_id = 0;
129 int stream_playing = 0;
130 int *pfd = malloc(sizeof(*pfd));
131 *pfd = fd;
132 float volume_scaler = 1.0;
133
134 if (pipe(pipefd) == -1) {
135 perror("failed to open pipe");
136 return -errno;
137 }
138 aud_format = cras_audio_format_create(SND_PCM_FORMAT_S16_LE, rate,
139 num_channels);
140 if (aud_format == NULL)
141 return -ENOMEM;
142
143 params = cras_client_unified_params_create(direction,
144 block_size,
145 0,
146 0,
147 pfd,
148 got_samples,
149 stream_error,
150 aud_format);
151 if (params == NULL)
152 return -ENOMEM;
153
154 cras_client_run_thread(client);
155 stream_playing =
156 start_stream(client, &stream_id, params, volume_scaler) == 0;
157 if (!stream_playing)
158 return -EINVAL;
159
160 int *pfd1 = malloc(sizeof(*pfd1));
161 *pfd1 = loop_fd;
162 struct cras_stream_params *loop_params;
163 cras_stream_id_t loop_stream_id = 0;
164
165 direction = CRAS_STREAM_OUTPUT;
166
167 loop_params = cras_client_unified_params_create(direction,
168 block_size,
169 0,
170 0,
171 pfd1,
172 put_samples,
173 stream_error,
174 aud_format);
175 stream_playing =
176 start_stream(client, &loop_stream_id,
177 loop_params, volume_scaler) == 0;
178 if (!stream_playing)
179 return -EINVAL;
180
181 fd_set poll_set;
182
183 FD_ZERO(&poll_set);
184 FD_SET(pipefd[0], &poll_set);
185 pselect(pipefd[0] + 1, &poll_set, NULL, NULL, NULL, NULL);
186 cras_client_stop(client);
187 cras_audio_format_destroy(aud_format);
188 cras_client_stream_params_destroy(params);
189 free(pfd);
190
191 close(pipefd[0]);
192 close(pipefd[1]);
193
194 return 0;
195 }
196
197 static struct option long_options[] = {
198 {"help", no_argument, 0, 'h'},
199 {"rate", required_argument, 0, 'r'},
200 {0, 0, 0, 0}
201 };
202
show_usage(void)203 static void show_usage(void)
204 {
205 printf("--help - shows this message and exits\n");
206 printf("--rate <N> - desired sample rate\n\n");
207 printf("Running cras_router will run a loop through ");
208 printf("from the currently set input to the currently set output.\n");
209 printf("Use cras_test_client --dump_s to see all avaiable nodes and");
210 printf(" cras_test_client --set_input/output to set a node.\n");
211 }
212
main(int argc,char ** argv)213 int main(int argc, char **argv)
214 {
215 struct cras_client *client;
216 size_t rate = 44100;
217 size_t num_channels = 2;
218 size_t block_size;
219 int rc = 0;
220 int c, option_index;
221
222 option_index = 0;
223
224 rc = cras_client_create(&client);
225 if (rc < 0) {
226 fprintf(stderr, "Couldn't create client.\n");
227 return rc;
228 }
229
230 rc = cras_client_connect(client);
231 if (rc) {
232 fprintf(stderr, "Couldn't connect to server.\n");
233 goto destroy_exit;
234 }
235
236 while (1) {
237 c = getopt_long(argc, argv, "hr:",
238 long_options, &option_index);
239 if (c == -1)
240 break;
241 switch (c) {
242 case 'h':
243 show_usage();
244 goto destroy_exit;
245 case 'r':
246 rate = atoi(optarg);
247 break;
248 default:
249 break;
250 }
251 }
252
253 block_size = get_block_size(PLAYBACK_BUFFERED_TIME_IN_NS, rate);
254
255 /* Run loopthrough */
256 int pfd[2];
257
258 rc = pipe(pfd);
259 if (rc < 0) {
260 fprintf(stderr, "Couldn't create loopthrough pipe.\n");
261 return rc;
262 }
263 run_file_io_stream(client, pfd[1], pfd[0], CRAS_STREAM_INPUT,
264 block_size, rate, num_channels);
265
266 destroy_exit:
267 cras_client_destroy(client);
268 return rc;
269 }
270