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;
101 const char *file = NULL;
105 mkfp = media_stream_kind_properties(ms).fprops;
106 TO_EXTERNAL_FORMAT(LISP_STRING, mkfp->filename,
107 ALLOCA, (file, file_len), Qnil);
108 SXE_SET_UNUSED(file_len);
110 if ( file != NULL ) {
111 sf = sf_open(file, SFM_READ, sfinfo);
116 mkind_string_properties *mksp = NULL;
117 SF_VIRTUAL_IO *sfvio = NULL;
118 /* our container for sfvio */
119 media_data *sd = NULL;
121 /* prepare sndfile's virtual-I/O */
122 sfvio = xnew_and_zero(SF_VIRTUAL_IO);
123 sfvio->get_filelen = &sndfile_vio_get_filelen;
124 sfvio->seek = &sndfile_vio_seek;
125 sfvio->read = &sndfile_vio_read;
126 sfvio->write = &sndfile_vio_write;
127 sfvio->tell = &sndfile_vio_tell;
129 /* prepare our user_data */
130 mksp = media_stream_kind_properties(ms).sprops;
131 sd = xnew_and_zero(media_data);
132 sd->length = mksp->size;
134 sd->data = mksp->stream_data;
136 /* retrieve the main handle */
137 sf = sf_open_virtual(sfvio, SFM_READ, sfinfo, (void*)sd);
144 case NUMBER_OF_MEDIA_KINDS:
151 media_stream_set_meths(ms, NULL);
152 media_stream_driver(ms) = MDRIVER_UNKNOWN;
156 /* now create a substream and fill it with information */
157 mss = make_media_substream_append(ms);
158 media_substream_type(mss) = MTYPE_AUDIO;
159 mtap = xnew_and_zero(mtype_audio_properties);
161 mtap->channels = sfinfo->channels;
162 mtap->samplerate = sfinfo->samplerate;
164 /* try to find a read function */
165 switch (sfinfo->format & 0xFF) {
168 case SF_FORMAT_PCM_U8:
169 case SF_FORMAT_PCM_S8:
170 mtap->samplewidth = 8;
171 mtap->framesize = mtap->channels;
172 /* stuff is read in as S16 values anyway */
173 mtap->msf = sxe_msf_S16;
175 case SF_FORMAT_PCM_16:
176 mtap->samplewidth = 16;
177 mtap->framesize = mtap->channels * 2;
178 mtap->msf = sxe_msf_S16;
180 case SF_FORMAT_PCM_24:
181 mtap->samplewidth = 32;
182 mtap->framesize = mtap->channels * 4;
183 mtap->msf = sxe_msf_S24;
185 case SF_FORMAT_PCM_32:
186 mtap->samplewidth = 32;
187 mtap->framesize = mtap->channels * 4;
188 mtap->msf = sxe_msf_S32;
190 case SF_FORMAT_FLOAT:
191 mtap->samplewidth = 32;
192 mtap->framesize = mtap->channels * 4;
193 mtap->msf = sxe_msf_FLT;
198 media_stream_set_meths(ms, NULL);
199 media_stream_driver(ms) = MDRIVER_UNKNOWN;
204 mtap->endianness = 0;
207 media_substream_type_properties(mss).aprops = mtap;
208 media_stream_set_meths(ms, media_sndfile);
210 /* keep the SNDFILE context */
211 sfd = xnew_and_zero(media_sndfile_data);
213 sfd->sfinfo = sfinfo;
214 media_substream_data(mss) = sfd;
215 media_stream_data(ms) = sfd;
217 /* set me as driver indicator */
218 media_stream_driver(ms) = MYSELF;
225 sndfile_vio_get_filelen(void* user_data)
227 media_data *sd = (media_data*)user_data;
232 sndfile_vio_seek(sf_count_t offset, int whence, void* user_data)
234 media_data *sd = (media_data*)user_data;
241 sd->seek = sd->seek+offset;
244 sd->seek = sd->length + offset;
253 sndfile_vio_read(void* ptr, sf_count_t count, void* user_data)
255 media_data *sd = (media_data*)user_data;
257 if (count > sd->length - sd->seek)
258 count = sd->length - sd->seek;
262 memcpy(ptr, sd->data+sd->seek, count);
267 sndfile_vio_write(const void* ptr, sf_count_t count, void* user_data)
269 media_data *sd = (media_data*)user_data;
271 memcpy(sd->data+sd->seek, ptr, count);
276 sndfile_vio_tell(void* user_data)
278 media_data *sd = (media_data*)user_data;
285 media_sndfile_read(media_substream *mss, void *outbuf, size_t length)
287 /* read at most `length' frames into `outbuf' */
288 /* libsndfile stuff */
289 media_sndfile_data *sfd;
291 /* media stream stuff */
292 Lisp_Media_Stream *ms = mss->up;
293 mtype_audio_properties *mtap;
294 media_sample_format_t *fmt;
295 sf_count_t _read = 0;
297 /* check the integrity of the media stream */
298 if (media_stream_driver(ms) != MYSELF)
300 if (media_substream_type(mss) != MTYPE_AUDIO)
303 /* fetch the SNDFILE context and our audio props */
304 if ((sfd = media_stream_data(ms)) == NULL ||
305 (sf = sfd->sf) == NULL)
307 if (!(mtap = media_substream_type_properties(mss).aprops))
312 switch (sfd->sfinfo->format & 0xFF) {
315 case SF_FORMAT_PCM_U8:
316 case SF_FORMAT_PCM_S8:
317 case SF_FORMAT_PCM_16:
318 _read = sf_readf_short(sf, outbuf, length);
320 case SF_FORMAT_PCM_24:
321 case SF_FORMAT_PCM_32:
322 _read = sf_readf_int(sf, outbuf, length);
324 case SF_FORMAT_FLOAT:
325 _read = sf_readf_float(sf, outbuf, length);
332 /* always convert to internal format */
333 MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, _read*mtap->channels);
335 SNDFILE_DEBUG_S("read %d frames\n", _read);
341 media_sndfile_rewind(media_substream *mss)
343 /* rewind the stream to the first frame */
344 /* libsndfile stuff */
345 media_sndfile_data *sfd;
347 /* media stream stuff */
348 Lisp_Media_Stream *ms = mss->up;
350 /* check the integrity of the media stream */
351 if (media_stream_driver(ms) != MYSELF)
353 if (media_substream_type(mss) != MTYPE_AUDIO)
356 /* fetch the SNDFILE context and our audio props */
357 if ((sfd = media_stream_data(ms)) == NULL ||
358 (sf = sfd->sf) == NULL)
361 SNDFILE_DEBUG_S("rewind stream 0x%x\n", (unsigned int)sf);
362 sf_seek(sf, 0, SEEK_SET);