build system: Don't use ${#var} etc.
[cmus.git] / wav.c
blobf9780938f5f5aebf9fb0d5198c8d59e12e66d86c
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: %d\n", priv->pcm_start);
175 d_print("pcm size: %d\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));
180 return 0;
181 error_exit:
182 save = errno;
183 free(priv);
184 errno = save;
185 return rc;
188 static int wav_close(struct input_plugin_data *ip_data)
190 struct wav_private *priv;
192 priv = ip_data->private;
193 free(priv);
194 ip_data->private = NULL;
195 return 0;
198 static int wav_read(struct input_plugin_data *ip_data, char *buffer, int count)
200 struct wav_private *priv;
201 int rc;
203 priv = ip_data->private;
204 if (priv->pos == priv->pcm_size) {
205 /* eof */
206 return 0;
208 if (count > priv->pcm_size - priv->pos)
209 count = priv->pcm_size - priv->pos;
210 rc = read(ip_data->fd, buffer, count);
211 if (rc == -1) {
212 d_print("read error\n");
213 return -IP_ERROR_ERRNO;
215 if (rc == 0) {
216 d_print("eof\n");
217 return 0;
219 priv->pos += rc;
220 return rc;
223 static int wav_seek(struct input_plugin_data *ip_data, double _offset)
225 struct wav_private *priv;
226 int offset, rc;
228 priv = ip_data->private;
229 offset = (int)(_offset * (double)priv->sec_size + 0.5);
230 /* aling to 4 bytes (2 * 16 / 8) */
231 offset -= offset % 4;
232 priv->pos = offset;
233 rc = lseek(ip_data->fd, priv->pcm_start + offset, SEEK_SET);
234 if (rc == -1)
235 return -1;
236 return 0;
239 static int wav_read_comments(struct input_plugin_data *ip_data,
240 struct keyval **comments)
242 *comments = xnew0(struct keyval, 1);
243 return 0;
246 static int wav_duration(struct input_plugin_data *ip_data)
248 struct wav_private *priv;
249 int duration;
251 priv = ip_data->private;
252 duration = priv->pcm_size / priv->sec_size;
253 return duration;
256 const struct input_plugin_ops ip_ops = {
257 .open = wav_open,
258 .close = wav_close,
259 .read = wav_read,
260 .seek = wav_seek,
261 .read_comments = wav_read_comments,
262 .duration = wav_duration
265 const char * const ip_extensions[] = { "wav", NULL };
266 const char * const ip_mime_types[] = { NULL };