1 /* tinyhostless.c
2 **
3 ** Copyright 2011, The Android Open Source Project
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are met:
7 ** * Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
9 ** * Redistributions in binary form must reproduce the above copyright
10 ** notice, this list of conditions and the following disclaimer in the
11 ** documentation and/or other materials provided with the distribution.
12 ** * Neither the name of The Android Open Source Project nor the names of
13 ** its contributors may be used to endorse or promote products derived
14 ** from this software without specific prior written permission.
15 **
16 ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 ** DAMAGE.
27 */
28
29 /* Playback data to a PCM device recorded from a capture PCM device. */
30
31 #include <tinyalsa/asoundlib.h>
32 #include <math.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <string.h>
37 #include <signal.h>
38 #include <time.h>
39 #include <unistd.h>
40
41 /* Used when that particular device isn't opened. */
42 #define TINYHOSTLESS_DEVICE_UNDEFINED 255
43
44 static int close_h = 0;
45
46 int play_sample(unsigned int card, unsigned int p_device,
47 unsigned int c_device, unsigned int channels,
48 unsigned int rate, unsigned int bits, unsigned int period_size,
49 unsigned int period_count, unsigned int play_cap_time,
50 int do_loopback);
51
stream_close(int sig)52 static void stream_close(int sig)
53 {
54 /* allow the stream to be closed gracefully */
55 signal(sig, SIG_IGN);
56 close_h = 1;
57 }
58
main(int argc,char ** argv)59 int main(int argc, char **argv)
60 {
61 unsigned int card = 0;
62 unsigned int p_device = TINYHOSTLESS_DEVICE_UNDEFINED;
63 unsigned int c_device = TINYHOSTLESS_DEVICE_UNDEFINED;
64 unsigned int period_size = 192;
65 unsigned int period_count = 4;
66 unsigned int number_bits = 16;
67 unsigned int num_channels = 2;
68 unsigned int sample_rate = 48000;
69 unsigned int play_cap_time = 0;
70 unsigned int do_loopback = 0;
71
72 if (argc < 2) {
73 fprintf(stderr, "Usage: %s [-D card] [-P playback device]"
74 " [-C capture device] [-p period_size] [-n n_periods]"
75 " [-c num_channels] [-r sample_rate] [-l]"
76 " [-T playback/capture time]\n\n"
77 "Used to enable 'hostless' mode for audio devices with a DSP back-end.\n"
78 "Alternatively, specify '-l' for loopback mode: this program will read\n"
79 "from the capture device and write to the playback device.\n",
80 argv[0]);
81 return 1;
82 }
83
84 /* parse command line arguments */
85 argv += 1;
86 while (*argv) {
87 if (strcmp(*argv, "-P") == 0) {
88 argv++;
89 if (*argv)
90 p_device = atoi(*argv);
91 }
92 if (strcmp(*argv, "-C") == 0) {
93 argv++;
94 if (*argv)
95 c_device = atoi(*argv);
96 }
97 if (strcmp(*argv, "-p") == 0) {
98 argv++;
99 if (*argv)
100 period_size = atoi(*argv);
101 }
102 if (strcmp(*argv, "-n") == 0) {
103 argv++;
104 if (*argv)
105 period_count = atoi(*argv);
106 }
107 if (strcmp(*argv, "-c") == 0) {
108 argv++;
109 if (*argv)
110 num_channels = atoi(*argv);
111 }
112 if (strcmp(*argv, "-r") == 0) {
113 argv++;
114 if (*argv)
115 sample_rate = atoi(*argv);
116 }
117 if (strcmp(*argv, "-T") == 0) {
118 argv++;
119 if (*argv)
120 play_cap_time = atoi(*argv);
121 }
122 if (strcmp(*argv, "-D") == 0) {
123 argv++;
124 if (*argv)
125 card = atoi(*argv);
126 }
127 if (strcmp(*argv, "-l") == 0) {
128 do_loopback = 1;
129 }
130 if (*argv)
131 argv++;
132 }
133
134 if (p_device == TINYHOSTLESS_DEVICE_UNDEFINED &&
135 c_device == TINYHOSTLESS_DEVICE_UNDEFINED) {
136 fprintf(stderr, "Specify at least one of -C (capture device) or -P (playback device).\n");
137 return EINVAL;
138 }
139
140 if (do_loopback && (p_device == TINYHOSTLESS_DEVICE_UNDEFINED ||
141 c_device == TINYHOSTLESS_DEVICE_UNDEFINED)) {
142 fprintf(stderr, "Loopback requires both playback and capture devices.\n");
143 return EINVAL;
144 }
145
146 return play_sample(card, p_device, c_device, num_channels, sample_rate,
147 number_bits, period_size, period_count, play_cap_time,
148 do_loopback);
149 }
150
check_param(struct pcm_params * params,unsigned int param,unsigned int value,char * param_name,char * param_unit)151 static int check_param(struct pcm_params *params, unsigned int param,
152 unsigned int value, char *param_name, char *param_unit)
153 {
154 unsigned int min;
155 unsigned int max;
156 int is_within_bounds = 1;
157
158 min = pcm_params_get_min(params, param);
159 if (value < min) {
160 fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value,
161 param_unit, min, param_unit);
162 is_within_bounds = 0;
163 }
164
165 max = pcm_params_get_max(params, param);
166 if (value > max) {
167 fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value,
168 param_unit, max, param_unit);
169 is_within_bounds = 0;
170 }
171
172 return is_within_bounds;
173 }
174
check_params(unsigned int card,unsigned int device,unsigned int direction,const struct pcm_config * config)175 static int check_params(unsigned int card, unsigned int device, unsigned int direction,
176 const struct pcm_config *config)
177 {
178 struct pcm_params *params;
179 int can_play;
180 int bits;
181
182 params = pcm_params_get(card, device, direction);
183 if (params == NULL) {
184 fprintf(stderr, "Unable to open PCM %s device %u.\n",
185 direction == PCM_OUT ? "playback" : "capture", device);
186 return 0;
187 }
188
189 switch (config->format) {
190 case PCM_FORMAT_S32_LE:
191 bits = 32;
192 break;
193 case PCM_FORMAT_S24_3LE:
194 bits = 24;
195 break;
196 case PCM_FORMAT_S16_LE:
197 bits = 16;
198 break;
199 default:
200 fprintf(stderr, "Invalid format: %u", config->format);
201 return 0;
202 }
203
204 can_play = check_param(params, PCM_PARAM_RATE, config->rate, "Sample rate", "Hz");
205 can_play &= check_param(params, PCM_PARAM_CHANNELS, config->channels, "Sample", " channels");
206 can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");
207 can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, config->period_size, "Period size", " frames");
208 can_play &= check_param(params, PCM_PARAM_PERIODS, config->period_count, "Period count", " periods");
209
210 pcm_params_free(params);
211
212 return can_play;
213 }
214
play_sample(unsigned int card,unsigned int p_device,unsigned int c_device,unsigned int channels,unsigned int rate,unsigned int bits,unsigned int period_size,unsigned int period_count,unsigned int play_cap_time,int do_loopback)215 int play_sample(unsigned int card, unsigned int p_device,
216 unsigned int c_device, unsigned int channels,
217 unsigned int rate, unsigned int bits, unsigned int period_size,
218 unsigned int period_count, unsigned int play_cap_time,
219 int do_loopback)
220 {
221 struct pcm_config config;
222 struct pcm *pcm_play =NULL, *pcm_cap=NULL;
223 unsigned int count =0;
224 char *buffer = NULL;
225 int size = 0;
226 int rc = 0;
227 struct timespec end;
228 struct timespec now;
229
230 memset(&config, 0, sizeof(config));
231 config.channels = channels;
232 config.rate = rate;
233 config.period_size = period_size;
234 config.period_count = period_count;
235 if (bits == 32)
236 config.format = PCM_FORMAT_S32_LE;
237 else if (bits == 24)
238 config.format = PCM_FORMAT_S24_3LE;
239 else if (bits == 16)
240 config.format = PCM_FORMAT_S16_LE;
241 config.start_threshold = 0;
242 config.stop_threshold = 0;
243 config.avail_min = 0;
244
245 if(p_device < TINYHOSTLESS_DEVICE_UNDEFINED ) {
246 if (!check_params(card, p_device, PCM_OUT, &config))
247 return EINVAL;
248 pcm_play = pcm_open(card, p_device, PCM_OUT, &config);
249 if (!pcm_play || !pcm_is_ready(pcm_play)) {
250 fprintf(stderr, "Unable to open PCM playback device %u (%s)\n",
251 p_device, pcm_get_error(pcm_play));
252 return errno;
253 }
254 }
255
256 if (c_device < TINYHOSTLESS_DEVICE_UNDEFINED ) {
257 if (!check_params(card, c_device, PCM_IN, &config))
258 return EINVAL;
259 pcm_cap = pcm_open(card, c_device, PCM_IN, &config);
260 if (!pcm_cap || !pcm_is_ready(pcm_cap)) {
261 fprintf(stderr, "Unable to open PCM capture device %u (%s)\n",
262 c_device, pcm_get_error(pcm_cap));
263 if (pcm_play != NULL ) pcm_close(pcm_play);
264 return errno;
265 }
266 }
267
268 printf("%s: Playing device %u, Capture Device %u\n",
269 do_loopback ? "Loopback" : "Hostless", p_device, c_device);
270 printf("Sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
271 if (play_cap_time)
272 printf("Duration in sec: %u\n", play_cap_time);
273 else
274 printf("Duration in sec: forever\n");
275
276 if (do_loopback) {
277 size = pcm_frames_to_bytes(pcm_cap, pcm_get_buffer_size(pcm_cap));
278 buffer = malloc(size);
279 if (!buffer) {
280 fprintf(stderr, "Unable to allocate %d bytes\n", size);
281 pcm_close(pcm_play);
282 pcm_close(pcm_cap);
283 return ENOMEM;
284 }
285 }
286
287 /* catch ctrl-c to shutdown cleanly */
288 signal(SIGINT, stream_close);
289 signal(SIGHUP, stream_close);
290 signal(SIGTERM, stream_close);
291
292 if (pcm_cap != NULL) pcm_start(pcm_cap);
293 if (pcm_play != NULL) pcm_start(pcm_play);
294
295 clock_gettime(CLOCK_MONOTONIC, &now);
296 end.tv_sec = now.tv_sec + play_cap_time;
297 end.tv_nsec = now.tv_nsec;
298
299 do {
300 if (do_loopback) {
301 if (pcm_read(pcm_cap, buffer, size)) {
302 fprintf(stderr, "Unable to read from PCM capture device %u (%s)\n",
303 c_device, pcm_get_error(pcm_cap));
304 rc = errno;
305 break;
306 }
307 if (pcm_write(pcm_play, buffer, size)) {
308 fprintf(stderr, "Unable to write to PCM playback device %u (%s)\n",
309 p_device, pcm_get_error(pcm_play));
310 break;
311 }
312 } else {
313 usleep(100000);
314 }
315 if (play_cap_time) {
316 clock_gettime(CLOCK_MONOTONIC, &now);
317 if (now.tv_sec > end.tv_sec ||
318 (now.tv_sec == end.tv_sec && now.tv_nsec >= end.tv_nsec))
319 break;
320 }
321 } while(!close_h);
322
323 if (buffer)
324 free(buffer);
325 if (pcm_play != NULL)
326 pcm_close(pcm_play);
327 if (pcm_cap != NULL)
328 pcm_close(pcm_cap);
329 return rc;
330 }
331