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
34 #define ID_RIFF 0x46464952
35 #define ID_WAVE 0x45564157
36 #define ID_FMT 0x20746d66
37 #define ID_DATA 0x61746164
38
39 struct riff_wave_header {
40 uint32_t riff_id;
41 uint32_t riff_sz;
42 uint32_t wave_id;
43 };
44
45 struct chunk_header {
46 uint32_t id;
47 uint32_t sz;
48 };
49
50 struct chunk_fmt {
51 uint16_t audio_format;
52 uint16_t num_channels;
53 uint32_t sample_rate;
54 uint32_t byte_rate;
55 uint16_t block_align;
56 uint16_t bits_per_sample;
57 };
58
59 void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
60 unsigned int rate, unsigned int bits, unsigned int period_size,
61 unsigned int period_count);
62
main(int argc,char ** argv)63 int main(int argc, char **argv)
64 {
65 FILE *file;
66 struct riff_wave_header riff_wave_header;
67 struct chunk_header chunk_header;
68 struct chunk_fmt chunk_fmt;
69 unsigned int device = 0;
70 unsigned int card = 0;
71 unsigned int period_size = 1024;
72 unsigned int period_count = 4;
73 char *filename;
74 int more_chunks = 1;
75
76 if (argc < 2) {
77 fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]"
78 " [-n n_periods] \n", argv[0]);
79 return 1;
80 }
81
82 filename = argv[1];
83 file = fopen(filename, "rb");
84 if (!file) {
85 fprintf(stderr, "Unable to open file '%s'\n", filename);
86 return 1;
87 }
88
89 fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
90 if ((riff_wave_header.riff_id != ID_RIFF) ||
91 (riff_wave_header.wave_id != ID_WAVE)) {
92 fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
93 fclose(file);
94 return 1;
95 }
96
97 do {
98 fread(&chunk_header, sizeof(chunk_header), 1, file);
99
100 switch (chunk_header.id) {
101 case ID_FMT:
102 fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
103 /* If the format header is larger, skip the rest */
104 if (chunk_header.sz > sizeof(chunk_fmt))
105 fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
106 break;
107 case ID_DATA:
108 /* Stop looking for chunks */
109 more_chunks = 0;
110 break;
111 default:
112 /* Unknown chunk, skip bytes */
113 fseek(file, chunk_header.sz, SEEK_CUR);
114 }
115 } while (more_chunks);
116
117 /* parse command line arguments */
118 argv += 2;
119 while (*argv) {
120 if (strcmp(*argv, "-d") == 0) {
121 argv++;
122 if (*argv)
123 device = atoi(*argv);
124 }
125 if (strcmp(*argv, "-p") == 0) {
126 argv++;
127 if (*argv)
128 period_size = atoi(*argv);
129 }
130 if (strcmp(*argv, "-n") == 0) {
131 argv++;
132 if (*argv)
133 period_count = atoi(*argv);
134 }
135 if (strcmp(*argv, "-D") == 0) {
136 argv++;
137 if (*argv)
138 card = atoi(*argv);
139 }
140 if (*argv)
141 argv++;
142 }
143
144 play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
145 chunk_fmt.bits_per_sample, period_size, period_count);
146
147 fclose(file);
148
149 return 0;
150 }
151
play_sample(FILE * file,unsigned int card,unsigned int device,unsigned int channels,unsigned int rate,unsigned int bits,unsigned int period_size,unsigned int period_count)152 void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
153 unsigned int rate, unsigned int bits, unsigned int period_size,
154 unsigned int period_count)
155 {
156 struct pcm_config config;
157 struct pcm *pcm;
158 char *buffer;
159 int size;
160 int num_read;
161
162 config.channels = channels;
163 config.rate = rate;
164 config.period_size = period_size;
165 config.period_count = period_count;
166 if (bits == 32)
167 config.format = PCM_FORMAT_S32_LE;
168 else if (bits == 16)
169 config.format = PCM_FORMAT_S16_LE;
170 config.start_threshold = 0;
171 config.stop_threshold = 0;
172 config.silence_threshold = 0;
173
174 pcm = pcm_open(card, device, PCM_OUT, &config);
175 if (!pcm || !pcm_is_ready(pcm)) {
176 fprintf(stderr, "Unable to open PCM device %u (%s)\n",
177 device, pcm_get_error(pcm));
178 return;
179 }
180
181 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
182 buffer = malloc(size);
183 if (!buffer) {
184 fprintf(stderr, "Unable to allocate %d bytes\n", size);
185 free(buffer);
186 pcm_close(pcm);
187 return;
188 }
189
190 printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
191
192 do {
193 num_read = fread(buffer, 1, size, file);
194 if (num_read > 0) {
195 if (pcm_write(pcm, buffer, num_read)) {
196 fprintf(stderr, "Error playing sample\n");
197 break;
198 }
199 }
200 } while (num_read > 0);
201
202 free(buffer);
203 pcm_close(pcm);
204 }
205
206