Home | History | Annotate | Download | only in tinycompress
      1 /*
      2  * BSD LICENSE
      3  *
      4  * tinyplay command line player for compress audio offload in alsa
      5  * Copyright (c) 2011-2012, Intel Corporation
      6  * All rights reserved.
      7  *
      8  * Author: Vinod Koul <vinod.koul (at) linux.intel.com>
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions are met:
     12  *
     13  * Redistributions of source code must retain the above copyright notice,
     14  * this list of conditions and the following disclaimer.
     15  * Redistributions in binary form must reproduce the above copyright notice,
     16  * this list of conditions and the following disclaimer in the documentation
     17  * and/or other materials provided with the distribution.
     18  * Neither the name of Intel Corporation nor the names of its contributors
     19  * may be used to endorse or promote products derived from this software
     20  * without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     32  * THE POSSIBILITY OF SUCH DAMAGE.
     33  *
     34  * LGPL LICENSE
     35  *
     36  * tinyplay command line player for compress audio offload in alsa
     37  * Copyright (c) 2011-2012, Intel Corporation.
     38  *
     39  * This program is free software; you can redistribute it and/or modify it
     40  * under the terms and conditions of the GNU Lesser General Public License,
     41  * version 2.1, as published by the Free Software Foundation.
     42  *
     43  * This program is distributed in the hope it will be useful, but WITHOUT
     44  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     45  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
     46  * License for more details.
     47  *
     48  * You should have received a copy of the GNU Lesser General Public License
     49  * along with this program; if not, write to
     50  * the Free Software Foundation, Inc.,
     51  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
     52  */
     53 
     54 #include <stdint.h>
     55 #include <linux/types.h>
     56 #include <fcntl.h>
     57 #include <errno.h>
     58 #include <unistd.h>
     59 #include <stdio.h>
     60 #include <stdlib.h>
     61 #include <string.h>
     62 #include <signal.h>
     63 #include <stdbool.h>
     64 #include <getopt.h>
     65 #include <sys/time.h>
     66 #define __force
     67 #define __bitwise
     68 #define __user
     69 #include "sound/compress_params.h"
     70 #include "tinycompress/tinycompress.h"
     71 #include "tinycompress/tinymp3.h"
     72 
     73 static int verbose;
     74 
     75 static void usage(void)
     76 {
     77 	fprintf(stderr, "usage: cplay [OPTIONS] filename\n"
     78 		"-c\tcard number\n"
     79 		"-d\tdevice node\n"
     80 		"-b\tbuffer size\n"
     81 		"-f\tfragments\n\n"
     82 		"-v\tverbose mode\n"
     83 		"-h\tPrints this help list\n\n"
     84 		"Example:\n"
     85 		"\tcplay -c 1 -d 2 test.mp3\n"
     86 		"\tcplay -f 5 test.mp3\n");
     87 
     88 	exit(EXIT_FAILURE);
     89 }
     90 
     91 void play_samples(char *name, unsigned int card, unsigned int device,
     92 		unsigned long buffer_size, unsigned int frag);
     93 
     94 struct mp3_header {
     95 	uint16_t sync;
     96 	uint8_t format1;
     97 	uint8_t format2;
     98 };
     99 
    100 int parse_mp3_header(struct mp3_header *header, unsigned int *num_channels,
    101 		unsigned int *sample_rate, unsigned int *bit_rate)
    102 {
    103 	int ver_idx, mp3_version, layer, bit_rate_idx, sample_rate_idx, channel_idx;
    104 
    105 	/* check sync bits */
    106 	if ((header->sync & MP3_SYNC) != MP3_SYNC) {
    107 		fprintf(stderr, "Error: Can't find sync word\n");
    108 		return -1;
    109 	}
    110 	ver_idx = (header->sync >> 11) & 0x03;
    111 	mp3_version = ver_idx == 0 ? MPEG25 : ((ver_idx & 0x1) ? MPEG1 : MPEG2);
    112 	layer = 4 - ((header->sync >> 9) & 0x03);
    113 	bit_rate_idx = ((header->format1 >> 4) & 0x0f);
    114 	sample_rate_idx = ((header->format1 >> 2) & 0x03);
    115 	channel_idx = ((header->format2 >> 6) & 0x03);
    116 
    117 	if (sample_rate_idx == 3 || layer == 4 || bit_rate_idx == 15) {
    118 		fprintf(stderr, "Error: Can't find valid header\n");
    119 		return -1;
    120 	}
    121 	*num_channels = (channel_idx == MONO ? 1 : 2);
    122 	*sample_rate = mp3_sample_rates[mp3_version][sample_rate_idx];
    123 	*bit_rate = (mp3_bit_rates[mp3_version][layer - 1][bit_rate_idx]) * 1000;
    124 	if (verbose)
    125 		printf("%s: exit\n", __func__);
    126 	return 0;
    127 }
    128 
    129 int check_codec_format_supported(unsigned int card, unsigned int device, struct snd_codec *codec)
    130 {
    131 	if (is_codec_supported(card, device, COMPRESS_IN, codec) == false) {
    132 		fprintf(stderr, "Error: This codec or format is not supported by DSP\n");
    133 		return -1;
    134 	}
    135 	return 0;
    136 }
    137 
    138 static int print_time(struct compress *compress)
    139 {
    140 	unsigned int avail;
    141 	struct timespec tstamp;
    142 
    143 	if (compress_get_hpointer(compress, &avail, &tstamp) != 0) {
    144 		fprintf(stderr, "Error querying timestamp\n");
    145 		fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
    146 		return -1;
    147 	} else
    148 		fprintf(stderr, "DSP played %jd.%jd\n", (intmax_t)tstamp.tv_sec, (intmax_t)tstamp.tv_nsec*1000);
    149 	return 0;
    150 }
    151 
    152 int main(int argc, char **argv)
    153 {
    154 	char *file;
    155 	unsigned long buffer_size = 0;
    156 	int c;
    157 	unsigned int card = 0, device = 0, frag = 0;
    158 
    159 
    160 	if (argc < 2)
    161 		usage();
    162 
    163 	verbose = 0;
    164 	while ((c = getopt(argc, argv, "hvb:f:c:d:")) != -1) {
    165 		switch (c) {
    166 		case 'h':
    167 			usage();
    168 			break;
    169 		case 'b':
    170 			buffer_size = strtol(optarg, NULL, 0);
    171 			break;
    172 		case 'f':
    173 			frag = strtol(optarg, NULL, 10);
    174 			break;
    175 		case 'c':
    176 			card = strtol(optarg, NULL, 10);
    177 			break;
    178 		case 'd':
    179 			device = strtol(optarg, NULL, 10);
    180 			break;
    181 		case 'v':
    182 			verbose = 1;
    183 			break;
    184 		default:
    185 			exit(EXIT_FAILURE);
    186 		}
    187 	}
    188 	if (optind >= argc)
    189 		usage();
    190 
    191 	file = argv[optind];
    192 
    193 	play_samples(file, card, device, buffer_size, frag);
    194 
    195 	fprintf(stderr, "Finish Playing.... Close Normally\n");
    196 	exit(EXIT_SUCCESS);
    197 }
    198 
    199 void play_samples(char *name, unsigned int card, unsigned int device,
    200 		unsigned long buffer_size, unsigned int frag)
    201 {
    202 	struct compr_config config;
    203 	struct snd_codec codec;
    204 	struct compress *compress;
    205 	struct mp3_header header;
    206 	FILE *file;
    207 	char *buffer;
    208 	int size, num_read, wrote;
    209 	unsigned int channels, rate, bits;
    210 
    211 	if (verbose)
    212 		printf("%s: entry\n", __func__);
    213 	file = fopen(name, "rb");
    214 	if (!file) {
    215 		fprintf(stderr, "Unable to open file '%s'\n", name);
    216 		exit(EXIT_FAILURE);
    217 	}
    218 
    219 	fread(&header, sizeof(header), 1, file);
    220 
    221 	if (parse_mp3_header(&header, &channels, &rate, &bits) == -1) {
    222 		fclose(file);
    223 		exit(EXIT_FAILURE);
    224 	}
    225 
    226 	codec.id = SND_AUDIOCODEC_MP3;
    227 	codec.ch_in = channels;
    228 	codec.ch_out = channels;
    229 	codec.sample_rate = rate;
    230 	if (!codec.sample_rate) {
    231 		fprintf(stderr, "invalid sample rate %d\n", rate);
    232 		fclose(file);
    233 		exit(EXIT_FAILURE);
    234 	}
    235 	codec.bit_rate = bits;
    236 	codec.rate_control = 0;
    237 	codec.profile = 0;
    238 	codec.level = 0;
    239 	codec.ch_mode = 0;
    240 	codec.format = 0;
    241 	if ((buffer_size != 0) && (frag != 0)) {
    242 		config.fragment_size = buffer_size/frag;
    243 		config.fragments = frag;
    244 	} else {
    245 		/* use driver defaults */
    246 		config.fragment_size = 0;
    247 		config.fragments = 0;
    248 	}
    249 	config.codec = &codec;
    250 
    251 	compress = compress_open(card, device, COMPRESS_IN, &config);
    252 	if (!compress || !is_compress_ready(compress)) {
    253 		fprintf(stderr, "Unable to open Compress device %d:%d\n",
    254 				card, device);
    255 		fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
    256 		goto FILE_EXIT;
    257 	};
    258 	if (verbose)
    259 		printf("%s: Opened compress device\n", __func__);
    260 	size = config.fragment_size;
    261 	buffer = malloc(size * config.fragments);
    262 	if (!buffer) {
    263 		fprintf(stderr, "Unable to allocate %d bytes\n", size);
    264 		goto COMP_EXIT;
    265 	}
    266 
    267 	/* we will write frag fragment_size and then start */
    268 	num_read = fread(buffer, 1, size * config.fragments, file);
    269 	if (num_read > 0) {
    270 		if (verbose)
    271 			printf("%s: Doing first buffer write of %d\n", __func__, num_read);
    272 		wrote = compress_write(compress, buffer, num_read);
    273 		if (wrote < 0) {
    274 			fprintf(stderr, "Error %d playing sample\n", wrote);
    275 			fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
    276 			goto BUF_EXIT;
    277 		}
    278 		if (wrote != num_read) {
    279 			/* TODO: Buufer pointer needs to be set here */
    280 			fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote);
    281 		}
    282 	}
    283 	printf("Playing file %s On Card %u device %u, with buffer of %lu bytes\n",
    284 			name, card, device, buffer_size);
    285 	printf("Format %u Channels %u, %u Hz, Bit Rate %d\n",
    286 			SND_AUDIOCODEC_MP3, channels, rate, bits);
    287 
    288 	compress_start(compress);
    289 	if (verbose)
    290 		printf("%s: You should hear audio NOW!!!\n", __func__);
    291 
    292 	do {
    293 		num_read = fread(buffer, 1, size, file);
    294 		if (num_read > 0) {
    295 			wrote = compress_write(compress, buffer, num_read);
    296 			if (wrote < 0) {
    297 				fprintf(stderr, "Error playing sample\n");
    298 				fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
    299 				goto BUF_EXIT;
    300 			}
    301 			if (wrote != num_read) {
    302 				/* TODO: Buffer pointer needs to be set here */
    303 				fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote);
    304 			}
    305 			if (verbose) {
    306 				print_time(compress);
    307 				printf("%s: wrote %d\n", __func__, wrote);
    308 			}
    309 		}
    310 	} while (num_read > 0);
    311 
    312 	if (verbose)
    313 		printf("%s: exit success\n", __func__);
    314 	/* issue drain if it supports */
    315 	compress_drain(compress);
    316 	free(buffer);
    317 	fclose(file);
    318 	compress_close(compress);
    319 	return;
    320 BUF_EXIT:
    321 	free(buffer);
    322 COMP_EXIT:
    323 	compress_close(compress);
    324 FILE_EXIT:
    325 	fclose(file);
    326 	if (verbose)
    327 		printf("%s: exit failure\n", __func__);
    328 	exit(EXIT_FAILURE);
    329 }
    330 
    331