aac: Collect all interesting ID3 frames
[cmus.git] / wav.c
blob55decf2e8a1cd997e91ac16a16d3173c37f20dd7
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "ip.h"
21 #include "file.h"
22 #include "xmalloc.h"
23 #include "debug.h"
25 #include <string.h>
26 #include <errno.h>
28 #include <sys/types.h>
29 #include <unistd.h>
31 struct wav_private {
32 unsigned int pcm_start;
33 unsigned int pcm_size;
34 unsigned int pos;
36 /* size of one second of data */
37 unsigned int sec_size;
40 static inline unsigned short read_u2(const char *buffer)
42 const unsigned char *buf = (const unsigned char *)buffer;
44 return buf[0] + (buf[1] << 8);
47 static inline unsigned int read_u4(const char *buffer)
49 const unsigned char *buf = (const unsigned char *)buffer;
51 return buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
54 static int read_chunk_header(int fd, const char *name, unsigned int *size)
56 int rc;
57 char buf[8];
59 rc = read_all(fd, buf, 8);
60 if (rc == -1)
61 return -IP_ERROR_ERRNO;
62 if (rc != 8)
63 return -IP_ERROR_FILE_FORMAT;
64 *size = read_u4(buf + 4);
65 if (memcmp(buf, name, 4))
66 return -IP_ERROR_FILE_FORMAT;
67 return 0;
70 static int find_chunk(int fd, const char *name, unsigned int *size)
72 int rc;
74 do {
75 rc = read_chunk_header(fd, name, size);
76 if (rc == 0)
77 return 0;
78 if (rc != -IP_ERROR_FILE_FORMAT)
79 return rc;
80 d_print("seeking %d\n", *size);
81 if (lseek(fd, *size, SEEK_CUR) == -1) {
82 d_print("seek failed\n");
83 return -IP_ERROR_ERRNO;
85 } while (1);
88 static int wav_open(struct input_plugin_data *ip_data)
90 struct wav_private *priv;
91 char buf[4];
92 char *fmt;
93 int rc;
94 unsigned int riff_size, fmt_size;
95 int save;
97 d_print("file: %s\n", ip_data->filename);
98 priv = xnew(struct wav_private, 1);
99 ip_data->private = priv;
100 rc = read_chunk_header(ip_data->fd, "RIFF", &riff_size);
101 if (rc)
102 goto error_exit;
103 rc = read_all(ip_data->fd, buf, 4);
104 if (rc == -1) {
105 rc = -IP_ERROR_ERRNO;
106 goto error_exit;
108 if (rc != 4 || memcmp(buf, "WAVE", 4) != 0) {
109 rc = -IP_ERROR_FILE_FORMAT;
110 goto error_exit;
113 rc = find_chunk(ip_data->fd, "fmt ", &fmt_size);
114 if (rc)
115 goto error_exit;
116 if (fmt_size < 16) {
117 d_print("size of \"fmt \" chunk is invalid (%d)\n", fmt_size);
118 rc = -IP_ERROR_FILE_FORMAT;
119 goto error_exit;
121 fmt = xnew(char, fmt_size);
122 rc = read_all(ip_data->fd, fmt, fmt_size);
123 if (rc == -1) {
124 save = errno;
125 free(fmt);
126 errno = save;
127 rc = -IP_ERROR_ERRNO;
128 goto error_exit;
130 if (rc != fmt_size) {
131 save = errno;
132 free(fmt);
133 errno = save;
134 rc = -IP_ERROR_FILE_FORMAT;
135 goto error_exit;
138 int format_tag, channels, rate, bits;
140 format_tag = read_u2(fmt + 0);
141 channels = read_u2(fmt + 2);
142 rate = read_u4(fmt + 4);
143 /* 4 bytes, bytes per second */
144 /* 2 bytes, bytes per sample */
145 bits = read_u2(fmt + 14);
146 free(fmt);
148 if (format_tag != 1) {
149 d_print("invalid format tag %d, should be 1\n", format_tag);
150 rc = -IP_ERROR_FILE_FORMAT;
151 goto error_exit;
153 if ((bits != 8 && bits != 16) || channels < 1 || channels > 2) {
154 rc = -IP_ERROR_SAMPLE_FORMAT;
155 goto error_exit;
157 ip_data->sf = sf_channels(channels) | sf_rate(rate) | sf_bits(bits) |
158 sf_signed(bits > 8);
161 rc = find_chunk(ip_data->fd, "data", &priv->pcm_size);
162 if (rc)
163 goto error_exit;
164 rc = lseek(ip_data->fd, 0, SEEK_CUR);
165 if (rc == -1) {
166 rc = -IP_ERROR_ERRNO;
167 goto error_exit;
169 priv->pcm_start = rc;
171 priv->sec_size = sf_get_second_size(ip_data->sf);
172 priv->pos = 0;
174 d_print("pcm start: %u\n", priv->pcm_start);
175 d_print("pcm size: %u\n", priv->pcm_size);
176 d_print("\n");
177 d_print("sr: %d, ch: %d, bits: %d, signed: %d\n", sf_get_rate(ip_data->sf),
178 sf_get_channels(ip_data->sf), sf_get_bits(ip_data->sf),
179 sf_get_signed(ip_data->sf));
181 /* clamp pcm_size to full frames (file might be corrupt or truncated) */
182 priv->pcm_size = priv->pcm_size & ~((unsigned int)sf_get_frame_size(ip_data->sf) - 1U);
183 return 0;
184 error_exit:
185 save = errno;
186 free(priv);
187 errno = save;
188 return rc;
191 static int wav_close(struct input_plugin_data *ip_data)
193 struct wav_private *priv;
195 priv = ip_data->private;
196 free(priv);
197 ip_data->private = NULL;
198 return 0;
201 static int wav_read(struct input_plugin_data *ip_data, char *buffer, int _count)
203 struct wav_private *priv = ip_data->private;
204 unsigned int count = _count;
205 int rc;
207 if (priv->pos == priv->pcm_size) {
208 /* eof */
209 return 0;
211 if (count > priv->pcm_size - priv->pos)
212 count = priv->pcm_size - priv->pos;
213 rc = read(ip_data->fd, buffer, count);
214 if (rc == -1) {
215 d_print("read error\n");
216 return -IP_ERROR_ERRNO;
218 if (rc == 0) {
219 d_print("eof\n");
220 return 0;
222 priv->pos += rc;
223 return rc;
226 static int wav_seek(struct input_plugin_data *ip_data, double _offset)
228 struct wav_private *priv = ip_data->private;
229 unsigned int offset;
230 off_t rc;
232 offset = (unsigned int)(_offset * (double)priv->sec_size + 0.5);
233 /* align to 4 bytes (2 * 16 / 8) */
234 offset &= ~3U;
235 priv->pos = offset;
236 rc = lseek(ip_data->fd, priv->pcm_start + offset, SEEK_SET);
237 if (rc == (off_t)-1)
238 return -1;
239 return 0;
242 static int wav_read_comments(struct input_plugin_data *ip_data,
243 struct keyval **comments)
245 *comments = xnew0(struct keyval, 1);
246 return 0;
249 static int wav_duration(struct input_plugin_data *ip_data)
251 struct wav_private *priv;
252 int duration;
254 priv = ip_data->private;
255 duration = priv->pcm_size / priv->sec_size;
256 return duration;
259 const struct input_plugin_ops ip_ops = {
260 .open = wav_open,
261 .close = wav_close,
262 .read = wav_read,
263 .seek = wav_seek,
264 .read_comments = wav_read_comments,
265 .duration = wav_duration
268 const char * const ip_extensions[] = { "wav", NULL };
269 const char * const ip_mime_types[] = { NULL };