blob: c0602775c269c574dc1c0a139d9679410b29a792 [file] [log] [blame]
Iliyan Malchev4765c432012-06-11 14:36:16 -07001/*
2** Copyright 2010, The Android Open-Source Project
3** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <fcntl.h>
21#include <unistd.h>
22#include <stdint.h>
23#include <string.h>
24#include <errno.h>
25#include <sys/poll.h>
26#include <sys/ioctl.h>
27#include <getopt.h>
28
29#include <sound/asound.h>
30#include "alsa_audio.h"
31
32#ifndef ANDROID
33#define strlcat g_strlcat
34#define strlcpy g_strlcpy
35#endif
36
37#define ID_RIFF 0x46464952
38#define ID_WAVE 0x45564157
39#define ID_FMT 0x20746d66
40#define ID_DATA 0x61746164
41
42#define FORMAT_PCM 1
43#define LOG_NDEBUG 1
44static pcm_flag = 1;
45static debug = 0;
46static uint32_t play_max_sz = 2147483648LL;
47static int format = SNDRV_PCM_FORMAT_S16_LE;
48static int period = 0;
49static int compressed = 0;
50static char *compr_codec;
51static int piped = 0;
52
53static struct option long_options[] =
54{
55 {"pcm", 0, 0, 'P'},
56 {"debug", 0, 0, 'V'},
57 {"Mmap", 0, 0, 'M'},
58 {"HW", 1, 0, 'D'},
59 {"Rate", 1, 0, 'R'},
60 {"channel", 1, 0, 'C'},
61 {"format", 1, 0, 'F'},
62 {"period", 1, 0, 'B'},
63 {"compressed", 0, 0, 'T'},
64 {0, 0, 0, 0}
65};
66
67struct wav_header {
68 uint32_t riff_id;
69 uint32_t riff_sz;
70 uint32_t riff_fmt;
71 uint32_t fmt_id;
72 uint32_t fmt_sz;
73 uint16_t audio_format;
74 uint16_t num_channels;
75 uint32_t sample_rate;
76 uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */
77 uint16_t block_align; /* num_channels * bps / 8 */
78 uint16_t bits_per_sample;
79 uint32_t data_id;
80 uint32_t data_sz;
81};
82
83static int set_params(struct pcm *pcm)
84{
85 struct snd_pcm_hw_params *params;
86 struct snd_pcm_sw_params *sparams;
87
88 unsigned long periodSize, bufferSize, reqBuffSize;
89 unsigned int periodTime, bufferTime;
90 unsigned int requestedRate = pcm->rate;
91 int channels = (pcm->flags & PCM_MONO) ? 1 : ((pcm->flags & PCM_5POINT1)? 6 : 2 );
92
93 params = (struct snd_pcm_hw_params*) calloc(1, sizeof(struct snd_pcm_hw_params));
94 if (!params) {
95 fprintf(stderr, "Aplay:Failed to allocate ALSA hardware parameters!");
96 return -ENOMEM;
97 }
98
99 param_init(params);
100
101 param_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS,
102 (pcm->flags & PCM_MMAP)? SNDRV_PCM_ACCESS_MMAP_INTERLEAVED : SNDRV_PCM_ACCESS_RW_INTERLEAVED);
103 param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, pcm->format);
104 param_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
105 SNDRV_PCM_SUBFORMAT_STD);
106 if (period)
107 param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, period);
108 else
109 param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 10);
110 param_set_int(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16);
111 param_set_int(params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
112 pcm->channels * 16);
113 param_set_int(params, SNDRV_PCM_HW_PARAM_CHANNELS,
114 pcm->channels);
115 param_set_int(params, SNDRV_PCM_HW_PARAM_RATE, pcm->rate);
116 param_set_hw_refine(pcm, params);
117
118 if (param_set_hw_params(pcm, params)) {
119 fprintf(stderr, "Aplay:cannot set hw params\n");
120 return -errno;
121 }
122 if (debug)
123 param_dump(params);
124
125 pcm->buffer_size = pcm_buffer_size(params);
126 pcm->period_size = pcm_period_size(params);
127 pcm->period_cnt = pcm->buffer_size/pcm->period_size;
128 if (debug) {
129 fprintf (stderr,"period_cnt = %d\n", pcm->period_cnt);
130 fprintf (stderr,"period_size = %d\n", pcm->period_size);
131 fprintf (stderr,"buffer_size = %d\n", pcm->buffer_size);
132 }
133 sparams = (struct snd_pcm_sw_params*) calloc(1, sizeof(struct snd_pcm_sw_params));
134 if (!sparams) {
135 fprintf(stderr, "Aplay:Failed to allocate ALSA software parameters!\n");
136 return -ENOMEM;
137 }
138 // Get the current software parameters
139 sparams->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
140 sparams->period_step = 1;
141
142 sparams->avail_min = pcm->period_size/(channels * 2) ;
143 sparams->start_threshold = pcm->period_size/(channels * 2) ;
144 sparams->stop_threshold = pcm->buffer_size ;
145 sparams->xfer_align = pcm->period_size/(channels * 2) ; /* needed for old kernels */
146
147 sparams->silence_size = 0;
148 sparams->silence_threshold = 0;
149
150 if (param_set_sw_params(pcm, sparams)) {
151 fprintf(stderr, "Aplay:cannot set sw params");
152 return -errno;
153 }
154 if (debug) {
155 fprintf (stderr,"sparams->avail_min= %lu\n", sparams->avail_min);
156 fprintf (stderr," sparams->start_threshold= %lu\n", sparams->start_threshold);
157 fprintf (stderr," sparams->stop_threshold= %lu\n", sparams->stop_threshold);
158 fprintf (stderr," sparams->xfer_align= %lu\n", sparams->xfer_align);
159 fprintf (stderr," sparams->boundary= %lu\n", sparams->boundary);
160 }
161 return 0;
162}
163
164static int play_file(unsigned rate, unsigned channels, int fd,
165 unsigned flags, const char *device, unsigned data_sz)
166{
167 struct pcm *pcm;
168 struct mixer *mixer;
169 struct pcm_ctl *ctl = NULL;
170 unsigned bufsize;
171 char *data;
172 long avail;
173 long frames;
174 int nfds = 1;
175 struct snd_xferi x;
176 unsigned offset = 0;
177 int err;
178 static int start = 0;
179 struct pollfd pfd[1];
180 int remainingData = 0;
181
182 flags |= PCM_OUT;
183
184 if (channels == 1)
185 flags |= PCM_MONO;
186 else if (channels == 6)
187 flags |= PCM_5POINT1;
188 else
189 flags |= PCM_STEREO;
190
191 if (debug)
192 flags |= DEBUG_ON;
193 else
194 flags |= DEBUG_OFF;
195
196 pcm = pcm_open(flags, device);
197 if (pcm < 0)
198 return pcm;
199
200 if (!pcm_ready(pcm)) {
201 pcm_close(pcm);
202 return -EBADFD;
203 }
204
Ajay Dudani9746c472012-06-18 16:01:16 -0700205#ifdef QCOM_COMPRESSED_AUDIO_ENABLED
Iliyan Malchev4765c432012-06-11 14:36:16 -0700206 if (compressed) {
207 struct snd_compr_caps compr_cap;
208 struct snd_compr_params compr_params;
209 if (ioctl(pcm->fd, SNDRV_COMPRESS_GET_CAPS, &compr_cap)) {
210 fprintf(stderr, "Aplay: SNDRV_COMPRESS_GET_CAPS, failed Error no %d \n", errno);
211 pcm_close(pcm);
212 return -errno;
213 }
214 if (!period)
215 period = compr_cap.min_fragment_size;
216 switch (get_compressed_format(compr_codec)) {
217 case FORMAT_MP3:
218 compr_params.codec.id = compr_cap.codecs[FORMAT_MP3];
219 break;
220 case FORMAT_AC3_PASS_THROUGH:
221 compr_params.codec.id = compr_cap.codecs[FORMAT_AC3_PASS_THROUGH];
222 printf("codec -d = %x\n", compr_params.codec.id);
223 break;
224 default:
225 break;
226 }
227 if (ioctl(pcm->fd, SNDRV_COMPRESS_SET_PARAMS, &compr_params)) {
228 fprintf(stderr, "Aplay: SNDRV_COMPRESS_SET_PARAMS,failed Error no %d \n", errno);
229 pcm_close(pcm);
230 return -errno;
231 }
232 }
Ajay Dudani9746c472012-06-18 16:01:16 -0700233#endif
Iliyan Malchev4765c432012-06-11 14:36:16 -0700234 pcm->channels = channels;
235 pcm->rate = rate;
236 pcm->flags = flags;
237 pcm->format = format;
238 if (set_params(pcm)) {
239 fprintf(stderr, "Aplay:params setting failed\n");
240 pcm_close(pcm);
241 return -errno;
242 }
243
244 if (!pcm_flag) {
245 if (pcm_prepare(pcm)) {
246 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
247 pcm_close(pcm);
248 return -errno;
249 }
250 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
251 fprintf(stderr, "Aplay: Hostless IOCTL_START Error no %d \n", errno);
252 pcm_close(pcm);
253 return -errno;
254 }
255 while(1);
256 }
257
258 remainingData = data_sz;
259
260 if (flags & PCM_MMAP) {
261 u_int8_t *dst_addr = NULL;
262 struct snd_pcm_sync_ptr *sync_ptr1 = pcm->sync_ptr;
263 if (mmap_buffer(pcm)) {
264 fprintf(stderr, "Aplay:params setting failed\n");
265 pcm_close(pcm);
266 return -errno;
267 }
268 if (pcm_prepare(pcm)) {
269 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
270 pcm_close(pcm);
271 return -errno;
272 }
273
274 bufsize = pcm->period_size;
275 if (debug)
276 fprintf(stderr, "Aplay:bufsize = %d\n", bufsize);
277
278 pfd[0].fd = pcm->timer_fd;
279 pfd[0].events = POLLIN;
280
281 frames = (pcm->flags & PCM_MONO) ? (bufsize / 2) : (bufsize / 4);
282 for (;;) {
283 if (!pcm->running) {
284 if (pcm_prepare(pcm)) {
285 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
286 pcm_close(pcm);
287 return -errno;
288 }
289 pcm->running = 1;
290 start = 0;
291 }
292 /* Sync the current Application pointer from the kernel */
293 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;//SNDRV_PCM_SYNC_PTR_HWSYNC;
294 err = sync_ptr(pcm);
295 if (err == EPIPE) {
296 fprintf(stderr, "Aplay:Failed in sync_ptr \n");
297 /* we failed to make our window -- try to restart */
298 pcm->underruns++;
299 pcm->running = 0;
300 continue;
301 }
302 /*
303 * Check for the available buffer in driver. If available buffer is
304 * less than avail_min we need to wait
305 */
306 avail = pcm_avail(pcm);
307 if (avail < 0) {
308 fprintf(stderr, "Aplay:Failed in pcm_avail\n");
309 pcm_close(pcm);
310 return avail;
311 }
312 if (avail < pcm->sw_p->avail_min) {
313 poll(pfd, nfds, TIMEOUT_INFINITE);
314 continue;
315 }
316 /*
317 * Now that we have buffer size greater than avail_min available to
318 * to be written we need to calcutate the buffer offset where we can
319 * start writting.
320 */
321 dst_addr = dst_address(pcm);
322
323 if (debug) {
324 fprintf(stderr, "dst_addr = 0x%08x\n", dst_addr);
325 fprintf(stderr, "Aplay:avail = %d frames = %d\n",avail, frames);
326 fprintf(stderr, "Aplay:sync_ptr->s.status.hw_ptr %ld pcm->buffer_size %d sync_ptr->c.control.appl_ptr %ld\n",
327 pcm->sync_ptr->s.status.hw_ptr,
328 pcm->buffer_size,
329 pcm->sync_ptr->c.control.appl_ptr);
330 }
331
332 /*
333 * Read from the file to the destination buffer in kernel mmaped buffer
334 * This reduces a extra copy of intermediate buffer.
335 */
336 memset(dst_addr, 0x0, bufsize);
337
338 if (data_sz && !piped) {
339 if (remainingData < bufsize) {
340 bufsize = remainingData;
341 frames = (pcm->flags & PCM_MONO) ? (remainingData / 2) : (remainingData / 4);
342 }
343 }
344
345 err = read(fd, dst_addr , bufsize);
346 if (debug)
347 fprintf(stderr, "read %d bytes from file\n", err);
348 if (err <= 0)
349 break;
350
351 if (data_sz && !piped) {
352 remainingData -= bufsize;
353 if (remainingData <= 0)
354 break;
355 }
356
357 /*
358 * Increment the application pointer with data written to kernel.
359 * Update kernel with the new sync pointer.
360 */
361 pcm->sync_ptr->c.control.appl_ptr += frames;
362 pcm->sync_ptr->flags = 0;
363
364 err = sync_ptr(pcm);
365 if (err == EPIPE) {
366 fprintf(stderr, "Aplay:Failed in sync_ptr 2 \n");
367 /* we failed to make our window -- try to restart */
368 pcm->underruns++;
369 pcm->running = 0;
370 continue;
371 }
372
373 if (debug) {
374 fprintf(stderr, "Aplay:sync_ptr->s.status.hw_ptr %ld sync_ptr->c.control.appl_ptr %ld\n",
375 pcm->sync_ptr->s.status.hw_ptr,
376 pcm->sync_ptr->c.control.appl_ptr);
Ajay Dudani9746c472012-06-18 16:01:16 -0700377#ifdef QCOM_COMPRESSED_AUDIO_ENABLED
Iliyan Malchev4765c432012-06-11 14:36:16 -0700378 if (compressed && start) {
379 struct snd_compr_tstamp tstamp;
380 if (ioctl(pcm->fd, SNDRV_COMPRESS_TSTAMP, &tstamp))
381 fprintf(stderr, "Aplay: failed SNDRV_COMPRESS_TSTAMP\n");
382 else
383 fprintf(stderr, "timestamp = %lld\n", tstamp.timestamp);
384 }
Ajay Dudani9746c472012-06-18 16:01:16 -0700385#endif
Iliyan Malchev4765c432012-06-11 14:36:16 -0700386 }
387 /*
388 * If we have reached start threshold of buffer prefill,
389 * its time to start the driver.
390 */
391 if(start)
392 goto start_done;
393 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
394 err = -errno;
395 if (errno == EPIPE) {
396 fprintf(stderr, "Aplay:Failed in SNDRV_PCM_IOCTL_START\n");
397 /* we failed to make our window -- try to restart */
398 pcm->underruns++;
399 pcm->running = 0;
400 continue;
401 } else {
402 fprintf(stderr, "Aplay:Error no %d \n", errno);
403 pcm_close(pcm);
404 return -errno;
405 }
406 } else
407 start = 1;
408
409start_done:
410 offset += frames;
411 }
412 while(1) {
413 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;//SNDRV_PCM_SYNC_PTR_HWSYNC;
414 sync_ptr(pcm);
415 /*
416 * Check for the available buffer in driver. If available buffer is
417 * less than avail_min we need to wait
418 */
419 if (pcm->sync_ptr->s.status.hw_ptr >= pcm->sync_ptr->c.control.appl_ptr) {
420 fprintf(stderr, "Aplay:sync_ptr->s.status.hw_ptr %ld sync_ptr->c.control.appl_ptr %ld\n",
421 pcm->sync_ptr->s.status.hw_ptr,
422 pcm->sync_ptr->c.control.appl_ptr);
423 break;
424 } else
425 poll(pfd, nfds, TIMEOUT_INFINITE);
426 }
427 } else {
428 if (pcm_prepare(pcm)) {
429 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
430 pcm_close(pcm);
431 return -errno;
432 }
433
434 bufsize = pcm->period_size;
435
436 data = calloc(1, bufsize);
437 if (!data) {
438 fprintf(stderr, "Aplay:could not allocate %d bytes\n", bufsize);
439 pcm_close(pcm);
440 return -ENOMEM;
441 }
442
443 if (data_sz && !piped) {
444 if (remainingData < bufsize)
445 bufsize = remainingData;
446 }
447
448 while (read(fd, data, bufsize) > 0) {
449 if (pcm_write(pcm, data, bufsize)){
450 fprintf(stderr, "Aplay: pcm_write failed\n");
451 free(data);
452 pcm_close(pcm);
453 return -errno;
454 }
455 memset(data, 0, bufsize);
456
457 if (data_sz && !piped) {
458 remainingData -= bufsize;
459 if (remainingData <= 0)
460 break;
461 if (remainingData < bufsize)
462 bufsize = remainingData;
463 }
464 }
465 free(data);
466 }
467 fprintf(stderr, "Aplay: Done playing\n");
468 pcm_close(pcm);
469 return 0;
470}
471
472int play_raw(const char *fg, int rate, int ch, const char *device, const char *fn)
473{
474 int fd;
475 unsigned flag = 0;
476
477 if(!fn) {
478 fd = fileno(stdin);
479 piped = 1;
480 } else {
481 fd = open(fn, O_RDONLY);
482 if (fd < 0) {
483 fprintf(stderr, "Aplay:aplay: cannot open '%s'\n", fn);
484 return fd;
485 }
486 }
487
488 if (!strncmp(fg, "M", sizeof("M")))
489 flag = PCM_MMAP;
490 else if (!strncmp(fg, "N", sizeof("N")))
491 flag = PCM_NMMAP;
492
493 fprintf(stderr, "aplay: Playing '%s': format %s ch = %d\n",
494 fn, get_format_desc(format), ch );
495 return play_file(rate, ch, fd, flag, device, 0);
496}
497
498int play_wav(const char *fg, int rate, int ch, const char *device, const char *fn)
499{
500 struct wav_header hdr;
501 int fd;
502 unsigned flag = 0;
503
504 if (pcm_flag) {
505 if(!fn) {
506 fd = fileno(stdin);
507 piped = 1;
508 } else {
509 fd = open(fn, O_RDONLY);
510 if (fd < 0) {
511 fprintf(stderr, "Aplay:aplay: cannot open '%s'\n", fn);
512 return fd;
513 }
514 }
515 if (compressed) {
516 hdr.sample_rate = rate;
517 hdr.num_channels = ch;
518 hdr.data_sz = 0;
519 goto ignore_header;
520 }
521
522 if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
523 fprintf(stderr, "Aplay:aplay: cannot read header\n");
524 return -errno;
525 }
526
527 if ((hdr.riff_id != ID_RIFF) ||
528 (hdr.riff_fmt != ID_WAVE) ||
529 (hdr.fmt_id != ID_FMT)) {
530 fprintf(stderr, "Aplay:aplay: '%s' is not a riff/wave file\n", fn);
531 return -EINVAL;
532 }
533 if ((hdr.audio_format != FORMAT_PCM) ||
534 (hdr.fmt_sz != 16)) {
535 fprintf(stderr, "Aplay:aplay: '%s' is not pcm format\n", fn);
536 return -EINVAL;
537 }
538 if (hdr.bits_per_sample != 16) {
539 fprintf(stderr, "Aplay:aplay: '%s' is not 16bit per sample\n", fn);
540 return -EINVAL;
541 }
542 } else {
543 fd = -EBADFD;
544 hdr.sample_rate = rate;
545 hdr.num_channels = ch;
546 hdr.data_sz = 0;
547 }
548
549ignore_header:
550 if (!strncmp(fg, "M", sizeof("M")))
551 flag = PCM_MMAP;
552 else if (!strncmp(fg, "N", sizeof("N")))
553 flag = PCM_NMMAP;
554 fprintf(stderr, "aplay: Playing '%s':%s\n", fn, get_format_desc(format) );
555
556 return play_file(hdr.sample_rate, hdr.num_channels, fd, flag, device, hdr.data_sz);
557}
558
559int main(int argc, char **argv)
560{
561 int option_index = 0;
562 int c,i;
563 int ch = 2;
564 int rate = 44100;
565 char *mmap = "N";
566 char *device = "hw:0,0";
567 char *filename;
568 int rc = 0;
569
570 if (argc <2) {
571 printf("\nUsage: aplay [options] <file>\n"
572 "options:\n"
573 "-D <hw:C,D> -- Alsa PCM by name\n"
574 "-M -- Mmap stream\n"
575 "-P -- Hostless steam[No PCM]\n"
576 "-C -- Channels\n"
577 "-R -- Rate\n"
578 "-V -- verbose\n"
579 "-F -- Format\n"
580 "-B -- Period\n"
581 "-T <MP3, AAC, AC3_PASS_THROUGH> -- Compressed\n"
582 "<file> \n");
583 fprintf(stderr, "Formats Supported:\n");
584 for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; ++i)
585 if (get_format_name(i))
586 fprintf(stderr, "%s ", get_format_name(i));
587 fprintf(stderr, "\nSome of these may not be available on selected hardware\n");
588 return 0;
589 }
590 while ((c = getopt_long(argc, argv, "PVMD:R:C:F:B:T:", long_options, &option_index)) != -1) {
591 switch (c) {
592 case 'P':
593 pcm_flag = 0;
594 break;
595 case 'V':
596 debug = 1;
597 break;
598 case 'M':
599 mmap = "M";
600 break;
601 case 'D':
602 device = optarg;
603 break;
604 case 'R':
605 rate = (int)strtol(optarg, NULL, 0);
606 break;
607 case 'C':
608 ch = (int)strtol(optarg, NULL, 0);
609 break;
610 case 'F':
611 printf("optarg = %s\n", optarg);
612 format = get_format(optarg);
613 break;
614 case 'B':
615 period = (int)strtol(optarg, NULL, 0);
616 break;
617 case 'T':
618 compressed = 1;
619 printf("compressed codec type requested = %s\n", optarg);
620 compr_codec = optarg;
621 break;
622 default:
623 printf("\nUsage: aplay [options] <file>\n"
624 "options:\n"
625 "-D <hw:C,D> -- Alsa PCM by name\n"
626 "-M -- Mmap stream\n"
627 "-P -- Hostless steam[No PCM]\n"
628 "-V -- verbose\n"
629 "-C -- Channels\n"
630 "-R -- Rate\n"
631 "-F -- Format\n"
632 "-B -- Period\n"
633 "-T -- Compressed\n"
634 "<file> \n");
635 fprintf(stderr, "Formats Supported:\n");
636 for (i = 0; i < SNDRV_PCM_FORMAT_LAST; ++i)
637 if (get_format_name(i))
638 fprintf(stderr, "%s ", get_format_name(i));
639 fprintf(stderr, "\nSome of these may not be available on selected hardware\n");
640 return -EINVAL;
641 }
642
643 }
644 filename = (char*) calloc(1, 30);
645 if (!filename) {
646 fprintf(stderr, "Aplay:Failed to allocate filename!");
647 return -ENOMEM;
648 }
649 if (optind > argc - 1) {
650 free(filename);
651 filename = NULL;
652 } else {
653 strlcpy(filename, argv[optind++], 30);
654 }
655
656 if (pcm_flag) {
657 if (format == SNDRV_PCM_FORMAT_S16_LE)
658 rc = play_wav(mmap, rate, ch, device, filename);
659 else
660 rc = play_raw(mmap, rate, ch, device, filename);
661 } else {
662 rc = play_wav(mmap, rate, ch, device, "dummy");
663 }
664 if (filename)
665 free(filename);
666
667 return rc;
668}
669