Merge remote-tracking branch 'origin/master' into for-steve
[sxemacs] / src / media / media-sox.c
1 /* media-sox.c - analyse audio files or streams via sox
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 /*
33 lisp.h defined UNUSED which also gets defined in some versions of SoX
34 in an incompatible fashion.  We don't need that macro here...
35 */
36 #undef UNUSED
37 #include "media-sox.h"
38
39 #define MYSELF MDRIVER_SOX
40
41 Lisp_Object Qsox;
42
43 #define __SOX_DEBUG__(args...)          fprintf(stderr, "SOX " args)
44 #ifndef SOX_DEBUG_FLAG
45 #define SOX_DEBUG(args...)
46 #else
47 #define SOX_DEBUG(args...)              __SOX_DEBUG__(args)
48 #endif
49 #define SOX_DEBUG_S(args...)            SOX_DEBUG("[stream]: " args)
50 #define SOX_CRITICAL(args...)           __SOX_DEBUG__("CRITICAL: " args)
51
52 \f
53 DECLARE_MEDIA_DRIVER_OPEN_METH(media_sox);
54 DECLARE_MEDIA_DRIVER_CLOSE_METH(media_sox);
55 DECLARE_MEDIA_DRIVER_READ_METH(media_sox);
56 DECLARE_MEDIA_DRIVER_REWIND_METH(media_sox);
57
58 DEFINE_MEDIA_DRIVER_CUSTOM(media_sox,
59                            media_sox_open, media_sox_close,
60                            NULL, NULL,
61                            media_sox_read, NULL,
62                            media_sox_rewind, NULL);
63
64 \f
65 /* called from util.c::st_fail() */
66 void cleanup(void)
67 {
68 }
69
70 \f
71 static ms_driver_data_t
72 media_sox_open(Lisp_Media_Stream *ms)
73 {
74         media_substream *mss;
75         mtype_audio_properties *mtap;
76         char *name = NULL;
77         /* libsndfile stuff */
78         sxe_sox_t ft = NULL;
79         sxe_sox_signalinfo_t *stinfo = NULL;
80
81         /* initialise */
82         switch (media_stream_kind(ms)) {
83         case MKIND_FILE: {
84                 mkind_file_properties *mkfp = NULL;
85                 const char *file = NULL;
86                 int file_len = 0;
87
88                 /* open the file */
89                 mkfp = media_stream_kind_properties(ms).fprops;
90                 TO_EXTERNAL_FORMAT(LISP_STRING, mkfp->filename,
91                                    ALLOCA, (file, file_len), Qnil);
92                 if( file != NULL ) {
93 #if defined HAVE_SOX_OPEN_READ_3ARGS
94                             ft = sxe_sox_open_read(file, NULL, NULL);
95 #elif defined HAVE_SOX_OPEN_READ_4ARGS
96                             ft = sxe_sox_open_read(file, NULL, NULL, NULL);
97 #else
98 # error You shouldnt be here.  Wake up before you try to compile me.
99 #endif
100                 }
101                 break;
102         }
103         case MKIND_STRING: {
104                 /* not yet handable */
105                 break;
106         }
107         default:
108                 break;
109         }
110
111         if (!ft) {
112                 media_stream_set_meths(ms, NULL);
113                 media_stream_driver(ms) = MDRIVER_UNKNOWN;
114                 return NULL;
115         }
116
117         /* retrieve the signal information */
118 #if defined HAVE_STRUCT_SOX_FORMAT
119         stinfo = &ft->signal;
120 #elif defined MEMBER_STRUCT_ST_SOUNDSTREAM_SIGNAL
121         stinfo = &ft->signal;
122 #elif defined MEMBER_STRUCT_ST_SOUNDSTREAM_INFO
123         stinfo = &ft->info;
124 #else
125 #  error "What's the matter with you?! How did you reach this?"
126 #endif
127
128         /* create a substream */
129         mss = make_media_substream_append(ms);
130         media_substream_type(mss) = MTYPE_AUDIO;
131         mtap = xnew_and_zero(mtype_audio_properties);
132
133         mtap->channels = stinfo->channels;
134         mtap->samplerate = stinfo->rate;
135
136         /* try to find a read function */
137 #if defined HAVE_SOX_SIGNALINFO_T_PRECISION
138         mtap->samplewidth = stinfo->precision;
139         mtap->framesize = mtap->channels * (stinfo->precision / 8);
140 #else
141         switch (stinfo->size) {
142         case SXE_SIZE_8BIT:
143                 mtap->samplewidth = 8;
144                 mtap->framesize = mtap->channels;
145                 break;
146         case SXE_SIZE_16BIT:
147                 mtap->samplewidth = 16;
148                 mtap->framesize = mtap->channels * 2;
149                 break;
150         case SXE_SIZE_24BIT:
151                 mtap->samplewidth = 24;
152                 mtap->framesize = mtap->channels * 3;
153                 break;
154         case SXE_SIZE_32BIT:
155                 mtap->samplewidth = 32;
156                 mtap->framesize = mtap->channels * 4;
157                 break;
158         case SXE_SIZE_64BIT:
159                 mtap->samplewidth = 64;
160                 mtap->framesize = mtap->channels * 8;
161                 break;
162         default:
163                 mtap->samplewidth = 32;
164                 mtap->framesize = mtap->channels * 4;
165                 break;
166         }
167 #endif
168         /* since SoX internally uses S32, we bang from S32 */
169         mtap->msf = sxe_msf_S32;
170 #if 0
171         mtap->samplewidth = 8 * sizeof(float);
172         mtap->framesize = mtap->channels * sizeof(float);
173 #endif
174
175         mtap->name = name;
176         mtap->endianness = 0;
177
178         /* now assign */
179         media_substream_type_properties(mss).aprops = mtap;
180         media_stream_set_meths(ms, media_sox);
181
182         /* keep the sox handle */
183         media_stream_data(ms) = ft;
184         media_substream_data(mss) = ft;
185
186         /* set me as driver indicator */
187         media_stream_driver(ms) = MYSELF;
188
189         return ft;
190 }
191
192 static void
193 media_sox_close(ms_driver_data_t arg)
194 {
195         sxe_sox_t ft = arg;
196         sxe_sox_close(ft);
197
198         SOX_DEBUG("closing SoX handle: 0x%x\n",
199                   (unsigned int)ft);
200
201         return;
202 }
203
204 \f
205 static size_t
206 media_sox_read(media_substream *mss, void *outbuf, size_t length)
207 {
208 /* read at most `length' frames into `outbuf' */
209         /* stream stuff */
210         Lisp_Media_Stream *ms = mss->up;
211         mtype_audio_properties *mtap;
212         /* libst stuff */
213         sxe_sox_t ft;
214         sxe_sox_ssize_t samples;
215         sxe_sox_sample_t *bptr;
216         uint16_t framesize;
217         media_sample_format_t *fmt;
218
219         /* check the integrity of the media stream */
220         if (media_stream_driver(ms) != MYSELF)
221                 return 0;
222         if (media_substream_type(mss) != MTYPE_AUDIO)
223                 return 0;
224
225         /* fetch the context and our audio props */
226         if (!(ft = media_stream_data(ms)))
227                 return 0;
228         if (!(mtap = media_substream_type_properties(mss).aprops))
229                 return 0;
230
231         framesize = mtap->framesize;
232         fmt = mtap->msf;
233
234         bptr = (sxe_sox_sample_t*)outbuf;
235         samples = sxe_sox_read(ft, bptr, mtap->channels*length);
236
237         SOX_DEBUG_S("read %d samples\n", samples);
238
239         if (samples < 0)
240                 return 0;
241
242         /* always convert to internal format */
243         MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, samples);
244
245         /* return number of frames */
246         return samples/mtap->channels;
247 }
248
249 static void
250 media_sox_rewind(media_substream *mss)
251 {
252 /* rewind the stream to the first frame */
253         Lisp_Media_Stream *ms = mss->up;
254         /* libst stuff */
255         sxe_sox_t ft;
256
257         /* check the integrity of the media stream */
258         if (media_stream_driver(ms) != MYSELF)
259                 return;
260         if (media_substream_type(mss) != MTYPE_AUDIO)
261                 return;
262
263         /* fetch the SNDFILE context and our audio props */
264         if (!(ft = media_stream_data(ms)))
265                 return;
266
267         SOX_DEBUG_S("rewind stream 0x%x\n", (unsigned int)ft);
268         sxe_sox_seek(ft, 0, SEEK_SET);
269 }
270
271 #undef MYSELF