1 /* tinywavinfo.c
2 **
3 ** Copyright 2015, 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 <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <math.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 analyse_sample(FILE *file, unsigned int channels, unsigned int bits,
64                     unsigned int data_chunk_size);
65 
stream_close(int sig)66 void stream_close(int sig)
67 {
68     /* allow the stream to be closed gracefully */
69     signal(sig, SIG_IGN);
70     close = 1;
71 }
72 
xfread(void * ptr,size_t size,size_t nmemb,FILE * stream)73 size_t xfread(void *ptr, size_t size, size_t nmemb, FILE *stream)
74 {
75     size_t sz = fread(ptr, size, nmemb, stream);
76 
77     if (sz != nmemb && ferror(stream)) {
78         fprintf(stderr, "Error: fread failed\n");
79         exit(1);
80     }
81     return sz;
82 }
83 
main(int argc,char ** argv)84 int main(int argc, char **argv)
85 {
86     FILE *file;
87     struct riff_wave_header riff_wave_header;
88     struct chunk_header chunk_header;
89     struct chunk_fmt chunk_fmt;
90     char *filename;
91     int more_chunks = 1;
92 
93     if (argc < 2) {
94         fprintf(stderr, "Usage: %s file.wav \n", argv[0]);
95         return 1;
96     }
97 
98     filename = argv[1];
99     file = fopen(filename, "rb");
100     if (!file) {
101         fprintf(stderr, "Unable to open file '%s'\n", filename);
102         return 1;
103     }
104 
105     xfread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
106     if ((riff_wave_header.riff_id != ID_RIFF) ||
107         (riff_wave_header.wave_id != ID_WAVE)) {
108         fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
109         fclose(file);
110         return 1;
111     }
112 
113     do {
114         xfread(&chunk_header, sizeof(chunk_header), 1, file);
115 
116         switch (chunk_header.id) {
117         case ID_FMT:
118             xfread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
119             /* If the format header is larger, skip the rest */
120             if (chunk_header.sz > sizeof(chunk_fmt))
121                 fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
122             break;
123         case ID_DATA:
124             /* Stop looking for chunks */
125             more_chunks = 0;
126             break;
127         default:
128             /* Unknown chunk, skip bytes */
129             fseek(file, chunk_header.sz, SEEK_CUR);
130         }
131     } while (more_chunks);
132 
133     printf("Input File       : %s \n", filename);
134     printf("Channels         : %u \n", chunk_fmt.num_channels);
135     printf("Sample Rate      : %u \n", chunk_fmt.sample_rate);
136     printf("Bits per sample  : %u \n\n", chunk_fmt.bits_per_sample);
137 
138     analyse_sample(file, chunk_fmt.num_channels, chunk_fmt.bits_per_sample,
139                     chunk_header.sz);
140 
141     fclose(file);
142 
143     return 0;
144 }
145 
analyse_sample(FILE * file,unsigned int channels,unsigned int bits,unsigned int data_chunk_size)146 void analyse_sample(FILE *file, unsigned int channels, unsigned int bits,
147                     unsigned int data_chunk_size)
148 {
149     void *buffer;
150     int size;
151     int num_read;
152     int i;
153     unsigned int ch;
154     int frame_size = 1024;
155     unsigned int bytes_per_sample = 0;
156     float *power;
157     int total_sample_per_channel;
158     float normalization_factor;
159 
160     if (bits == 32)
161         bytes_per_sample = 4;
162     else if (bits == 16)
163         bytes_per_sample = 2;
164 
165     normalization_factor = (float)pow(2.0, (bits-1));
166 
167     size = channels * bytes_per_sample * frame_size;
168 
169     buffer = malloc(size);
170     if (!buffer) {
171         fprintf(stderr, "Unable to allocate %d bytes\n", size);
172         free(buffer);
173         return;
174     }
175 
176     power = (float *) calloc(channels, sizeof(float));
177 
178     total_sample_per_channel = data_chunk_size / (channels * bytes_per_sample);
179 
180     /* catch ctrl-c to shutdown cleanly */
181     signal(SIGINT, stream_close);
182 
183     do {
184         num_read = xfread(buffer, 1, size, file);
185         if (num_read > 0) {
186             if (2 == bytes_per_sample) {
187                 short *buffer_ptr = (short *)buffer;
188                 for (i = 0; i < num_read; i += channels) {
189                     for (ch = 0; ch < channels; ch++) {
190                         int temp = *buffer_ptr++;
191                         /* Signal Normalization */
192                         float f = (float) temp / normalization_factor;
193                         *(power + ch) += (float) (f * f);
194                     }
195                 }
196             }
197             if (4 == bytes_per_sample) {
198                 int *buffer_ptr = (int *)buffer;
199                 for (i = 0; i < num_read; i += channels) {
200                     for (ch = 0; ch < channels; ch++) {
201                         int temp = *buffer_ptr++;
202                         /* Signal Normalization */
203                         float f = (float) temp / normalization_factor;
204                         *(power + ch) += (float) (f * f);
205                     }
206                 }
207             }
208         }
209     }while (!close && num_read > 0);
210 
211     for (ch = 0; ch < channels; ch++) {
212         float average_power = 10 * log10((*(power + ch)) / total_sample_per_channel);
213         if(isinf (average_power)) {
214             printf("Channel [%2u] Average Power : NO signal or ZERO signal\n", ch);
215         } else {
216             printf("Channel [%2u] Average Power : %.2f dB\n", ch, average_power);
217         }
218     }
219 
220     free(buffer);
221     free(power);
222 
223 }
224 
225