1 /* tinyplay.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 #include <tinyalsa/asoundlib.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <stdint.h> 33 #include <string.h> 34 #include <signal.h> 35 #include <endian.h> 36 37 #define ID_RIFF 0x46464952 38 #define ID_WAVE 0x45564157 39 #define ID_FMT 0x20746d66 40 #define ID_DATA 0x61746164 41 42 struct riff_wave_header { 43 uint32_t riff_id; 44 uint32_t riff_sz; 45 uint32_t wave_id; 46 }; 47 48 struct chunk_header { 49 uint32_t id; 50 uint32_t sz; 51 }; 52 53 struct chunk_fmt { 54 uint16_t audio_format; 55 uint16_t num_channels; 56 uint32_t sample_rate; 57 uint32_t byte_rate; 58 uint16_t block_align; 59 uint16_t bits_per_sample; 60 }; 61 62 static int close = 0; 63 64 void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, 65 unsigned int rate, unsigned int bits, unsigned int period_size, 66 unsigned int period_count, uint32_t data_sz); 67 68 void stream_close(int sig) 69 { 70 /* allow the stream to be closed gracefully */ 71 signal(sig, SIG_IGN); 72 close = 1; 73 } 74 75 int main(int argc, char **argv) 76 { 77 FILE *file; 78 struct riff_wave_header riff_wave_header; 79 struct chunk_header chunk_header; 80 struct chunk_fmt chunk_fmt; 81 unsigned int device = 0; 82 unsigned int card = 0; 83 unsigned int period_size = 1024; 84 unsigned int period_count = 4; 85 char *filename; 86 int more_chunks = 1; 87 88 if (argc < 2) { 89 fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]" 90 " [-n n_periods] \n", argv[0]); 91 return 1; 92 } 93 94 filename = argv[1]; 95 file = fopen(filename, "rb"); 96 if (!file) { 97 fprintf(stderr, "Unable to open file '%s'\n", filename); 98 return 1; 99 } 100 101 fread(&riff_wave_header, sizeof(riff_wave_header), 1, file); 102 if ((riff_wave_header.riff_id != ID_RIFF) || 103 (riff_wave_header.wave_id != ID_WAVE)) { 104 fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename); 105 fclose(file); 106 return 1; 107 } 108 109 do { 110 fread(&chunk_header, sizeof(chunk_header), 1, file); 111 112 switch (chunk_header.id) { 113 case ID_FMT: 114 fread(&chunk_fmt, sizeof(chunk_fmt), 1, file); 115 /* If the format header is larger, skip the rest */ 116 if (chunk_header.sz > sizeof(chunk_fmt)) 117 fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR); 118 break; 119 case ID_DATA: 120 /* Stop looking for chunks */ 121 more_chunks = 0; 122 chunk_header.sz = le32toh(chunk_header.sz); 123 break; 124 default: 125 /* Unknown chunk, skip bytes */ 126 fseek(file, chunk_header.sz, SEEK_CUR); 127 } 128 } while (more_chunks); 129 130 /* parse command line arguments */ 131 argv += 2; 132 while (*argv) { 133 if (strcmp(*argv, "-d") == 0) { 134 argv++; 135 if (*argv) 136 device = atoi(*argv); 137 } 138 if (strcmp(*argv, "-p") == 0) { 139 argv++; 140 if (*argv) 141 period_size = atoi(*argv); 142 } 143 if (strcmp(*argv, "-n") == 0) { 144 argv++; 145 if (*argv) 146 period_count = atoi(*argv); 147 } 148 if (strcmp(*argv, "-D") == 0) { 149 argv++; 150 if (*argv) 151 card = atoi(*argv); 152 } 153 if (*argv) 154 argv++; 155 } 156 157 play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate, 158 chunk_fmt.bits_per_sample, period_size, period_count, chunk_header.sz); 159 160 fclose(file); 161 162 return 0; 163 } 164 165 int check_param(struct pcm_params *params, unsigned int param, unsigned int value, 166 char *param_name, char *param_unit) 167 { 168 unsigned int min; 169 unsigned int max; 170 int is_within_bounds = 1; 171 172 min = pcm_params_get_min(params, param); 173 if (value < min) { 174 fprintf(stderr, "%s is %u%s, device only supports >= %u%s\n", param_name, value, 175 param_unit, min, param_unit); 176 is_within_bounds = 0; 177 } 178 179 max = pcm_params_get_max(params, param); 180 if (value > max) { 181 fprintf(stderr, "%s is %u%s, device only supports <= %u%s\n", param_name, value, 182 param_unit, max, param_unit); 183 is_within_bounds = 0; 184 } 185 186 return is_within_bounds; 187 } 188 189 int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels, 190 unsigned int rate, unsigned int bits, unsigned int period_size, 191 unsigned int period_count) 192 { 193 struct pcm_params *params; 194 int can_play; 195 196 params = pcm_params_get(card, device, PCM_OUT); 197 if (params == NULL) { 198 fprintf(stderr, "Unable to open PCM device %u.\n", device); 199 return 0; 200 } 201 202 can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz"); 203 can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels"); 204 can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits"); 205 can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", " frames"); 206 can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", " periods"); 207 208 pcm_params_free(params); 209 210 return can_play; 211 } 212 213 void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, 214 unsigned int rate, unsigned int bits, unsigned int period_size, 215 unsigned int period_count, uint32_t data_sz) 216 { 217 struct pcm_config config; 218 struct pcm *pcm; 219 char *buffer; 220 unsigned int size, read_sz; 221 int num_read; 222 223 memset(&config, 0, sizeof(config)); 224 config.channels = channels; 225 config.rate = rate; 226 config.period_size = period_size; 227 config.period_count = period_count; 228 if (bits == 32) 229 config.format = PCM_FORMAT_S32_LE; 230 else if (bits == 24) 231 config.format = PCM_FORMAT_S24_3LE; 232 else if (bits == 16) 233 config.format = PCM_FORMAT_S16_LE; 234 config.start_threshold = 0; 235 config.stop_threshold = 0; 236 config.silence_threshold = 0; 237 238 if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) { 239 return; 240 } 241 242 pcm = pcm_open(card, device, PCM_OUT, &config); 243 if (!pcm || !pcm_is_ready(pcm)) { 244 fprintf(stderr, "Unable to open PCM device %u (%s)\n", 245 device, pcm_get_error(pcm)); 246 return; 247 } 248 249 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); 250 buffer = malloc(size); 251 if (!buffer) { 252 fprintf(stderr, "Unable to allocate %d bytes\n", size); 253 free(buffer); 254 pcm_close(pcm); 255 return; 256 } 257 258 printf("Playing sample: %u ch, %u hz, %u bit %u bytes\n", channels, rate, bits, data_sz); 259 260 /* catch ctrl-c to shutdown cleanly */ 261 signal(SIGINT, stream_close); 262 263 do { 264 read_sz = size < data_sz ? size : data_sz; 265 num_read = fread(buffer, 1, read_sz, file); 266 if (num_read > 0) { 267 if (pcm_write(pcm, buffer, num_read)) { 268 fprintf(stderr, "Error playing sample\n"); 269 break; 270 } 271 data_sz -= num_read; 272 } 273 } while (!close && num_read > 0 && data_sz > 0); 274 275 free(buffer); 276 pcm_close(pcm); 277 } 278 279