Partially sync files.el from XEmacs 21.5 for wildcard support.
[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 = NULL;
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                 if ( file != NULL ) {
109                         sf = sf_open(file, SFM_READ, sfinfo);
110                 }
111                 break;
112         }
113         case MKIND_STRING: {
114                 mkind_string_properties *mksp = NULL;
115                 SF_VIRTUAL_IO *sfvio = NULL;
116                 /* our container for sfvio */
117                 media_data *sd = NULL;
118
119                 /* prepare sndfile's virtual-I/O */
120                 sfvio = xnew_and_zero(SF_VIRTUAL_IO);
121                 sfvio->get_filelen = &sndfile_vio_get_filelen;
122                 sfvio->seek = &sndfile_vio_seek;
123                 sfvio->read = &sndfile_vio_read;
124                 sfvio->write = &sndfile_vio_write;
125                 sfvio->tell = &sndfile_vio_tell;
126
127                 /* prepare our user_data */
128                 mksp = media_stream_kind_properties(ms).sprops;
129                 sd = xnew_and_zero(media_data);
130                 sd->length = mksp->size;
131                 sd->seek = 0;
132                 sd->data = mksp->stream_data;
133
134                 /* retrieve the main handle */
135                 sf = sf_open_virtual(sfvio, SFM_READ, sfinfo, (void*)sd);
136                 break;
137         }
138
139         case MKIND_UNKNOWN:
140         case MKIND_FIFO:
141         case MKIND_STREAM:
142         case NUMBER_OF_MEDIA_KINDS:
143         default:
144                 break;
145         }
146
147         if (!sf) {
148                 xfree(sfinfo);
149                 media_stream_set_meths(ms, NULL);
150                 media_stream_driver(ms) = MDRIVER_UNKNOWN;
151                 return NULL;
152         }
153
154         /* now create a substream and fill it with information */
155         mss = make_media_substream_append(ms);
156         media_substream_type(mss) = MTYPE_AUDIO;
157         mtap = xnew_and_zero(mtype_audio_properties);
158
159         mtap->channels = sfinfo->channels;
160         mtap->samplerate = sfinfo->samplerate;
161
162         /* try to find a read function */
163         switch (sfinfo->format & 0xFF) {
164         case SF_FORMAT_ULAW:
165         case SF_FORMAT_ALAW:
166         case SF_FORMAT_PCM_U8:
167         case SF_FORMAT_PCM_S8:
168                 mtap->samplewidth = 8;
169                 mtap->framesize = mtap->channels;
170                 /* stuff is read in as S16 values anyway */
171                 mtap->msf = sxe_msf_S16;
172                 break;
173         case SF_FORMAT_PCM_16:
174                 mtap->samplewidth = 16;
175                 mtap->framesize = mtap->channels * 2;
176                 mtap->msf = sxe_msf_S16;
177                 break;
178         case SF_FORMAT_PCM_24:
179                 mtap->samplewidth = 32;
180                 mtap->framesize = mtap->channels * 4;
181                 mtap->msf = sxe_msf_S24;
182                 break;
183         case SF_FORMAT_PCM_32:
184                 mtap->samplewidth = 32;
185                 mtap->framesize = mtap->channels * 4;
186                 mtap->msf = sxe_msf_S32;
187                 break;
188         case SF_FORMAT_FLOAT:
189                 mtap->samplewidth = 32;
190                 mtap->framesize = mtap->channels * 4;
191                 mtap->msf = sxe_msf_FLT;
192                 break;
193         default:
194                 xfree(sfinfo);
195                 xfree(mtap);
196                 media_stream_set_meths(ms, NULL);
197                 media_stream_driver(ms) = MDRIVER_UNKNOWN;
198                 return NULL;
199                 break;
200         }
201         mtap->name = NULL;
202         mtap->endianness = 0;
203
204         /* now assign */
205         media_substream_type_properties(mss).aprops = mtap;
206         media_stream_set_meths(ms, media_sndfile);
207
208         /* keep the SNDFILE context */
209         sfd = xnew_and_zero(media_sndfile_data);
210         sfd->sf = sf;
211         sfd->sfinfo = sfinfo;
212         media_substream_data(mss) = sfd;
213         media_stream_data(ms) = sfd;
214
215         /* set me as driver indicator */
216         media_stream_driver(ms) = MYSELF;
217
218         return sf;
219 }
220
221 \f
222 static sf_count_t
223 sndfile_vio_get_filelen(void* user_data)
224 {
225         media_data *sd = (media_data*)user_data;
226
227         return sd->length;
228 }
229 static sf_count_t
230 sndfile_vio_seek(sf_count_t offset, int whence, void* user_data)
231 {
232         media_data *sd = (media_data*)user_data;
233
234         switch (whence) {
235         case SEEK_SET:
236                 sd->seek = offset;
237                 break;
238         case SEEK_CUR:
239                 sd->seek = sd->seek+offset;
240                 break;
241         case SEEK_END:
242                 sd->seek = sd->length + offset;
243                 break;
244         default:
245                 /* be harsh */
246                 abort();
247         }
248         return sd->seek;
249 }
250 static sf_count_t
251 sndfile_vio_read(void* ptr, sf_count_t count, void* user_data)
252 {
253         media_data *sd = (media_data*)user_data;
254
255         if (count > sd->length - sd->seek)
256                 count = sd->length - sd->seek;
257         if (count < 0)
258                 count = 0;
259
260         memcpy(ptr, sd->data+sd->seek, count);
261         sd->seek += count;
262         return count;
263 }
264 static sf_count_t
265 sndfile_vio_write(const void* ptr, sf_count_t count, void* user_data)
266 {
267         media_data *sd = (media_data*)user_data;
268
269         memcpy(sd->data+sd->seek, ptr, count);
270         sd->seek += count;
271         return count;
272 }
273 static sf_count_t
274 sndfile_vio_tell(void* user_data)
275 {
276         media_data *sd = (media_data*)user_data;
277
278         return sd->seek;
279 }
280
281 \f
282 static size_t
283 media_sndfile_read(media_substream *mss, void *outbuf, size_t length)
284 {
285 /* read at most `length' frames into `outbuf' */
286         /* libsndfile stuff */
287         media_sndfile_data *sfd;
288         SNDFILE *sf;
289         /* media stream stuff */
290         Lisp_Media_Stream *ms = mss->up;
291         mtype_audio_properties *mtap;
292         media_sample_format_t *fmt;
293         sf_count_t _read = 0;
294
295         /* check the integrity of the media stream */
296         if (media_stream_driver(ms) != MYSELF)
297                 return 0;
298         if (media_substream_type(mss) != MTYPE_AUDIO)
299                 return 0;
300
301         /* fetch the SNDFILE context and our audio props */
302         if ((sfd = media_stream_data(ms)) == NULL ||
303             (sf = sfd->sf) == NULL)
304                 return 0;
305         if (!(mtap = media_substream_type_properties(mss).aprops))
306                 return 0;
307
308         fmt = mtap->msf;
309
310         switch (sfd->sfinfo->format & 0xFF) {
311         case SF_FORMAT_ULAW:
312         case SF_FORMAT_ALAW:
313         case SF_FORMAT_PCM_U8:
314         case SF_FORMAT_PCM_S8:
315         case SF_FORMAT_PCM_16:
316                 _read = sf_readf_short(sf, outbuf, length);
317                 break;
318         case SF_FORMAT_PCM_24:
319         case SF_FORMAT_PCM_32:
320                 _read = sf_readf_int(sf, outbuf, length);
321                 break;
322         case SF_FORMAT_FLOAT:
323                 _read = sf_readf_float(sf, outbuf, length);
324                 break;
325         default:
326                 /* be mean */
327                 abort();
328         }
329
330         /* always convert to internal format */
331         MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, _read*mtap->channels);
332
333         SNDFILE_DEBUG_S("read %d frames\n", _read);
334
335         return _read;
336 }
337
338 static void
339 media_sndfile_rewind(media_substream *mss)
340 {
341 /* rewind the stream to the first frame */
342         /* libsndfile stuff */
343         media_sndfile_data *sfd;
344         SNDFILE *sf;
345         /* media stream stuff */
346         Lisp_Media_Stream *ms = mss->up;
347
348         /* check the integrity of the media stream */
349         if (media_stream_driver(ms) != MYSELF)
350                 return;
351         if (media_substream_type(mss) != MTYPE_AUDIO)
352                 return;
353
354         /* fetch the SNDFILE context and our audio props */
355         if ((sfd = media_stream_data(ms)) == NULL ||
356             (sf = sfd->sf) == NULL)
357                 return;
358
359         SNDFILE_DEBUG_S("rewind stream 0x%x\n", (unsigned int)sf);
360         sf_seek(sf, 0, SEEK_SET);
361 }
362
363 #undef MYSELF