1 /* media-sndfile.c - analyse audio files or streams
3 Copyright (C) 2006 Sebastian Freundt
5 This file is part of SXEmacs
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.
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.
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/>. */
21 /* Synched up with: Not in FSF. */
32 #include "media-sndfile.h"
36 #define MYSELF MDRIVER_SNDFILE
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*);
44 #define __SNDFILE_DEBUG__(args...) fprintf(stderr, "SNDFILE " args)
45 #ifndef SNDFILE_DEBUG_FLAG
46 #define SNDFILE_DEBUG(args...)
48 #define SNDFILE_DEBUG(args...) __SNDFILE_DEBUG__(args)
50 #define SNDFILE_DEBUG_S(args...) SNDFILE_DEBUG("[stream]: " args)
51 #define SNDFILE_CRITICAL(args...) __SNDFILE_DEBUG__("CRITICAL: " args)
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);
59 DEFINE_MEDIA_DRIVER_CUSTOM(media_sndfile,
60 media_sndfile_open, media_sndfile_close,
62 media_sndfile_read, NULL,
63 media_sndfile_rewind, NULL);
67 media_sndfile_close(ms_driver_data_t arg)
69 media_sndfile_data *sfd = arg;
71 SNDFILE_DEBUG("closing sndfile handle: 0x%x\n",
72 (unsigned int)sfd->sf);
85 static ms_driver_data_t
86 media_sndfile_open(Lisp_Media_Stream *ms)
88 mtype_audio_properties *mtap;
90 /* libsndfile stuff */
91 media_sndfile_data *sfd = NULL;
96 sfinfo = xnew_and_zero(SF_INFO);
98 switch (media_stream_kind(ms)) {
100 mkind_file_properties *mkfp = NULL;
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);
112 mkind_string_properties *mksp = NULL;
113 SF_VIRTUAL_IO *sfvio = NULL;
114 /* our container for sfvio */
115 media_data *sd = NULL;
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;
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;
130 sd->data = mksp->stream_data;
132 /* retrieve the main handle */
133 sf = sf_open_virtual(sfvio, SFM_READ, sfinfo, (void*)sd);
140 case NUMBER_OF_MEDIA_KINDS:
147 media_stream_set_meths(ms, NULL);
148 media_stream_driver(ms) = MDRIVER_UNKNOWN;
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);
157 mtap->channels = sfinfo->channels;
158 mtap->samplerate = sfinfo->samplerate;
160 /* try to find a read function */
161 switch (sfinfo->format & 0xFF) {
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;
171 case SF_FORMAT_PCM_16:
172 mtap->samplewidth = 16;
173 mtap->framesize = mtap->channels * 2;
174 mtap->msf = sxe_msf_S16;
176 case SF_FORMAT_PCM_24:
177 mtap->samplewidth = 32;
178 mtap->framesize = mtap->channels * 4;
179 mtap->msf = sxe_msf_S24;
181 case SF_FORMAT_PCM_32:
182 mtap->samplewidth = 32;
183 mtap->framesize = mtap->channels * 4;
184 mtap->msf = sxe_msf_S32;
186 case SF_FORMAT_FLOAT:
187 mtap->samplewidth = 32;
188 mtap->framesize = mtap->channels * 4;
189 mtap->msf = sxe_msf_FLT;
194 media_stream_set_meths(ms, NULL);
195 media_stream_driver(ms) = MDRIVER_UNKNOWN;
200 mtap->endianness = 0;
203 media_substream_type_properties(mss).aprops = mtap;
204 media_stream_set_meths(ms, media_sndfile);
206 /* keep the SNDFILE context */
207 sfd = xnew_and_zero(media_sndfile_data);
209 sfd->sfinfo = sfinfo;
210 media_substream_data(mss) = sfd;
211 media_stream_data(ms) = sfd;
213 /* set me as driver indicator */
214 media_stream_driver(ms) = MYSELF;
221 sndfile_vio_get_filelen(void* user_data)
223 media_data *sd = (media_data*)user_data;
228 sndfile_vio_seek(sf_count_t offset, int whence, void* user_data)
230 media_data *sd = (media_data*)user_data;
237 sd->seek = sd->seek+offset;
240 sd->seek = sd->length + offset;
249 sndfile_vio_read(void* ptr, sf_count_t count, void* user_data)
251 media_data *sd = (media_data*)user_data;
253 if (count > sd->length - sd->seek)
254 count = sd->length - sd->seek;
258 memcpy(ptr, sd->data+sd->seek, count);
263 sndfile_vio_write(const void* ptr, sf_count_t count, void* user_data)
265 media_data *sd = (media_data*)user_data;
267 memcpy(sd->data+sd->seek, ptr, count);
272 sndfile_vio_tell(void* user_data)
274 media_data *sd = (media_data*)user_data;
281 media_sndfile_read(media_substream *mss, void *outbuf, size_t length)
283 /* read at most `length' frames into `outbuf' */
284 /* libsndfile stuff */
285 media_sndfile_data *sfd;
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;
293 /* check the integrity of the media stream */
294 if (media_stream_driver(ms) != MYSELF)
296 if (media_substream_type(mss) != MTYPE_AUDIO)
299 /* fetch the SNDFILE context and our audio props */
300 if ((sfd = media_stream_data(ms)) == NULL ||
301 (sf = sfd->sf) == NULL)
303 if (!(mtap = media_substream_type_properties(mss).aprops))
308 switch (sfd->sfinfo->format & 0xFF) {
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);
316 case SF_FORMAT_PCM_24:
317 case SF_FORMAT_PCM_32:
318 _read = sf_readf_int(sf, outbuf, length);
320 case SF_FORMAT_FLOAT:
321 _read = sf_readf_float(sf, outbuf, length);
328 /* always convert to internal format */
329 MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, _read*mtap->channels);
331 SNDFILE_DEBUG_S("read %d frames\n", _read);
337 media_sndfile_rewind(media_substream *mss)
339 /* rewind the stream to the first frame */
340 /* libsndfile stuff */
341 media_sndfile_data *sfd;
343 /* media stream stuff */
344 Lisp_Media_Stream *ms = mss->up;
346 /* check the integrity of the media stream */
347 if (media_stream_driver(ms) != MYSELF)
349 if (media_substream_type(mss) != MTYPE_AUDIO)
352 /* fetch the SNDFILE context and our audio props */
353 if ((sfd = media_stream_data(ms)) == NULL ||
354 (sf = sfd->sf) == NULL)
357 SNDFILE_DEBUG_S("rewind stream 0x%x\n", (unsigned int)sf);
358 sf_seek(sf, 0, SEEK_SET);