Commit | Line | Data |
---|---|---|
56e73ff7 DM |
1 | /* ------------------------------------------------------------------ |
2 | * Copyright (C) 2009 Martin Storsjo | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | |
13 | * express or implied. | |
14 | * See the License for the specific language governing permissions | |
15 | * and limitations under the License. | |
16 | * ------------------------------------------------------------------- | |
17 | */ | |
18 | ||
19 | #include "wavreader.h" | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
23 | #include <stdint.h> | |
24 | ||
25 | #define TAG(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) | |
26 | ||
27 | struct wav_reader { | |
28 | FILE *wav; | |
29 | uint32_t data_length; | |
30 | ||
31 | int format; | |
32 | int sample_rate; | |
33 | int bits_per_sample; | |
34 | int channels; | |
35 | int byte_rate; | |
36 | int block_align; | |
37 | }; | |
38 | ||
39 | static uint32_t read_tag(struct wav_reader* wr) { | |
40 | uint32_t tag = 0; | |
41 | tag = (tag << 8) | fgetc(wr->wav); | |
42 | tag = (tag << 8) | fgetc(wr->wav); | |
43 | tag = (tag << 8) | fgetc(wr->wav); | |
44 | tag = (tag << 8) | fgetc(wr->wav); | |
45 | return tag; | |
46 | } | |
47 | ||
48 | static uint32_t read_int32(struct wav_reader* wr) { | |
49 | uint32_t value = 0; | |
50 | value |= fgetc(wr->wav) << 0; | |
51 | value |= fgetc(wr->wav) << 8; | |
52 | value |= fgetc(wr->wav) << 16; | |
53 | value |= fgetc(wr->wav) << 24; | |
54 | return value; | |
55 | } | |
56 | ||
57 | static uint16_t read_int16(struct wav_reader* wr) { | |
58 | uint16_t value = 0; | |
59 | value |= fgetc(wr->wav) << 0; | |
60 | value |= fgetc(wr->wav) << 8; | |
61 | return value; | |
62 | } | |
63 | ||
64 | void* wav_read_open(const char *filename) { | |
65 | struct wav_reader* wr = (struct wav_reader*) malloc(sizeof(*wr)); | |
66 | long data_pos = 0; | |
67 | memset(wr, 0, sizeof(*wr)); | |
68 | ||
69 | wr->wav = fopen(filename, "rb"); | |
70 | if (wr->wav == NULL) { | |
71 | free(wr); | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | while (1) { | |
76 | uint32_t tag, tag2, length; | |
77 | tag = read_tag(wr); | |
78 | if (feof(wr->wav)) | |
79 | break; | |
80 | length = read_int32(wr); | |
81 | if (tag != TAG('R', 'I', 'F', 'F') || length < 4) { | |
82 | fseek(wr->wav, length, SEEK_CUR); | |
83 | continue; | |
84 | } | |
85 | tag2 = read_tag(wr); | |
86 | length -= 4; | |
87 | if (tag2 != TAG('W', 'A', 'V', 'E')) { | |
88 | fseek(wr->wav, length, SEEK_CUR); | |
89 | continue; | |
90 | } | |
91 | // RIFF chunk found, iterate through it | |
92 | while (length >= 8) { | |
93 | uint32_t subtag, sublength; | |
94 | subtag = read_tag(wr); | |
95 | if (feof(wr->wav)) | |
96 | break; | |
97 | sublength = read_int32(wr); | |
98 | length -= 8; | |
99 | if (length < sublength) | |
100 | break; | |
101 | if (subtag == TAG('f', 'm', 't', ' ')) { | |
102 | if (sublength < 16) { | |
103 | // Insufficient data for 'fmt ' | |
104 | break; | |
105 | } | |
106 | wr->format = read_int16(wr); | |
107 | wr->channels = read_int16(wr); | |
108 | wr->sample_rate = read_int32(wr); | |
109 | wr->byte_rate = read_int32(wr); | |
110 | wr->block_align = read_int16(wr); | |
111 | wr->bits_per_sample = read_int16(wr); | |
112 | fseek(wr->wav, sublength - 16, SEEK_CUR); | |
113 | } else if (subtag == TAG('d', 'a', 't', 'a')) { | |
114 | data_pos = ftell(wr->wav); | |
115 | wr->data_length = sublength; | |
116 | fseek(wr->wav, sublength, SEEK_CUR); | |
117 | } else { | |
118 | fseek(wr->wav, sublength, SEEK_CUR); | |
119 | } | |
120 | length -= sublength; | |
121 | } | |
122 | if (length > 0) { | |
123 | // Bad chunk? | |
124 | fseek(wr->wav, length, SEEK_CUR); | |
125 | } | |
126 | } | |
127 | fseek(wr->wav, data_pos, SEEK_SET); | |
128 | return wr; | |
129 | } | |
130 | ||
131 | void wav_read_close(void* obj) { | |
132 | struct wav_reader* wr = (struct wav_reader*) obj; | |
133 | fclose(wr->wav); | |
134 | free(wr); | |
135 | } | |
136 | ||
137 | int wav_get_header(void* obj, int* format, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_length) { | |
138 | struct wav_reader* wr = (struct wav_reader*) obj; | |
139 | if (format) | |
140 | *format = wr->format; | |
141 | if (channels) | |
142 | *channels = wr->channels; | |
143 | if (sample_rate) | |
144 | *sample_rate = wr->sample_rate; | |
145 | if (bits_per_sample) | |
146 | *bits_per_sample = wr->bits_per_sample; | |
147 | if (data_length) | |
148 | *data_length = wr->data_length; | |
149 | return wr->format && wr->sample_rate; | |
150 | } | |
151 | ||
152 | int wav_read_data(void* obj, unsigned char* data, unsigned int length) { | |
153 | struct wav_reader* wr = (struct wav_reader*) obj; | |
154 | int n; | |
155 | if (wr->wav == NULL) | |
156 | return -1; | |
157 | if (length > wr->data_length) | |
158 | length = wr->data_length; | |
159 | n = fread(data, 1, length, wr->wav); | |
160 | wr->data_length -= length; | |
161 | return n; | |
162 | } | |
163 |