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 if ( file != NULL ) {
109 sf = sf_open(file, SFM_READ, sfinfo);
114 mkind_string_properties *mksp = NULL;
115 SF_VIRTUAL_IO *sfvio = NULL;
116 /* our container for sfvio */
117 media_data *sd = NULL;
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;
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;
132 sd->data = mksp->stream_data;
134 /* retrieve the main handle */
135 sf = sf_open_virtual(sfvio, SFM_READ, sfinfo, (void*)sd);
142 case NUMBER_OF_MEDIA_KINDS:
149 media_stream_set_meths(ms, NULL);
150 media_stream_driver(ms) = MDRIVER_UNKNOWN;
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);
159 mtap->channels = sfinfo->channels;
160 mtap->samplerate = sfinfo->samplerate;
162 /* try to find a read function */
163 switch (sfinfo->format & 0xFF) {
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;
173 case SF_FORMAT_PCM_16:
174 mtap->samplewidth = 16;
175 mtap->framesize = mtap->channels * 2;
176 mtap->msf = sxe_msf_S16;
178 case SF_FORMAT_PCM_24:
179 mtap->samplewidth = 32;
180 mtap->framesize = mtap->channels * 4;
181 mtap->msf = sxe_msf_S24;
183 case SF_FORMAT_PCM_32:
184 mtap->samplewidth = 32;
185 mtap->framesize = mtap->channels * 4;
186 mtap->msf = sxe_msf_S32;
188 case SF_FORMAT_FLOAT:
189 mtap->samplewidth = 32;
190 mtap->framesize = mtap->channels * 4;
191 mtap->msf = sxe_msf_FLT;
196 media_stream_set_meths(ms, NULL);
197 media_stream_driver(ms) = MDRIVER_UNKNOWN;
202 mtap->endianness = 0;
205 media_substream_type_properties(mss).aprops = mtap;
206 media_stream_set_meths(ms, media_sndfile);
208 /* keep the SNDFILE context */
209 sfd = xnew_and_zero(media_sndfile_data);
211 sfd->sfinfo = sfinfo;
212 media_substream_data(mss) = sfd;
213 media_stream_data(ms) = sfd;
215 /* set me as driver indicator */
216 media_stream_driver(ms) = MYSELF;
223 sndfile_vio_get_filelen(void* user_data)
225 media_data *sd = (media_data*)user_data;
230 sndfile_vio_seek(sf_count_t offset, int whence, void* user_data)
232 media_data *sd = (media_data*)user_data;
239 sd->seek = sd->seek+offset;
242 sd->seek = sd->length + offset;
251 sndfile_vio_read(void* ptr, sf_count_t count, void* user_data)
253 media_data *sd = (media_data*)user_data;
255 if (count > sd->length - sd->seek)
256 count = sd->length - sd->seek;
260 memcpy(ptr, sd->data+sd->seek, count);
265 sndfile_vio_write(const void* ptr, sf_count_t count, void* user_data)
267 media_data *sd = (media_data*)user_data;
269 memcpy(sd->data+sd->seek, ptr, count);
274 sndfile_vio_tell(void* user_data)
276 media_data *sd = (media_data*)user_data;
283 media_sndfile_read(media_substream *mss, void *outbuf, size_t length)
285 /* read at most `length' frames into `outbuf' */
286 /* libsndfile stuff */
287 media_sndfile_data *sfd;
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;
295 /* check the integrity of the media stream */
296 if (media_stream_driver(ms) != MYSELF)
298 if (media_substream_type(mss) != MTYPE_AUDIO)
301 /* fetch the SNDFILE context and our audio props */
302 if ((sfd = media_stream_data(ms)) == NULL ||
303 (sf = sfd->sf) == NULL)
305 if (!(mtap = media_substream_type_properties(mss).aprops))
310 switch (sfd->sfinfo->format & 0xFF) {
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);
318 case SF_FORMAT_PCM_24:
319 case SF_FORMAT_PCM_32:
320 _read = sf_readf_int(sf, outbuf, length);
322 case SF_FORMAT_FLOAT:
323 _read = sf_readf_float(sf, outbuf, length);
330 /* always convert to internal format */
331 MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, _read*mtap->channels);
333 SNDFILE_DEBUG_S("read %d frames\n", _read);
339 media_sndfile_rewind(media_substream *mss)
341 /* rewind the stream to the first frame */
342 /* libsndfile stuff */
343 media_sndfile_data *sfd;
345 /* media stream stuff */
346 Lisp_Media_Stream *ms = mss->up;
348 /* check the integrity of the media stream */
349 if (media_stream_driver(ms) != MYSELF)
351 if (media_substream_type(mss) != MTYPE_AUDIO)
354 /* fetch the SNDFILE context and our audio props */
355 if ((sfd = media_stream_data(ms)) == NULL ||
356 (sf = sfd->sf) == NULL)
359 SNDFILE_DEBUG_S("rewind stream 0x%x\n", (unsigned int)sf);
360 sf_seek(sf, 0, SEEK_SET);