• 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, 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