blob: 4a3897f378ebfb8c29197c21bdb3208aae0ab854 [file] [log] [blame]
Iliyan Malchev4765c432012-06-11 14:36:16 -07001/*
2** Copyright 2010, The Android Open-Source Project
Duy Truongeb337332013-01-17 10:33:22 -08003** Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Iliyan Malchev4765c432012-06-11 14:36:16 -07004**
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>
dhacker2994db5552013-04-26 21:32:42 -050030#ifdef QCOM_COMPRESSED_AUDIO_ENABLED
Mingming Yinbbd94ad2012-11-29 20:04:36 -080031#include <sound/compress_params.h>
32#include <sound/compress_offload.h>
dhacker2994db5552013-04-26 21:32:42 -050033#endif
Iliyan Malchev4765c432012-06-11 14:36:16 -070034#include "alsa_audio.h"
35
36#ifndef ANDROID
37#define strlcat g_strlcat
38#define strlcpy g_strlcpy
39#endif
40
41#define ID_RIFF 0x46464952
42#define ID_WAVE 0x45564157
43#define ID_FMT 0x20746d66
44#define ID_DATA 0x61746164
45
46#define FORMAT_PCM 1
47#define LOG_NDEBUG 1
Mingming Yinbbd94ad2012-11-29 20:04:36 -080048
49struct output_metadata_handle_t {
50 uint32_t metadataLength;
51 uint32_t bufferLength;
52 uint64_t timestamp;
53 uint32_t reserved[12];
54};
55
56static struct output_metadata_handle_t outputMetadataTunnel;
57
Iliyan Malchev4765c432012-06-11 14:36:16 -070058static pcm_flag = 1;
59static debug = 0;
60static uint32_t play_max_sz = 2147483648LL;
61static int format = SNDRV_PCM_FORMAT_S16_LE;
62static int period = 0;
63static int compressed = 0;
Mingming Yinbbd94ad2012-11-29 20:04:36 -080064static int set_channel_map = 0;
65static char channel_map[8];
Iliyan Malchev4765c432012-06-11 14:36:16 -070066static char *compr_codec;
67static int piped = 0;
Mingming Yinbbd94ad2012-11-29 20:04:36 -080068static int outputMetadataLength = 0;
69static int eosSet = 0;
Iliyan Malchev4765c432012-06-11 14:36:16 -070070
71static struct option long_options[] =
72{
73 {"pcm", 0, 0, 'P'},
74 {"debug", 0, 0, 'V'},
75 {"Mmap", 0, 0, 'M'},
76 {"HW", 1, 0, 'D'},
77 {"Rate", 1, 0, 'R'},
78 {"channel", 1, 0, 'C'},
79 {"format", 1, 0, 'F'},
80 {"period", 1, 0, 'B'},
81 {"compressed", 0, 0, 'T'},
Mingming Yinbbd94ad2012-11-29 20:04:36 -080082 {"channelMap", 0, 0, 'X'},
Iliyan Malchev4765c432012-06-11 14:36:16 -070083 {0, 0, 0, 0}
84};
85
86struct wav_header {
87 uint32_t riff_id;
88 uint32_t riff_sz;
89 uint32_t riff_fmt;
90 uint32_t fmt_id;
91 uint32_t fmt_sz;
92 uint16_t audio_format;
93 uint16_t num_channels;
94 uint32_t sample_rate;
95 uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */
96 uint16_t block_align; /* num_channels * bps / 8 */
97 uint16_t bits_per_sample;
98 uint32_t data_id;
99 uint32_t data_sz;
100};
101
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800102
103void updateMetaData(size_t bytes) {
104 outputMetadataTunnel.metadataLength = sizeof(outputMetadataTunnel);
105 outputMetadataTunnel.timestamp = 0;
106 outputMetadataTunnel.bufferLength = bytes;
107 fprintf(stderr, "bytes = %d\n", bytes);
108}
Iliyan Malchev4765c432012-06-11 14:36:16 -0700109static int set_params(struct pcm *pcm)
110{
111 struct snd_pcm_hw_params *params;
112 struct snd_pcm_sw_params *sparams;
113
114 unsigned long periodSize, bufferSize, reqBuffSize;
115 unsigned int periodTime, bufferTime;
116 unsigned int requestedRate = pcm->rate;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800117 int channels;
118 if(pcm->flags & PCM_MONO)
119 channels = 1;
120 else if(pcm->flags & PCM_QUAD)
121 channels = 4;
122 else if(pcm->flags & PCM_5POINT1)
123 channels = 6;
124 else if(pcm->flags & PCM_7POINT1)
125 channels = 8;
126 else
127 channels = 2;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700128
129 params = (struct snd_pcm_hw_params*) calloc(1, sizeof(struct snd_pcm_hw_params));
130 if (!params) {
131 fprintf(stderr, "Aplay:Failed to allocate ALSA hardware parameters!");
132 return -ENOMEM;
133 }
134
135 param_init(params);
136
137 param_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS,
138 (pcm->flags & PCM_MMAP)? SNDRV_PCM_ACCESS_MMAP_INTERLEAVED : SNDRV_PCM_ACCESS_RW_INTERLEAVED);
139 param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, pcm->format);
140 param_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
141 SNDRV_PCM_SUBFORMAT_STD);
142 if (period)
143 param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, period);
144 else
145 param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 10);
146 param_set_int(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16);
147 param_set_int(params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
148 pcm->channels * 16);
149 param_set_int(params, SNDRV_PCM_HW_PARAM_CHANNELS,
150 pcm->channels);
151 param_set_int(params, SNDRV_PCM_HW_PARAM_RATE, pcm->rate);
152 param_set_hw_refine(pcm, params);
153
154 if (param_set_hw_params(pcm, params)) {
155 fprintf(stderr, "Aplay:cannot set hw params\n");
156 return -errno;
157 }
158 if (debug)
159 param_dump(params);
160
161 pcm->buffer_size = pcm_buffer_size(params);
162 pcm->period_size = pcm_period_size(params);
163 pcm->period_cnt = pcm->buffer_size/pcm->period_size;
164 if (debug) {
165 fprintf (stderr,"period_cnt = %d\n", pcm->period_cnt);
166 fprintf (stderr,"period_size = %d\n", pcm->period_size);
167 fprintf (stderr,"buffer_size = %d\n", pcm->buffer_size);
168 }
169 sparams = (struct snd_pcm_sw_params*) calloc(1, sizeof(struct snd_pcm_sw_params));
170 if (!sparams) {
171 fprintf(stderr, "Aplay:Failed to allocate ALSA software parameters!\n");
172 return -ENOMEM;
173 }
174 // Get the current software parameters
175 sparams->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
176 sparams->period_step = 1;
177
178 sparams->avail_min = pcm->period_size/(channels * 2) ;
179 sparams->start_threshold = pcm->period_size/(channels * 2) ;
180 sparams->stop_threshold = pcm->buffer_size ;
181 sparams->xfer_align = pcm->period_size/(channels * 2) ; /* needed for old kernels */
182
183 sparams->silence_size = 0;
184 sparams->silence_threshold = 0;
185
186 if (param_set_sw_params(pcm, sparams)) {
187 fprintf(stderr, "Aplay:cannot set sw params");
188 return -errno;
189 }
190 if (debug) {
191 fprintf (stderr,"sparams->avail_min= %lu\n", sparams->avail_min);
192 fprintf (stderr," sparams->start_threshold= %lu\n", sparams->start_threshold);
193 fprintf (stderr," sparams->stop_threshold= %lu\n", sparams->stop_threshold);
194 fprintf (stderr," sparams->xfer_align= %lu\n", sparams->xfer_align);
195 fprintf (stderr," sparams->boundary= %lu\n", sparams->boundary);
196 }
197 return 0;
198}
199
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800200void send_channel_map_driver(struct pcm *pcm)
201{
202 int i, ret;
203 struct mixer *mixer;
204 const char* device = "/dev/snd/controlC0";
205
206 mixer = mixer_open(device);
207 if (!mixer) {
208 fprintf(stderr,"oops: %s: %d\n", strerror(errno), __LINE__);
209 return;
210 }
211 ret = pcm_set_channel_map(pcm, mixer, 8, channel_map);
212 if (ret < 0)
213 fprintf(stderr, "could not set channel mask\n");
214 mixer_close(mixer);
215
216 return;
217}
218
Iliyan Malchev4765c432012-06-11 14:36:16 -0700219static int play_file(unsigned rate, unsigned channels, int fd,
220 unsigned flags, const char *device, unsigned data_sz)
221{
222 struct pcm *pcm;
223 struct mixer *mixer;
224 struct pcm_ctl *ctl = NULL;
225 unsigned bufsize;
226 char *data;
227 long avail;
228 long frames;
229 int nfds = 1;
230 struct snd_xferi x;
231 unsigned offset = 0;
232 int err;
233 static int start = 0;
234 struct pollfd pfd[1];
235 int remainingData = 0;
236
237 flags |= PCM_OUT;
238
239 if (channels == 1)
240 flags |= PCM_MONO;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800241 else if (channels == 4)
242 flags |= PCM_QUAD;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700243 else if (channels == 6)
244 flags |= PCM_5POINT1;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800245 else if (channels == 8)
246 flags |= PCM_7POINT1;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700247 else
248 flags |= PCM_STEREO;
249
250 if (debug)
251 flags |= DEBUG_ON;
252 else
253 flags |= DEBUG_OFF;
254
255 pcm = pcm_open(flags, device);
256 if (pcm < 0)
257 return pcm;
258
259 if (!pcm_ready(pcm)) {
260 pcm_close(pcm);
261 return -EBADFD;
262 }
263
264 if (compressed) {
265 struct snd_compr_caps compr_cap;
266 struct snd_compr_params compr_params;
267 if (ioctl(pcm->fd, SNDRV_COMPRESS_GET_CAPS, &compr_cap)) {
268 fprintf(stderr, "Aplay: SNDRV_COMPRESS_GET_CAPS, failed Error no %d \n", errno);
269 pcm_close(pcm);
270 return -errno;
271 }
272 if (!period)
273 period = compr_cap.min_fragment_size;
274 switch (get_compressed_format(compr_codec)) {
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800275 case SND_AUDIOCODEC_MP3:
276 compr_params.codec.id = SND_AUDIOCODEC_MP3;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700277 break;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800278 case SND_AUDIOCODEC_AC3_PASS_THROUGH:
279 compr_params.codec.id = SND_AUDIOCODEC_AC3_PASS_THROUGH;
280 printf("codec -d = %x\n", SND_AUDIOCODEC_AC3_PASS_THROUGH);
281 break;
282 case SND_AUDIOCODEC_AAC:
283 compr_params.codec.id = SND_AUDIOCODEC_AAC;
284 printf("codec -d = %x\n", SND_AUDIOCODEC_AAC);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700285 break;
286 default:
287 break;
288 }
289 if (ioctl(pcm->fd, SNDRV_COMPRESS_SET_PARAMS, &compr_params)) {
290 fprintf(stderr, "Aplay: SNDRV_COMPRESS_SET_PARAMS,failed Error no %d \n", errno);
291 pcm_close(pcm);
292 return -errno;
293 }
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800294 outputMetadataLength = sizeof(struct output_metadata_handle_t);
295 } else if (channels > 2) {
296 if(set_channel_map) {
297 send_channel_map_driver(pcm);
298 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700299 }
300 pcm->channels = channels;
301 pcm->rate = rate;
302 pcm->flags = flags;
303 pcm->format = format;
304 if (set_params(pcm)) {
305 fprintf(stderr, "Aplay:params setting failed\n");
306 pcm_close(pcm);
307 return -errno;
308 }
309
310 if (!pcm_flag) {
311 if (pcm_prepare(pcm)) {
312 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
313 pcm_close(pcm);
314 return -errno;
315 }
316 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
317 fprintf(stderr, "Aplay: Hostless IOCTL_START Error no %d \n", errno);
318 pcm_close(pcm);
319 return -errno;
320 }
321 while(1);
322 }
323
324 remainingData = data_sz;
325
326 if (flags & PCM_MMAP) {
327 u_int8_t *dst_addr = NULL;
328 struct snd_pcm_sync_ptr *sync_ptr1 = pcm->sync_ptr;
329 if (mmap_buffer(pcm)) {
330 fprintf(stderr, "Aplay:params setting failed\n");
331 pcm_close(pcm);
332 return -errno;
333 }
334 if (pcm_prepare(pcm)) {
335 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
336 pcm_close(pcm);
337 return -errno;
338 }
339
340 bufsize = pcm->period_size;
341 if (debug)
342 fprintf(stderr, "Aplay:bufsize = %d\n", bufsize);
343
344 pfd[0].fd = pcm->timer_fd;
345 pfd[0].events = POLLIN;
346
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800347 frames = bufsize / (2*channels);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700348 for (;;) {
349 if (!pcm->running) {
350 if (pcm_prepare(pcm)) {
351 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
352 pcm_close(pcm);
353 return -errno;
354 }
355 pcm->running = 1;
356 start = 0;
357 }
358 /* Sync the current Application pointer from the kernel */
359 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;//SNDRV_PCM_SYNC_PTR_HWSYNC;
360 err = sync_ptr(pcm);
361 if (err == EPIPE) {
362 fprintf(stderr, "Aplay:Failed in sync_ptr \n");
363 /* we failed to make our window -- try to restart */
364 pcm->underruns++;
365 pcm->running = 0;
366 continue;
367 }
368 /*
369 * Check for the available buffer in driver. If available buffer is
370 * less than avail_min we need to wait
371 */
372 avail = pcm_avail(pcm);
373 if (avail < 0) {
374 fprintf(stderr, "Aplay:Failed in pcm_avail\n");
375 pcm_close(pcm);
376 return avail;
377 }
378 if (avail < pcm->sw_p->avail_min) {
379 poll(pfd, nfds, TIMEOUT_INFINITE);
380 continue;
381 }
382 /*
383 * Now that we have buffer size greater than avail_min available to
384 * to be written we need to calcutate the buffer offset where we can
385 * start writting.
386 */
387 dst_addr = dst_address(pcm);
388
389 if (debug) {
390 fprintf(stderr, "dst_addr = 0x%08x\n", dst_addr);
391 fprintf(stderr, "Aplay:avail = %d frames = %d\n",avail, frames);
392 fprintf(stderr, "Aplay:sync_ptr->s.status.hw_ptr %ld pcm->buffer_size %d sync_ptr->c.control.appl_ptr %ld\n",
393 pcm->sync_ptr->s.status.hw_ptr,
394 pcm->buffer_size,
395 pcm->sync_ptr->c.control.appl_ptr);
396 }
397
398 /*
399 * Read from the file to the destination buffer in kernel mmaped buffer
400 * This reduces a extra copy of intermediate buffer.
401 */
402 memset(dst_addr, 0x0, bufsize);
403
404 if (data_sz && !piped) {
405 if (remainingData < bufsize) {
406 bufsize = remainingData;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800407 frames = remainingData / (2*channels);
Iliyan Malchev4765c432012-06-11 14:36:16 -0700408 }
409 }
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800410 fprintf(stderr, "addr = %d, size = %d \n", (dst_addr + outputMetadataLength),(bufsize - outputMetadataLength));
411 err = read(fd, (dst_addr + outputMetadataLength) , (bufsize - outputMetadataLength));
412 if(compressed) {
413 updateMetaData(err);
414 memcpy(dst_addr, &outputMetadataTunnel, outputMetadataLength);
415 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700416
Iliyan Malchev4765c432012-06-11 14:36:16 -0700417 if (debug)
418 fprintf(stderr, "read %d bytes from file\n", err);
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800419 if (err <= 0 ) {
420 fprintf(stderr," EOS set\n ");
421 eosSet = 1;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700422 break;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800423 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700424 if (data_sz && !piped) {
425 remainingData -= bufsize;
426 if (remainingData <= 0)
427 break;
428 }
429
430 /*
431 * Increment the application pointer with data written to kernel.
432 * Update kernel with the new sync pointer.
433 */
434 pcm->sync_ptr->c.control.appl_ptr += frames;
435 pcm->sync_ptr->flags = 0;
436
437 err = sync_ptr(pcm);
438 if (err == EPIPE) {
439 fprintf(stderr, "Aplay:Failed in sync_ptr 2 \n");
440 /* we failed to make our window -- try to restart */
441 pcm->underruns++;
442 pcm->running = 0;
443 continue;
444 }
445
446 if (debug) {
447 fprintf(stderr, "Aplay:sync_ptr->s.status.hw_ptr %ld sync_ptr->c.control.appl_ptr %ld\n",
448 pcm->sync_ptr->s.status.hw_ptr,
449 pcm->sync_ptr->c.control.appl_ptr);
450 if (compressed && start) {
451 struct snd_compr_tstamp tstamp;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800452 if (ioctl(pcm->fd, SNDRV_COMPRESS_TSTAMP, &tstamp))
453 fprintf(stderr, "Aplay: failed SNDRV_COMPRESS_TSTAMP\n");
Iliyan Malchev4765c432012-06-11 14:36:16 -0700454 else
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800455 fprintf(stderr, "timestamp = %lld\n", tstamp.timestamp);
456 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700457 }
458 /*
459 * If we have reached start threshold of buffer prefill,
460 * its time to start the driver.
461 */
462 if(start)
463 goto start_done;
464 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
465 err = -errno;
466 if (errno == EPIPE) {
467 fprintf(stderr, "Aplay:Failed in SNDRV_PCM_IOCTL_START\n");
468 /* we failed to make our window -- try to restart */
469 pcm->underruns++;
470 pcm->running = 0;
471 continue;
472 } else {
473 fprintf(stderr, "Aplay:Error no %d \n", errno);
474 pcm_close(pcm);
475 return -errno;
476 }
477 } else
478 start = 1;
479
480start_done:
481 offset += frames;
482 }
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800483
Iliyan Malchev4765c432012-06-11 14:36:16 -0700484 while(1) {
485 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;//SNDRV_PCM_SYNC_PTR_HWSYNC;
486 sync_ptr(pcm);
487 /*
488 * Check for the available buffer in driver. If available buffer is
489 * less than avail_min we need to wait
490 */
491 if (pcm->sync_ptr->s.status.hw_ptr >= pcm->sync_ptr->c.control.appl_ptr) {
492 fprintf(stderr, "Aplay:sync_ptr->s.status.hw_ptr %ld sync_ptr->c.control.appl_ptr %ld\n",
493 pcm->sync_ptr->s.status.hw_ptr,
494 pcm->sync_ptr->c.control.appl_ptr);
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800495
496 if(compressed && eosSet) {
497 fprintf(stderr,"Audio Drain DONE ++\n");
498 if ( ioctl(pcm->fd, SNDRV_COMPRESS_DRAIN) < 0 ) {
499 fprintf(stderr,"Audio Drain failed\n");
500 }
501 fprintf(stderr,"Audio Drain DONE --\n");
502 }
Iliyan Malchev4765c432012-06-11 14:36:16 -0700503 break;
504 } else
505 poll(pfd, nfds, TIMEOUT_INFINITE);
506 }
507 } else {
508 if (pcm_prepare(pcm)) {
509 fprintf(stderr, "Aplay:Failed in pcm_prepare\n");
510 pcm_close(pcm);
511 return -errno;
512 }
513
514 bufsize = pcm->period_size;
515
516 data = calloc(1, bufsize);
517 if (!data) {
518 fprintf(stderr, "Aplay:could not allocate %d bytes\n", bufsize);
519 pcm_close(pcm);
520 return -ENOMEM;
521 }
522
523 if (data_sz && !piped) {
524 if (remainingData < bufsize)
525 bufsize = remainingData;
526 }
527
528 while (read(fd, data, bufsize) > 0) {
529 if (pcm_write(pcm, data, bufsize)){
530 fprintf(stderr, "Aplay: pcm_write failed\n");
531 free(data);
532 pcm_close(pcm);
533 return -errno;
534 }
535 memset(data, 0, bufsize);
536
537 if (data_sz && !piped) {
538 remainingData -= bufsize;
539 if (remainingData <= 0)
540 break;
541 if (remainingData < bufsize)
542 bufsize = remainingData;
543 }
544 }
545 free(data);
546 }
547 fprintf(stderr, "Aplay: Done playing\n");
548 pcm_close(pcm);
549 return 0;
550}
551
552int play_raw(const char *fg, int rate, int ch, const char *device, const char *fn)
553{
554 int fd;
555 unsigned flag = 0;
556
557 if(!fn) {
558 fd = fileno(stdin);
559 piped = 1;
560 } else {
561 fd = open(fn, O_RDONLY);
562 if (fd < 0) {
563 fprintf(stderr, "Aplay:aplay: cannot open '%s'\n", fn);
564 return fd;
565 }
566 }
567
568 if (!strncmp(fg, "M", sizeof("M")))
569 flag = PCM_MMAP;
570 else if (!strncmp(fg, "N", sizeof("N")))
571 flag = PCM_NMMAP;
572
573 fprintf(stderr, "aplay: Playing '%s': format %s ch = %d\n",
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800574 fn, get_format_desc(format), ch );
Iliyan Malchev4765c432012-06-11 14:36:16 -0700575 return play_file(rate, ch, fd, flag, device, 0);
576}
577
578int play_wav(const char *fg, int rate, int ch, const char *device, const char *fn)
579{
580 struct wav_header hdr;
581 int fd;
582 unsigned flag = 0;
583
584 if (pcm_flag) {
585 if(!fn) {
586 fd = fileno(stdin);
587 piped = 1;
588 } else {
589 fd = open(fn, O_RDONLY);
590 if (fd < 0) {
591 fprintf(stderr, "Aplay:aplay: cannot open '%s'\n", fn);
592 return fd;
593 }
594 }
595 if (compressed) {
596 hdr.sample_rate = rate;
597 hdr.num_channels = ch;
598 hdr.data_sz = 0;
599 goto ignore_header;
600 }
601
602 if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
603 fprintf(stderr, "Aplay:aplay: cannot read header\n");
604 return -errno;
605 }
606
607 if ((hdr.riff_id != ID_RIFF) ||
608 (hdr.riff_fmt != ID_WAVE) ||
609 (hdr.fmt_id != ID_FMT)) {
610 fprintf(stderr, "Aplay:aplay: '%s' is not a riff/wave file\n", fn);
611 return -EINVAL;
612 }
613 if ((hdr.audio_format != FORMAT_PCM) ||
614 (hdr.fmt_sz != 16)) {
615 fprintf(stderr, "Aplay:aplay: '%s' is not pcm format\n", fn);
616 return -EINVAL;
617 }
618 if (hdr.bits_per_sample != 16) {
619 fprintf(stderr, "Aplay:aplay: '%s' is not 16bit per sample\n", fn);
620 return -EINVAL;
621 }
622 } else {
623 fd = -EBADFD;
624 hdr.sample_rate = rate;
625 hdr.num_channels = ch;
626 hdr.data_sz = 0;
627 }
628
629ignore_header:
630 if (!strncmp(fg, "M", sizeof("M")))
631 flag = PCM_MMAP;
632 else if (!strncmp(fg, "N", sizeof("N")))
633 flag = PCM_NMMAP;
634 fprintf(stderr, "aplay: Playing '%s':%s\n", fn, get_format_desc(format) );
635
636 return play_file(hdr.sample_rate, hdr.num_channels, fd, flag, device, hdr.data_sz);
637}
638
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800639char get_channel_map_val(char *string)
640{
641 char retval = 0;
642 if( !strncmp(string, "RRC", sizeof(string)) )
643 retval = 16;
644 else if( !strncmp(string, "RLC", sizeof(string)) )
645 retval = 15;
646 else if( !strncmp(string, "FRC", sizeof(string)) )
647 retval = 14;
648 else if( !strncmp(string, "FLC", sizeof(string)) )
649 retval = 13;
650 else if( !strncmp(string, "MS", sizeof(string)) )
651 retval = 12;
652 else if( !strncmp(string, "CVH", sizeof(string)) )
653 retval = 11;
654 else if( !strncmp(string, "TS", sizeof(string)) )
655 retval = 10;
656 else if( !strncmp(string, "RB", sizeof(string)) )
657 retval = 9;
658 else if( !strncmp(string, "LB", sizeof(string)) )
659 retval = 8;
660 else if( !strncmp(string, "CS", sizeof(string)) )
661 retval = 7;
662 else if( !strncmp(string, "LFE", sizeof(string)) )
663 retval = 6;
664 else if( !strncmp(string, "RS", sizeof(string)) )
665 retval = 5;
666 else if( !strncmp(string, "LS", sizeof(string)) )
667 retval = 4;
668 else if( !strncmp(string, "FC", sizeof(string)) )
669 retval = 3;
670 else if( !strncmp(string, "FR", sizeof(string)) )
671 retval = 2;
672 else if( !strncmp(string, "FL", sizeof(string)) )
673 retval = 1;
674
675 return retval;
676}
677
Iliyan Malchev4765c432012-06-11 14:36:16 -0700678int main(int argc, char **argv)
679{
680 int option_index = 0;
681 int c,i;
682 int ch = 2;
683 int rate = 44100;
684 char *mmap = "N";
685 char *device = "hw:0,0";
686 char *filename;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800687 char *ptr;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700688 int rc = 0;
689
690 if (argc <2) {
691 printf("\nUsage: aplay [options] <file>\n"
692 "options:\n"
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800693 "-D <hw:C,D> -- Alsa PCM by name\n"
694 "-M -- Mmap stream\n"
695 "-P -- Hostless steam[No PCM]\n"
696 "-C -- Channels\n"
697 "-R -- Rate\n"
698 "-V -- verbose\n"
699 "-F -- Format\n"
Iliyan Malchev4765c432012-06-11 14:36:16 -0700700 "-B -- Period\n"
701 "-T <MP3, AAC, AC3_PASS_THROUGH> -- Compressed\n"
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800702 "-X <\"FL,FR,FC,Ls,Rs,LFE\" for 5.1 configuration\n"
703 " supported channels: \n"
704 " FL, FR, FC, LS, RS, LFE, CS, TS \n"
705 " LB, RB, FLC, FRC, RLC, RRC, CVH, MS\n"
Iliyan Malchev4765c432012-06-11 14:36:16 -0700706 "<file> \n");
707 fprintf(stderr, "Formats Supported:\n");
708 for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; ++i)
709 if (get_format_name(i))
710 fprintf(stderr, "%s ", get_format_name(i));
711 fprintf(stderr, "\nSome of these may not be available on selected hardware\n");
712 return 0;
713 }
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800714 while ((c = getopt_long(argc, argv, "PVMD:R:C:F:B:T:X:", long_options, &option_index)) != -1) {
Iliyan Malchev4765c432012-06-11 14:36:16 -0700715 switch (c) {
716 case 'P':
717 pcm_flag = 0;
718 break;
719 case 'V':
720 debug = 1;
721 break;
722 case 'M':
723 mmap = "M";
724 break;
725 case 'D':
726 device = optarg;
727 break;
728 case 'R':
729 rate = (int)strtol(optarg, NULL, 0);
730 break;
731 case 'C':
732 ch = (int)strtol(optarg, NULL, 0);
733 break;
734 case 'F':
735 printf("optarg = %s\n", optarg);
736 format = get_format(optarg);
737 break;
738 case 'B':
739 period = (int)strtol(optarg, NULL, 0);
740 break;
741 case 'T':
742 compressed = 1;
743 printf("compressed codec type requested = %s\n", optarg);
744 compr_codec = optarg;
745 break;
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800746 case 'X':
747 set_channel_map = 1; i = 0;
748 memset(channel_map, 0, sizeof(channel_map));
749 ptr = strtok(optarg, ",");
750 while((ptr != NULL) && (i < sizeof(channel_map))) {
751 channel_map[i] = get_channel_map_val(ptr);
752 if (channel_map[i] < 0 || channel_map[i] > 16) {
753 set_channel_map = 0;
754 break;
755 }
756 ptr = strtok(NULL,","); i++;
757 }
758 break;
Iliyan Malchev4765c432012-06-11 14:36:16 -0700759 default:
760 printf("\nUsage: aplay [options] <file>\n"
761 "options:\n"
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800762 "-D <hw:C,D> -- Alsa PCM by name\n"
763 "-M -- Mmap stream\n"
764 "-P -- Hostless steam[No PCM]\n"
765 "-V -- verbose\n"
766 "-C -- Channels\n"
767 "-R -- Rate\n"
768 "-F -- Format\n"
Iliyan Malchev4765c432012-06-11 14:36:16 -0700769 "-B -- Period\n"
770 "-T -- Compressed\n"
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800771 "-X <\"FL,FR,FC,Ls,Rs,LFE\" for 5.1 configuration\n"
772 " supported channels: \n"
773 " FL, FR, FC, LS, RS, LFE, CS, TS \n"
774 " LB, RB, FLC, FRC, RLC, RRC, CVH, MS\n"
Iliyan Malchev4765c432012-06-11 14:36:16 -0700775 "<file> \n");
776 fprintf(stderr, "Formats Supported:\n");
777 for (i = 0; i < SNDRV_PCM_FORMAT_LAST; ++i)
778 if (get_format_name(i))
779 fprintf(stderr, "%s ", get_format_name(i));
780 fprintf(stderr, "\nSome of these may not be available on selected hardware\n");
781 return -EINVAL;
782 }
783
784 }
785 filename = (char*) calloc(1, 30);
786 if (!filename) {
787 fprintf(stderr, "Aplay:Failed to allocate filename!");
788 return -ENOMEM;
789 }
790 if (optind > argc - 1) {
791 free(filename);
792 filename = NULL;
793 } else {
794 strlcpy(filename, argv[optind++], 30);
795 }
796
797 if (pcm_flag) {
Mingming Yinbbd94ad2012-11-29 20:04:36 -0800798 if (format == SNDRV_PCM_FORMAT_S16_LE)
Iliyan Malchev4765c432012-06-11 14:36:16 -0700799 rc = play_wav(mmap, rate, ch, device, filename);
800 else
801 rc = play_raw(mmap, rate, ch, device, filename);
802 } else {
803 rc = play_wav(mmap, rate, ch, device, "dummy");
804 }
805 if (filename)
806 free(filename);
807
808 return rc;
809}
810