1 /*
2 * Copyright (C) 1999-2001 Claude Barras and Kare Sjolander
3 */
4
5 #include <tcl.h>
6 #include "snack.h"
7 #include <sp/sphere.h>
8
9 #if defined(__WIN32__)
10 # define WIN32_LEAN_AND_MEAN
11 # include <windows.h>
12 # undef WIN32_LEAN_AND_MEAN
13 # define EXPORT(a,b) __declspec(dllexport) a b
14 BOOL APIENTRY
DllMain(HINSTANCE hInst,DWORD reason,LPVOID reserved)15 DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved)
16 {
17 return TRUE;
18 }
19 #else
20 # define EXPORT(a,b) a b
21 #endif
22
23 #define fail(a) { \
24 Tcl_AppendResult(interp, a, NULL); \
25 if (ch) sp_close((SP_FILE *)ch); \
26 return TCL_ERROR; \
27 }
28
29 #define NIST_STRING "NIST"
30 #define SPHERE_STRING "SPHERE"
31 #define SPHERE_HEADERSIZE 1024
32 #define SNACK_SPHERE_INT 17
33
34 static int
35 GetSphereHeader(Sound *s, Tcl_Interp *interp, Tcl_Channel ch, Tcl_Obj *obj,
36 char *buf);
37
38 static char *
GuessSphereFile(char * buf,int len)39 GuessSphereFile(char *buf, int len)
40 {
41 if (len < (int) strlen(NIST_STRING)) return(QUE_STRING);
42 if (strncasecmp(NIST_STRING, buf, strlen(NIST_STRING)) == 0) {
43 return(SPHERE_STRING);
44 }
45 return(NULL);
46 }
47
48 char *
ExtSphereFile(char * s)49 ExtSphereFile(char *s)
50 {
51 int l1 = strlen(".sph");
52 int l2 = strlen(s);
53
54 if (strncasecmp(".sph", &s[l2 - l1], l1) == 0) {
55 return(SPHERE_STRING);
56 }
57 return(NULL);
58 }
59
60 #define SPHERE_BUFFER_SIZE 100000
61
62 static int
OpenSphereFile(Sound * s,Tcl_Interp * interp,Tcl_Channel * ch,char * mode)63 OpenSphereFile(Sound *s, Tcl_Interp *interp, Tcl_Channel *ch, char *mode)
64 {
65 /* open sphere file */
66 *ch = (Tcl_Channel) sp_open(Snack_GetSoundFilename(s), mode);
67 if (*ch == NULL) {
68 Tcl_AppendResult(interp, "SPHERE: unable to open file: ",
69 Snack_GetSoundFilename(s), NULL);
70 return TCL_ERROR;
71 }
72 /*
73 Hack for the pculaw format. Read header again because
74 sp_set_data_mode() has to be called.
75 */
76 GetSphereHeader(s, interp, *ch, NULL, NULL);
77
78 if (s->extHead != NULL && s->extHeadType != SNACK_SPHERE_INT) {
79 Snack_FileFormat *ff;
80
81 for (ff = Snack_GetFileFormats(); ff != NULL; ff = ff->nextPtr) {
82 if (strcmp(s->fileType, ff->name) == 0) {
83 if (ff->freeHeaderProc != NULL) {
84 (ff->freeHeaderProc)(s);
85 }
86 }
87 }
88 }
89
90 if (s->extHead == NULL) {
91 s->extHead = ckalloc(sizeof(short) * SPHERE_BUFFER_SIZE);
92 s->extHeadType = SNACK_SPHERE_INT;
93 }
94
95 return TCL_OK;
96 }
97
98 static int
CloseSphereFile(Sound * s,Tcl_Interp * interp,Tcl_Channel * ch)99 CloseSphereFile(Sound *s, Tcl_Interp *interp, Tcl_Channel *ch)
100 {
101 /* close */
102 if (sp_close((SP_FILE *)*ch))
103 fail("SPHERE: error closing file");
104 *ch = NULL;
105
106 return TCL_OK;
107 }
108
109 static int
ReadSphereSamples(Sound * s,Tcl_Interp * interp,Tcl_Channel ch,char * ibuf,float * obuf,int len)110 ReadSphereSamples(Sound *s, Tcl_Interp *interp, Tcl_Channel ch, char *ibuf,
111 float *obuf, int len)
112 {
113 int tot = len / Snack_GetNumChannels(s);
114 int i = 0, le = Snack_PlatformIsLittleEndian();
115 unsigned char *q = (unsigned char *) s->extHead;
116 char *sc = (char *) s->extHead;
117 short *r = (short *) s->extHead;
118 int *is = (int *) s->extHead;
119 float *fs = (float *) s->extHead;
120 float *f = obuf;
121 int size = min(tot, SPHERE_BUFFER_SIZE / Snack_GetNumChannels(s));
122 int read = sp_read_data(s->extHead, size, (SP_FILE *)ch);
123
124 if (!(sp_error((SP_FILE *)ch) == 0 || sp_error((SP_FILE *)ch) == 101)) {
125 return -1;
126 }
127
128 for (i = 0; i < read * Snack_GetNumChannels(s); i++) {
129 switch (s->encoding) {
130 case LIN16:
131 if (s->swap) *r = Snack_SwapShort(*r);
132 *f++ = (float) *r++;
133 break;
134 case LIN32:
135 if (s->swap) *is = Snack_SwapLong(*is);
136 *f++ = (float) *is++;
137 break;
138 case SNACK_FLOAT:
139 if (s->swap) *fs = (float) Snack_SwapLong((int)*fs);
140 *f++ = (float) *fs++;
141 break;
142 case ALAW:
143 *f++ = (float) Snack_Alaw2Lin(*q++);
144 break;
145 case MULAW:
146 *f++ = (float) Snack_Mulaw2Lin(*q++);
147 break;
148 case LIN8:
149 *f++ = (float) *sc++;
150 break;
151 case LIN8OFFSET:
152 *f++ = (float) *q++;
153 break;
154 case LIN24:
155 {
156 int ee;
157 if (s->swap) {
158 if (le) {
159 ee = 0;
160 } else {
161 ee = 1;
162 }
163 } else {
164 if (le) {
165 ee = 1;
166 } else {
167 ee = 0;
168 }
169 }
170 if (ee) {
171 int t = *q++;
172 t |= *q++ << 8;
173 t |= *q++ << 16;
174 if (t & 0x00800000) {
175 t |= (unsigned int) 0xff000000;
176 }
177 *f++ = (float) t;
178 } else {
179 int t = *q++ << 16;
180 t |= *q++ << 8;
181 t |= *q++;
182 if (t & 0x00800000) {
183 t |= (unsigned int) 0xff000000;
184 }
185 *f++ = (float) t;
186 }
187 break;
188 }
189 }
190 }
191
192 return(i);
193 }
194
195 static int
SeekSphereFile(Sound * s,Tcl_Interp * interp,Tcl_Channel ch,int pos)196 SeekSphereFile(Sound *s, Tcl_Interp *interp, Tcl_Channel ch, int pos)
197 {
198 /* seek to pos */
199
200 if (sp_seek((SP_FILE *)ch, pos, 0)) {
201 return(-1);
202 } else {
203 return(pos);
204 }
205 }
206
207 static int
GetSphereHeader(Sound * s,Tcl_Interp * interp,Tcl_Channel ch,Tcl_Obj * obj,char * buf)208 GetSphereHeader(Sound *s, Tcl_Interp *interp, Tcl_Channel ch, Tcl_Obj *obj,
209 char *buf)
210 {
211 /* SPHERE file and header fields */
212 long sample_rate = 16000;
213 long channel_count = 1;
214 long sample_n_bytes = 2;
215 long sample_cnt = 0;
216 char *sample_coding = "";
217
218 if (obj != NULL)
219 fail("'data' subcommand forbidden for NIST/SPHERE format");
220
221 if (Snack_GetDebugFlag(s) > 2) {
222 Snack_WriteLog(" Reading NIST/SPHERE header\n");
223 }
224
225 /* sample_rate */
226 if (sp_h_get_field((SP_FILE *)ch, "sample_rate", T_INTEGER,
227 (void *)&sample_rate) > 0)
228 fail("SPHERE: unable to read sample_rate");
229
230 Snack_SetSampleRate(s, sample_rate);
231 if (Snack_GetDebugFlag(s) > 3) {
232 Snack_WriteLogInt(" Setting rate", Snack_GetSampleRate(s));
233 }
234
235 /* sample_n_bytes */
236 if (sp_h_get_field((SP_FILE *)ch, "sample_n_bytes", T_INTEGER,
237 (void *)&sample_n_bytes) > 0)
238 fail("SPHERE: unable to read sample_n_bytes");
239 Snack_SetBytesPerSample(s, sample_n_bytes);
240 if (Snack_GetDebugFlag(s) > 3) {
241 Snack_WriteLogInt(" Setting sampsize", Snack_GetBytesPerSample(s));
242 }
243
244 /* channel_count */
245 if (sp_h_get_field((SP_FILE *)ch, "channel_count", T_INTEGER,
246 (void *)&channel_count) > 0)
247 fail("SPHERE: unable to read channel_count");
248 Snack_SetNumChannels(s, channel_count);
249 if (Snack_GetDebugFlag(s) > 3) {
250 Snack_WriteLogInt(" Setting channels", Snack_GetNumChannels(s));
251 }
252
253 /* sample_count */
254 if (sp_h_get_field((SP_FILE *)ch, "sample_count", T_INTEGER,
255 (void *)&sample_cnt) > 0) {
256 sample_cnt = 0;
257 }
258 if (Snack_GetDebugFlag(s) > 3) {
259 Snack_WriteLogInt(" Setting length", sample_cnt);
260 }
261
262 /* sample_coding */
263 if (sp_h_get_field((SP_FILE *)ch, "sample_coding", T_STRING,
264 (void *)&sample_coding) > 0) {
265 sample_coding = "";
266 }
267 if (strncmp(sample_coding, "pculaw", 6) == 0) {
268 sp_set_data_mode((SP_FILE *)ch, "SE-PCM-2");
269 Snack_SetSampleEncoding(s, LIN16);
270 Snack_SetBytesPerSample(s, 2);
271 } else if (strncmp(sample_coding, "alaw", 4) == 0) {
272 Snack_SetSampleEncoding(s, ALAW);
273 } else if (strncmp(sample_coding, "ulaw", 4) == 0) {
274 Snack_SetSampleEncoding(s, MULAW);
275 } else if (strncmp(sample_coding, "pcm", 3) == 0 || sample_coding == "") {
276 if (Snack_GetBytesPerSample(s) == 2) {
277 Snack_SetSampleEncoding(s, LIN16);
278 } else {
279 Snack_SetSampleEncoding(s, LIN8);
280 }
281 }
282 if (sample_coding != "") {
283 free(sample_coding);
284 }
285
286 /* header size shouldn't be needed by user,
287 so it is not given directly by SPHERE user interface */
288
289 Snack_SetHeaderSize(s, SPHERE_HEADERSIZE);
290 Snack_SetLength(s, sample_cnt);
291
292 return TCL_OK;
293 }
294
295 void
FreeSphereHeader(Sound * s)296 FreeSphereHeader(Sound *s)
297 {
298 if (s->extHead != NULL) {
299 ckfree((char *) s->extHead);
300 s->extHead = NULL;
301 s->extHeadType = 0;
302 }
303 }
304
305 #define SPHEREFILE_VERSION "1.2"
306
307 Snack_FileFormat snackSphFormat = {
308 SPHERE_STRING,
309 GuessSphereFile,
310 GetSphereHeader,
311 ExtSphereFile,
312 NULL,
313 OpenSphereFile,
314 CloseSphereFile,
315 ReadSphereSamples,
316 NULL,
317 SeekSphereFile,
318 FreeSphereHeader,
319 NULL,
320 (Snack_FileFormat *) NULL
321 };
322
323 /* Called by "load libsnacksphere" */
EXPORT(int,Snacksphere_Init)324 EXPORT(int, Snacksphere_Init) _ANSI_ARGS_((Tcl_Interp *interp))
325 {
326 int res;
327
328 #ifdef USE_TCL_STUBS
329 if (Tcl_InitStubs(interp, "8", 0) == NULL) {
330 return TCL_ERROR;
331 }
332 #endif
333
334 #ifdef USE_SNACK_STUBS
335 if (Snack_InitStubs(interp, "2", 0) == NULL) {
336 return TCL_ERROR;
337 }
338 #endif
339
340 res = Tcl_PkgProvide(interp, "snacksphere", SPHEREFILE_VERSION);
341
342 if (res != TCL_OK) return res;
343
344 Tcl_SetVar(interp, "snack::snacksphere", SPHEREFILE_VERSION,TCL_GLOBAL_ONLY);
345
346 Snack_CreateFileFormat(&snackSphFormat);
347
348 return TCL_OK;
349 }
350
EXPORT(int,Snacksphere_SafeInit)351 EXPORT(int, Snacksphere_SafeInit)(Tcl_Interp *interp)
352 {
353 return Snacksphere_Init(interp);
354 }
355