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