Handle streams separately in tree_add_track()
[cmus.git] / wav.c
blobbd9c8d7fa409c05399fc8357db1dd18e7c4d47f8
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"
24 #include "utils.h"
26 #include <string.h>
27 #include <errno.h>
29 #include <sys/types.h>
30 #include <unistd.h>
32 struct wav_private {
33 unsigned int pcm_start;
34 unsigned int pcm_size;
35 unsigned int pos;
37 /* size of one second of data */
38 unsigned int sec_size;
41 static int read_chunk_header(int fd, const char *name, unsigned int *size)
43 int rc;
44 char buf[8];
46 rc = read_all(fd, buf, 8);
47 if (rc == -1)
48 return -IP_ERROR_ERRNO;
49 if (rc != 8)
50 return -IP_ERROR_FILE_FORMAT;
51 *size = read_le32(buf + 4);
52 if (memcmp(buf, name, 4))
53 return -IP_ERROR_FILE_FORMAT;
54 return 0;
57 static int find_chunk(int fd, const char *name, unsigned int *size)
59 int rc;
61 do {
62 rc = read_chunk_header(fd, name, size);
63 if (rc == 0)
64 return 0;
65 if (rc != -IP_ERROR_FILE_FORMAT)
66 return rc;
67 d_print("seeking %d\n", *size);
68 if (lseek(fd, *size, SEEK_CUR) == -1) {
69 d_print("seek failed\n");
70 return -IP_ERROR_ERRNO;
72 } while (1);
75 static int wav_open(struct input_plugin_data *ip_data)
77 struct wav_private *priv;
78 char buf[4];
79 char *fmt;
80 int rc;
81 unsigned int riff_size, fmt_size;
82 int save;
84 d_print("file: %s\n", ip_data->filename);
85 priv = xnew(struct wav_private, 1);
86 ip_data->private = priv;
87 rc = read_chunk_header(ip_data->fd, "RIFF", &riff_size);
88 if (rc)
89 goto error_exit;
90 rc = read_all(ip_data->fd, buf, 4);
91 if (rc == -1) {
92 rc = -IP_ERROR_ERRNO;
93 goto error_exit;
95 if (rc != 4 || memcmp(buf, "WAVE", 4) != 0) {
96 rc = -IP_ERROR_FILE_FORMAT;
97 goto error_exit;
100 rc = find_chunk(ip_data->fd, "fmt ", &fmt_size);
101 if (rc)
102 goto error_exit;
103 if (fmt_size < 16) {
104 d_print("size of \"fmt \" chunk is invalid (%d)\n", fmt_size);
105 rc = -IP_ERROR_FILE_FORMAT;
106 goto error_exit;
108 fmt = xnew(char, fmt_size);
109 rc = read_all(ip_data->fd, fmt, fmt_size);
110 if (rc == -1) {
111 save = errno;
112 free(fmt);
113 errno = save;
114 rc = -IP_ERROR_ERRNO;
115 goto error_exit;
117 if (rc != fmt_size) {
118 save = errno;
119 free(fmt);
120 errno = save;
121 rc = -IP_ERROR_FILE_FORMAT;
122 goto error_exit;
125 int format_tag, channels, rate, bits;
127 format_tag = read_le16(fmt + 0);
128 channels = read_le16(fmt + 2);
129 rate = read_le32(fmt + 4);
130 /* 4 bytes, bytes per second */
131 /* 2 bytes, bytes per sample */
132 bits = read_le16(fmt + 14);
133 free(fmt);
135 if (format_tag != 1) {
136 d_print("invalid format tag %d, should be 1\n", format_tag);
137 rc = -IP_ERROR_FILE_FORMAT;
138 goto error_exit;
140 if ((bits != 8 && bits != 16) || channels < 1 || channels > 2) {
141 rc = -IP_ERROR_SAMPLE_FORMAT;
142 goto error_exit;
144 ip_data->sf = sf_channels(channels) | sf_rate(rate) | sf_bits(bits) |
145 sf_signed(bits > 8);
148 rc = find_chunk(ip_data->fd, "data", &priv->pcm_size);
149 if (rc)
150 goto error_exit;
151 rc = lseek(ip_data->fd, 0, SEEK_CUR);
152 if (rc == -1) {
153 rc = -IP_ERROR_ERRNO;
154 goto error_exit;
156 priv->pcm_start = rc;
158 priv->sec_size = sf_get_second_size(ip_data->sf);
159 priv->pos = 0;
161 d_print("pcm start: %u\n", priv->pcm_start);
162 d_print("pcm size: %u\n", priv->pcm_size);
163 d_print("\n");
164 d_print("sr: %d, ch: %d, bits: %d, signed: %d\n", sf_get_rate(ip_data->sf),
165 sf_get_channels(ip_data->sf), sf_get_bits(ip_data->sf),
166 sf_get_signed(ip_data->sf));
168 /* clamp pcm_size to full frames (file might be corrupt or truncated) */
169 priv->pcm_size = priv->pcm_size & ~((unsigned int)sf_get_frame_size(ip_data->sf) - 1U);
170 return 0;
171 error_exit:
172 save = errno;
173 free(priv);
174 errno = save;
175 return rc;
178 static int wav_close(struct input_plugin_data *ip_data)
180 struct wav_private *priv;
182 priv = ip_data->private;
183 free(priv);
184 ip_data->private = NULL;
185 return 0;
188 static int wav_read(struct input_plugin_data *ip_data, char *buffer, int _count)
190 struct wav_private *priv = ip_data->private;
191 unsigned int count = _count;
192 int rc;
194 if (priv->pos == priv->pcm_size) {
195 /* eof */
196 return 0;
198 if (count > priv->pcm_size - priv->pos)
199 count = priv->pcm_size - priv->pos;
200 rc = read(ip_data->fd, buffer, count);
201 if (rc == -1) {
202 d_print("read error\n");
203 return -IP_ERROR_ERRNO;
205 if (rc == 0) {
206 d_print("eof\n");
207 return 0;
209 priv->pos += rc;
210 return rc;
213 static int wav_seek(struct input_plugin_data *ip_data, double _offset)
215 struct wav_private *priv = ip_data->private;
216 unsigned int offset;
217 off_t rc;
219 offset = (unsigned int)(_offset * (double)priv->sec_size + 0.5);
220 /* align to 4 bytes (2 * 16 / 8) */
221 offset &= ~3U;
222 priv->pos = offset;
223 rc = lseek(ip_data->fd, priv->pcm_start + offset, SEEK_SET);
224 if (rc == (off_t)-1)
225 return -1;
226 return 0;
229 static int wav_read_comments(struct input_plugin_data *ip_data,
230 struct keyval **comments)
232 *comments = xnew0(struct keyval, 1);
233 return 0;
236 static int wav_duration(struct input_plugin_data *ip_data)
238 struct wav_private *priv;
239 int duration;
241 priv = ip_data->private;
242 duration = priv->pcm_size / priv->sec_size;
243 return duration;
246 const struct input_plugin_ops ip_ops = {
247 .open = wav_open,
248 .close = wav_close,
249 .read = wav_read,
250 .seek = wav_seek,
251 .read_comments = wav_read_comments,
252 .duration = wav_duration
255 const char * const ip_extensions[] = { "wav", NULL };
256 const char * const ip_mime_types[] = { NULL };