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 41 struct riff_wave_header { 42 uint32_t riff_id; 43 uint32_t riff_sz; 44 uint32_t wave_id; 45 }; 46 47 struct chunk_header { 48 uint32_t id; 49 uint32_t sz; 50 }; 51 52 struct 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 61 static int close = 0; 62 63 void 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 67 void stream_close(int sig) 68 { 69 /* allow the stream to be closed gracefully */ 70 signal(sig, SIG_IGN); 71 close = 1; 72 } 73 74 int 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 163 void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, 164 unsigned int rate, unsigned int bits, unsigned int period_size, 165 unsigned int period_count) 166 { 167 struct pcm_config config; 168 struct pcm *pcm; 169 char *buffer; 170 int size; 171 int num_read; 172 173 config.channels = channels; 174 config.rate = rate; 175 config.period_size = period_size; 176 config.period_count = period_count; 177 if (bits == 32) 178 config.format = PCM_FORMAT_S32_LE; 179 else if (bits == 16) 180 config.format = PCM_FORMAT_S16_LE; 181 config.start_threshold = 0; 182 config.stop_threshold = 0; 183 config.silence_threshold = 0; 184 185 pcm = pcm_open(card, device, PCM_OUT, &config); 186 if (!pcm || !pcm_is_ready(pcm)) { 187 fprintf(stderr, "Unable to open PCM device %u (%s)\n", 188 device, pcm_get_error(pcm)); 189 return; 190 } 191 192 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); 193 buffer = malloc(size); 194 if (!buffer) { 195 fprintf(stderr, "Unable to allocate %d bytes\n", size); 196 free(buffer); 197 pcm_close(pcm); 198 return; 199 } 200 201 printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits); 202 203 /* catch ctrl-c to shutdown cleanly */ 204 signal(SIGINT, stream_close); 205 206 do { 207 num_read = fread(buffer, 1, size, file); 208 if (num_read > 0) { 209 if (pcm_write(pcm, buffer, num_read)) { 210 fprintf(stderr, "Error playing sample\n"); 211 break; 212 } 213 } 214 } while (!close && num_read > 0); 215 216 free(buffer); 217 pcm_close(pcm); 218 } 219 220