• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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