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