Security fixes
[sxemacs] / src / media / media-sndfile.c
1 /* media-sndfile.c - analyse audio files or streams
2
3    Copyright (C) 2006 Sebastian Freundt
4
5 This file is part of SXEmacs
6
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19
20
21 /* Synched up with: Not in FSF. */
22
23 #include <config.h>
24 #include "lisp.h"
25
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31
32 #include "media-sndfile.h"
33
34 Lisp_Object Qsndfile;
35
36 #define MYSELF MDRIVER_SNDFILE
37
38 static sf_count_t sndfile_vio_get_filelen(void*);
39 static sf_count_t sndfile_vio_seek(sf_count_t, int, void*);
40 static sf_count_t sndfile_vio_read(void*, sf_count_t, void*);
41 static sf_count_t sndfile_vio_write(const void*, sf_count_t, void*);
42 static sf_count_t sndfile_vio_tell(void*);
43
44 #define __SNDFILE_DEBUG__(args...)      fprintf(stderr, "SNDFILE " args)
45 #ifndef SNDFILE_DEBUG_FLAG
46 #define SNDFILE_DEBUG(args...)
47 #else
48 #define SNDFILE_DEBUG(args...)          __SNDFILE_DEBUG__(args)
49 #endif
50 #define SNDFILE_DEBUG_S(args...)        SNDFILE_DEBUG("[stream]: " args)
51 #define SNDFILE_CRITICAL(args...)       __SNDFILE_DEBUG__("CRITICAL: " args)
52
53 \f
54 DECLARE_MEDIA_DRIVER_OPEN_METH(media_sndfile);
55 DECLARE_MEDIA_DRIVER_CLOSE_METH(media_sndfile);
56 DECLARE_MEDIA_DRIVER_READ_METH(media_sndfile);
57 DECLARE_MEDIA_DRIVER_REWIND_METH(media_sndfile);
58
59 DEFINE_MEDIA_DRIVER_CUSTOM(media_sndfile,
60                            media_sndfile_open, media_sndfile_close,
61                            NULL, NULL,
62                            media_sndfile_read, NULL,
63                            media_sndfile_rewind, NULL);
64
65 \f
66 static void
67 media_sndfile_close(ms_driver_data_t arg)
68 {
69         media_sndfile_data *sfd = arg;
70
71         SNDFILE_DEBUG("closing sndfile handle: 0x%x\n",
72                       (unsigned int)sfd->sf);
73
74         if (sfd->sf)
75                 sf_close(sfd->sf);
76         sfd->sf = NULL;
77
78         if (sfd->sfinfo)
79                 xfree(sfd->sfinfo);
80         sfd->sfinfo = NULL;
81
82         return;
83 }
84
85 static ms_driver_data_t
86 media_sndfile_open(Lisp_Media_Stream *ms)
87 {
88         mtype_audio_properties *mtap;
89         media_substream *mss;
90         /* libsndfile stuff */
91         media_sndfile_data *sfd = NULL;
92         SNDFILE *sf = NULL;
93         SF_INFO *sfinfo;
94
95         /* initialise */
96         sfinfo = xnew_and_zero(SF_INFO);
97
98         switch (media_stream_kind(ms)) {
99         case MKIND_FILE: {
100                 mkind_file_properties *mkfp = NULL;
101                 const char *file;
102                 int file_len = 0;
103
104                 /* open the file */
105                 mkfp = media_stream_kind_properties(ms).fprops;
106                 TO_EXTERNAL_FORMAT(LISP_STRING, mkfp->filename,
107                                    ALLOCA, (file, file_len), Qnil);
108                 sf = sf_open(file, SFM_READ, sfinfo);
109                 break;
110         }
111         case MKIND_STRING: {
112                 mkind_string_properties *mksp = NULL;
113                 SF_VIRTUAL_IO *sfvio = NULL;
114                 /* our container for sfvio */
115                 media_data *sd = NULL;
116
117                 /* prepare sndfile's virtual-I/O */
118                 sfvio = xnew_and_zero(SF_VIRTUAL_IO);
119                 sfvio->get_filelen = &sndfile_vio_get_filelen;
120                 sfvio->seek = &sndfile_vio_seek;
121                 sfvio->read = &sndfile_vio_read;
122                 sfvio->write = &sndfile_vio_write;
123                 sfvio->tell = &sndfile_vio_tell;
124
125                 /* prepare our user_data */
126                 mksp = media_stream_kind_properties(ms).sprops;
127                 sd = xnew_and_zero(media_data);
128                 sd->length = mksp->size;
129                 sd->seek = 0;
130                 sd->data = mksp->stream_data;
131
132                 /* retrieve the main handle */
133                 sf = sf_open_virtual(sfvio, SFM_READ, sfinfo, (void*)sd);
134                 break;
135         }
136
137         case MKIND_UNKNOWN:
138         case MKIND_FIFO:
139         case MKIND_STREAM:
140         case NUMBER_OF_MEDIA_KINDS:
141         default:
142                 break;
143         }
144
145         if (!sf) {
146                 xfree(sfinfo);
147                 media_stream_set_meths(ms, NULL);
148                 media_stream_driver(ms) = MDRIVER_UNKNOWN;
149                 return NULL;
150         }
151
152         /* now create a substream and fill it with information */
153         mss = make_media_substream_append(ms);
154         media_substream_type(mss) = MTYPE_AUDIO;
155         mtap = xnew_and_zero(mtype_audio_properties);
156
157         mtap->channels = sfinfo->channels;
158         mtap->samplerate = sfinfo->samplerate;
159
160         /* try to find a read function */
161         switch (sfinfo->format & 0xFF) {
162         case SF_FORMAT_ULAW:
163         case SF_FORMAT_ALAW:
164         case SF_FORMAT_PCM_U8:
165         case SF_FORMAT_PCM_S8:
166                 mtap->samplewidth = 8;
167                 mtap->framesize = mtap->channels;
168                 /* stuff is read in as S16 values anyway */
169                 mtap->msf = sxe_msf_S16;
170                 break;
171         case SF_FORMAT_PCM_16:
172                 mtap->samplewidth = 16;
173                 mtap->framesize = mtap->channels * 2;
174                 mtap->msf = sxe_msf_S16;
175                 break;
176         case SF_FORMAT_PCM_24:
177                 mtap->samplewidth = 32;
178                 mtap->framesize = mtap->channels * 4;
179                 mtap->msf = sxe_msf_S24;
180                 break;
181         case SF_FORMAT_PCM_32:
182                 mtap->samplewidth = 32;
183                 mtap->framesize = mtap->channels * 4;
184                 mtap->msf = sxe_msf_S32;
185                 break;
186         case SF_FORMAT_FLOAT:
187                 mtap->samplewidth = 32;
188                 mtap->framesize = mtap->channels * 4;
189                 mtap->msf = sxe_msf_FLT;
190                 break;
191         default:
192                 xfree(sfinfo);
193                 xfree(mtap);
194                 media_stream_set_meths(ms, NULL);
195                 media_stream_driver(ms) = MDRIVER_UNKNOWN;
196                 return NULL;
197                 break;
198         }
199         mtap->name = NULL;
200         mtap->endianness = 0;
201
202         /* now assign */
203         media_substream_type_properties(mss).aprops = mtap;
204         media_stream_set_meths(ms, media_sndfile);
205
206         /* keep the SNDFILE context */
207         sfd = xnew_and_zero(media_sndfile_data);
208         sfd->sf = sf;
209         sfd->sfinfo = sfinfo;
210         media_substream_data(mss) = sfd;
211         media_stream_data(ms) = sfd;
212
213         /* set me as driver indicator */
214         media_stream_driver(ms) = MYSELF;
215
216         return sf;
217 }
218
219 \f
220 static sf_count_t
221 sndfile_vio_get_filelen(void* user_data)
222 {
223         media_data *sd = (media_data*)user_data;
224
225         return sd->length;
226 }
227 static sf_count_t
228 sndfile_vio_seek(sf_count_t offset, int whence, void* user_data)
229 {
230         media_data *sd = (media_data*)user_data;
231
232         switch (whence) {
233         case SEEK_SET:
234                 sd->seek = offset;
235                 break;
236         case SEEK_CUR:
237                 sd->seek = sd->seek+offset;
238                 break;
239         case SEEK_END:
240                 sd->seek = sd->length + offset;
241                 break;
242         default:
243                 /* be harsh */
244                 abort();
245         }
246         return sd->seek;
247 }
248 static sf_count_t
249 sndfile_vio_read(void* ptr, sf_count_t count, void* user_data)
250 {
251         media_data *sd = (media_data*)user_data;
252
253         if (count > sd->length - sd->seek)
254                 count = sd->length - sd->seek;
255         if (count < 0)
256                 count = 0;
257
258         memcpy(ptr, sd->data+sd->seek, count);
259         sd->seek += count;
260         return count;
261 }
262 static sf_count_t
263 sndfile_vio_write(const void* ptr, sf_count_t count, void* user_data)
264 {
265         media_data *sd = (media_data*)user_data;
266
267         memcpy(sd->data+sd->seek, ptr, count);
268         sd->seek += count;
269         return count;
270 }
271 static sf_count_t
272 sndfile_vio_tell(void* user_data)
273 {
274         media_data *sd = (media_data*)user_data;
275
276         return sd->seek;
277 }
278
279 \f
280 static size_t
281 media_sndfile_read(media_substream *mss, void *outbuf, size_t length)
282 {
283 /* read at most `length' frames into `outbuf' */
284         /* libsndfile stuff */
285         media_sndfile_data *sfd;
286         SNDFILE *sf;
287         /* media stream stuff */
288         Lisp_Media_Stream *ms = mss->up;
289         mtype_audio_properties *mtap;
290         media_sample_format_t *fmt;
291         sf_count_t _read = 0;
292
293         /* check the integrity of the media stream */
294         if (media_stream_driver(ms) != MYSELF)
295                 return 0;
296         if (media_substream_type(mss) != MTYPE_AUDIO)
297                 return 0;
298
299         /* fetch the SNDFILE context and our audio props */
300         if ((sfd = media_stream_data(ms)) == NULL ||
301             (sf = sfd->sf) == NULL)
302                 return 0;
303         if (!(mtap = media_substream_type_properties(mss).aprops))
304                 return 0;
305
306         fmt = mtap->msf;
307
308         switch (sfd->sfinfo->format & 0xFF) {
309         case SF_FORMAT_ULAW:
310         case SF_FORMAT_ALAW:
311         case SF_FORMAT_PCM_U8:
312         case SF_FORMAT_PCM_S8:
313         case SF_FORMAT_PCM_16:
314                 _read = sf_readf_short(sf, outbuf, length);
315                 break;
316         case SF_FORMAT_PCM_24:
317         case SF_FORMAT_PCM_32:
318                 _read = sf_readf_int(sf, outbuf, length);
319                 break;
320         case SF_FORMAT_FLOAT:
321                 _read = sf_readf_float(sf, outbuf, length);
322                 break;
323         default:
324                 /* be mean */
325                 abort();
326         }
327
328         /* always convert to internal format */
329         MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, _read*mtap->channels);
330
331         SNDFILE_DEBUG_S("read %d frames\n", _read);
332
333         return _read;
334 }
335
336 static void
337 media_sndfile_rewind(media_substream *mss)
338 {
339 /* rewind the stream to the first frame */
340         /* libsndfile stuff */
341         media_sndfile_data *sfd;
342         SNDFILE *sf;
343         /* media stream stuff */
344         Lisp_Media_Stream *ms = mss->up;
345
346         /* check the integrity of the media stream */
347         if (media_stream_driver(ms) != MYSELF)
348                 return;
349         if (media_substream_type(mss) != MTYPE_AUDIO)
350                 return;
351
352         /* fetch the SNDFILE context and our audio props */
353         if ((sfd = media_stream_data(ms)) == NULL ||
354             (sf = sfd->sf) == NULL)
355                 return;
356
357         SNDFILE_DEBUG_S("rewind stream 0x%x\n", (unsigned int)sf);
358         sf_seek(sf, 0, SEEK_SET);
359 }
360
361 #undef MYSELF