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