Photo optimisation from Horst
[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 __attribute__((unused)) = 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_T
119         stinfo = &ft->signal;
120 #else
121 #  error "What's the matter with you?! How did you reach this?"
122 #endif
123
124         /* create a substream */
125         mss = make_media_substream_append(ms);
126         media_substream_type(mss) = MTYPE_AUDIO;
127         mtap = xnew_and_zero(mtype_audio_properties);
128
129         mtap->channels = stinfo->channels;
130         mtap->samplerate = stinfo->rate;
131
132         /* try to find a read function */
133 #if defined HAVE_SOX_SIGNALINFO_T_PRECISION
134         mtap->samplewidth = stinfo->precision;
135         mtap->framesize = mtap->channels * (stinfo->precision / 8);
136 #else
137         switch (stinfo->size) {
138         case SXE_SIZE_8BIT:
139                 mtap->samplewidth = 8;
140                 mtap->framesize = mtap->channels;
141                 break;
142         case SXE_SIZE_16BIT:
143                 mtap->samplewidth = 16;
144                 mtap->framesize = mtap->channels * 2;
145                 break;
146         case SXE_SIZE_24BIT:
147                 mtap->samplewidth = 24;
148                 mtap->framesize = mtap->channels * 3;
149                 break;
150         case SXE_SIZE_32BIT:
151                 mtap->samplewidth = 32;
152                 mtap->framesize = mtap->channels * 4;
153                 break;
154         case SXE_SIZE_64BIT:
155                 mtap->samplewidth = 64;
156                 mtap->framesize = mtap->channels * 8;
157                 break;
158         default:
159                 mtap->samplewidth = 32;
160                 mtap->framesize = mtap->channels * 4;
161                 break;
162         }
163 #endif
164         /* since SoX internally uses S32, we bang from S32 */
165         mtap->msf = sxe_msf_S32;
166 #if 0
167         mtap->samplewidth = 8 * sizeof(float);
168         mtap->framesize = mtap->channels * sizeof(float);
169 #endif
170
171         mtap->name = name;
172         mtap->endianness = 0;
173
174         /* now assign */
175         media_substream_type_properties(mss).aprops = mtap;
176         media_stream_set_meths(ms, media_sox);
177
178         /* keep the sox handle */
179         media_stream_data(ms) = ft;
180         media_substream_data(mss) = ft;
181
182         /* set me as driver indicator */
183         media_stream_driver(ms) = MYSELF;
184
185         return ft;
186 }
187
188 static void
189 media_sox_close(ms_driver_data_t arg)
190 {
191         sxe_sox_t ft = arg;
192         sxe_sox_close(ft);
193
194         SOX_DEBUG("closing SoX handle: 0x%lx\n",
195                   (unsigned long int)ft);
196
197         return;
198 }
199
200 \f
201 static size_t
202 media_sox_read(media_substream *mss, void *outbuf, size_t length)
203 {
204 /* read at most `length' frames into `outbuf' */
205         /* stream stuff */
206         Lisp_Media_Stream *ms = mss->up;
207         mtype_audio_properties *mtap;
208         /* libsox stuff */
209         sxe_sox_t ft;
210         sxe_sox_ssize_t samples;
211         sxe_sox_sample_t *bptr;
212         uint16_t framesize __attribute__((unused));
213         media_sample_format_t *fmt;
214
215         /* check the integrity of the media stream */
216         if (media_stream_driver(ms) != MYSELF)
217                 return 0;
218         if (media_substream_type(mss) != MTYPE_AUDIO)
219                 return 0;
220
221         /* fetch the context and our audio props */
222         if (!(ft = media_stream_data(ms)))
223                 return 0;
224         if (!(mtap = media_substream_type_properties(mss).aprops))
225                 return 0;
226
227         framesize = mtap->framesize;
228         fmt = mtap->msf;
229
230         bptr = (sxe_sox_sample_t*)outbuf;
231         samples = sxe_sox_read(ft, bptr, mtap->channels*length);
232
233         SOX_DEBUG_S("read %zd samples\n", samples);
234
235         if (samples < 0)
236                 return 0;
237
238         /* always convert to internal format */
239         MEDIA_SAMPLE_FORMAT_UPSAMPLE(fmt)(outbuf, outbuf, samples);
240
241         /* return number of frames */
242         return samples/mtap->channels;
243 }
244
245 static void
246 media_sox_rewind(media_substream *mss)
247 {
248 /* rewind the stream to the first frame */
249         Lisp_Media_Stream *ms = mss->up;
250         /* libsox stuff */
251         sxe_sox_t ft;
252
253         /* check the integrity of the media stream */
254         if (media_stream_driver(ms) != MYSELF)
255                 return;
256         if (media_substream_type(mss) != MTYPE_AUDIO)
257                 return;
258
259         /* fetch the SNDFILE context and our audio props */
260         if (!(ft = media_stream_data(ms)))
261                 return;
262
263         SOX_DEBUG_S("rewind stream 0x%lx\n", (unsigned long int)ft);
264         sxe_sox_seek(ft, 0, SOX_SEEK_SET);
265 }
266
267 #undef MYSELF