1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2 
3 /***
4   This file is part of libcanberra.
5 
6   Copyright 2008 Lennart Poettering
7 
8   libcanberra is free software; you can redistribute it and/or modify
9   it under the terms of the GNU Lesser General Public License as
10   published by the Free Software Foundation, either version 2.1 of the
11   License, or (at your option) any later version.
12 
13   libcanberra is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17 
18   You should have received a copy of the GNU Lesser General Public
19   License along with libcanberra. If not, see
20   <http://www.gnu.org/licenses/>.
21 ***/
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <errno.h>
28 
29 #include "read-sound-file.h"
30 #include "read-wav.h"
31 #include "read-vorbis.h"
32 #include "macro.h"
33 #include "malloc.h"
34 #include "canberra.h"
35 
36 struct ca_sound_file {
37         ca_wav *wav;
38         ca_vorbis *vorbis;
39         char *filename;
40 
41         unsigned nchannels;
42         unsigned rate;
43         ca_sample_type_t type;
44 };
45 
ca_sound_file_open(ca_sound_file ** _f,const char * fn)46 int ca_sound_file_open(ca_sound_file **_f, const char *fn) {
47         FILE *file;
48         ca_sound_file *f;
49         int ret;
50 
51         ca_return_val_if_fail(_f, CA_ERROR_INVALID);
52         ca_return_val_if_fail(fn, CA_ERROR_INVALID);
53 
54         if (!(f = ca_new0(ca_sound_file, 1)))
55                 return CA_ERROR_OOM;
56 
57         if (!(f->filename = ca_strdup(fn))) {
58                 ret = CA_ERROR_OOM;
59                 goto fail;
60         }
61 
62         if (!(file = fopen(fn, "r"))) {
63                 ret = errno == ENOENT ? CA_ERROR_NOTFOUND : CA_ERROR_SYSTEM;
64                 goto fail;
65         }
66 
67         if ((ret = ca_wav_open(&f->wav, file)) == CA_SUCCESS) {
68                 f->nchannels = ca_wav_get_nchannels(f->wav);
69                 f->rate = ca_wav_get_rate(f->wav);
70                 f->type = ca_wav_get_sample_type(f->wav);
71                 *_f = f;
72                 return CA_SUCCESS;
73         }
74 
75         if (ret == CA_ERROR_CORRUPT) {
76 
77                 if (fseek(file, 0, SEEK_SET) < 0) {
78                         ret = CA_ERROR_SYSTEM;
79                         goto fail;
80                 }
81 
82                 if ((ret = ca_vorbis_open(&f->vorbis, file)) == CA_SUCCESS)  {
83                         f->nchannels = ca_vorbis_get_nchannels(f->vorbis);
84                         f->rate = ca_vorbis_get_rate(f->vorbis);
85                         f->type = CA_SAMPLE_S16NE;
86                         *_f = f;
87                         return CA_SUCCESS;
88                 }
89         }
90 
91 fail:
92 
93         ca_free(f->filename);
94         ca_free(f);
95 
96         return ret;
97 }
98 
ca_sound_file_close(ca_sound_file * f)99 void ca_sound_file_close(ca_sound_file *f) {
100         ca_assert(f);
101 
102         if (f->wav)
103                 ca_wav_close(f->wav);
104         if (f->vorbis)
105                 ca_vorbis_close(f->vorbis);
106 
107         ca_free(f->filename);
108         ca_free(f);
109 }
110 
ca_sound_file_get_nchannels(ca_sound_file * f)111 unsigned ca_sound_file_get_nchannels(ca_sound_file *f) {
112         ca_assert(f);
113         return f->nchannels;
114 }
115 
ca_sound_file_get_rate(ca_sound_file * f)116 unsigned ca_sound_file_get_rate(ca_sound_file *f) {
117         ca_assert(f);
118         return f->rate;
119 }
120 
ca_sound_file_get_sample_type(ca_sound_file * f)121 ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f) {
122         ca_assert(f);
123         return f->type;
124 }
125 
ca_sound_file_get_channel_map(ca_sound_file * f)126 const ca_channel_position_t* ca_sound_file_get_channel_map(ca_sound_file *f) {
127         ca_assert(f);
128 
129         if (f->wav)
130                 return ca_wav_get_channel_map(f->wav);
131         else
132                 return ca_vorbis_get_channel_map(f->vorbis);
133 }
134 
ca_sound_file_read_int16(ca_sound_file * f,int16_t * d,size_t * n)135 int ca_sound_file_read_int16(ca_sound_file *f, int16_t *d, size_t *n) {
136         ca_return_val_if_fail(f, CA_ERROR_INVALID);
137         ca_return_val_if_fail(d, CA_ERROR_INVALID);
138         ca_return_val_if_fail(n, CA_ERROR_INVALID);
139         ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID);
140         ca_return_val_if_fail(f->wav || f->vorbis, CA_ERROR_STATE);
141         ca_return_val_if_fail(f->type == CA_SAMPLE_S16NE || f->type == CA_SAMPLE_S16RE, CA_ERROR_STATE);
142 
143         if (f->wav)
144                 return ca_wav_read_s16le(f->wav, d, n);
145         else
146                 return ca_vorbis_read_s16ne(f->vorbis, d, n);
147 }
148 
ca_sound_file_read_uint8(ca_sound_file * f,uint8_t * d,size_t * n)149 int ca_sound_file_read_uint8(ca_sound_file *f, uint8_t *d, size_t *n) {
150         ca_return_val_if_fail(f, CA_ERROR_INVALID);
151         ca_return_val_if_fail(d, CA_ERROR_INVALID);
152         ca_return_val_if_fail(n, CA_ERROR_INVALID);
153         ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID);
154         ca_return_val_if_fail(f->wav && !f->vorbis, CA_ERROR_STATE);
155         ca_return_val_if_fail(f->type == CA_SAMPLE_U8, CA_ERROR_STATE);
156 
157         if (f->wav)
158                 return ca_wav_read_u8(f->wav, d, n);
159 
160         return CA_ERROR_STATE;
161 }
162 
ca_sound_file_read_arbitrary(ca_sound_file * f,void * d,size_t * n)163 int ca_sound_file_read_arbitrary(ca_sound_file *f, void *d, size_t *n) {
164         int ret;
165 
166         ca_return_val_if_fail(f, CA_ERROR_INVALID);
167         ca_return_val_if_fail(d, CA_ERROR_INVALID);
168         ca_return_val_if_fail(n, CA_ERROR_INVALID);
169         ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID);
170 
171         switch (f->type) {
172         case CA_SAMPLE_S16NE:
173         case CA_SAMPLE_S16RE: {
174                 size_t k;
175 
176                 k = *n / sizeof(int16_t);
177                 if ((ret = ca_sound_file_read_int16(f, d, &k)) == CA_SUCCESS)
178                         *n = k * sizeof(int16_t);
179 
180                 break;
181         }
182 
183         case CA_SAMPLE_U8: {
184                 size_t k;
185 
186                 k = *n;
187                 if ((ret = ca_sound_file_read_uint8(f, d, &k)) == CA_SUCCESS)
188                         *n = k;
189 
190                 break;
191         }
192 
193         default:
194                 ca_assert_not_reached();
195         }
196 
197         return ret;
198 }
199 
ca_sound_file_get_size(ca_sound_file * f)200 off_t ca_sound_file_get_size(ca_sound_file *f) {
201         ca_return_val_if_fail(f, (off_t) -1);
202 
203         if (f->wav)
204                 return ca_wav_get_size(f->wav);
205         else
206                 return ca_vorbis_get_size(f->vorbis);
207 }
208 
ca_sound_file_frame_size(ca_sound_file * f)209 size_t ca_sound_file_frame_size(ca_sound_file *f) {
210         unsigned c;
211 
212         ca_assert(f);
213 
214         c = ca_sound_file_get_nchannels(f);
215 
216         return c * (ca_sound_file_get_sample_type(f) == CA_SAMPLE_U8 ? 1U : 2U);
217 }
218