1/*
2 * Copyright (C) 1997-2004 Kare Sjolander <kare@speech.kth.se>
3 *
4 * This file is part of the Snack Sound Toolkit.
5 * The latest version can be found at http://www.speech.kth.se/snack/
6 *
7 * This program 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 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program 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, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <string.h>
23#include "tk.h"
24#include "snack.h"
25#include "jkCanvItems.h"
26
27#if defined(__WIN32__)
28#  define WIN32_LEAN_AND_MEAN
29#  include <windows.h>
30#  undef WIN32_LEAN_AND_MEAN
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36EXTERN int Snack_Init(Tcl_Interp *interp);
37EXTERN int Snack_SafeInit(Tcl_Interp *interp);
38
39#ifdef __cplusplus
40}
41#endif
42
43HINSTANCE snackHInst = NULL;
44
45BOOL APIENTRY
46DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved)
47{
48  snackHInst = hInst;
49  return TRUE;
50}
51#endif
52
53#ifdef MAC
54#include <string.h>
55int main (void)
56{
57	return 0;
58}
59
60double
61hypotd(double x, double y)
62{
63    double sum;
64
65    sum = x*x + y*y;
66    return sqrt(sum);
67}
68#endif
69
70extern Tk_ItemType snackWaveType;
71extern Tk_ItemType snackSpectrogramType;
72extern Tk_ItemType snackSectionType;
73extern Tk_CustomOption waveTagsOption;
74extern Tk_CustomOption spegTagsOption;
75extern Tk_CustomOption sectTagsOption;
76
77#define play_width 19
78#define play_height 19
79static unsigned char play_bits[] = {
80   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
81   0x78, 0x00, 0x00, 0xf8, 0x01, 0x00, 0xf8, 0x07, 0x00, 0xf8, 0x1f, 0x00,
82   0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x1f, 0x00, 0xf8, 0x07, 0x00,
83   0xf8, 0x01, 0x00, 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
84   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
85
86#define rec_width 19
87#define rec_height 19
88static unsigned char rec_bits[] = {
89   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00,
90   0xe0, 0x1f, 0x00, 0xf0, 0x3f, 0x00, 0xf0, 0x3f, 0x00, 0xf8, 0x7f, 0x00,
91   0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf0, 0x3f, 0x00,
92   0xf0, 0x3f, 0x00, 0xe0, 0x1f, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00,
93   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
94
95#define stop_width 19
96#define stop_height 19
97static unsigned char stop_bits[] = {
98   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0x00,
99   0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00,
100   0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00,
101   0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00,
102   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
103
104#define pause_width 19
105#define pause_height 19
106static unsigned char pause_bits[] = {
107   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7c, 0x00,
108   0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00,
109   0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00,
110   0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00, 0xf8, 0x7c, 0x00, 0x00, 0x00, 0x00,
111   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
112
113#define playnext_width 20
114#define playnext_height 19
115static unsigned char playnext_bits[] = {
116  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x00,
117  0x78, 0xe0, 0x00, 0xf8, 0xe1, 0x00, 0xf8, 0xe7, 0x00, 0xf8, 0xff, 0x00,
118  0xf8, 0xff, 0x00, 0xf8, 0xff, 0x00, 0xf8, 0xff, 0x00, 0xf8, 0xe7, 0x00,
119  0xf8, 0xe1, 0x00, 0x78, 0xe0, 0x00, 0x18, 0xe0, 0x00, 0x00, 0x00, 0x00,
120  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
121
122#define playprev_width 20
123#define playprev_height 19
124static unsigned char playprev_bits[] = {
125  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xc0, 0x00,
126  0x38, 0xf0, 0x00, 0x38, 0xfc, 0x00, 0x38, 0xff, 0x00, 0xf8, 0xff, 0x00,
127  0xf8, 0xff, 0x00, 0xf8, 0xff, 0x00, 0xf8, 0xff, 0x00, 0x38, 0xff, 0x00,
128  0x38, 0xfc, 0x00, 0x38, 0xf0, 0x00, 0x38, 0xc0, 0x00, 0x00, 0x00, 0x00,
129  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
130
131Tcl_Channel snackDebugChannel = NULL;
132static Tcl_Interp *debugInterp = NULL;
133int debugLevel = 0;
134char *snackDumpFile = NULL;
135
136int
137Snack_DebugCmd(ClientData cdata, Tcl_Interp *interp, int objc,
138	       Tcl_Obj *CONST objv[])
139{
140  int len;
141  char *str;
142  CONST84 char *patchLevelStr;
143
144  if (objc > 1) {
145    if (Tcl_GetIntFromObj(interp, objv[1], &debugLevel) != TCL_OK)
146      return TCL_ERROR;
147  }
148  if (objc >= 3) {
149    if (Tcl_IsSafe(interp)) {
150      Tcl_AppendResult(interp, "can not open log file in a safe interpreter",
151		       (char *) NULL);
152      return TCL_ERROR;
153    }
154    str = Tcl_GetStringFromObj(objv[2], &len);
155    if (len > 0) {
156      snackDebugChannel = Tcl_OpenFileChannel(interp, str, "w", 420);
157      if (snackDebugChannel == 0) {
158	return TCL_ERROR;
159      }
160    }
161  }
162  if (objc == 4) {
163    if (Tcl_IsSafe(interp)) {
164      Tcl_AppendResult(interp, "can not open dump file in a safe interpreter",
165		       (char *) NULL);
166      return TCL_ERROR;
167    }
168    str = Tcl_GetStringFromObj(objv[3], &len);
169    snackDumpFile = (char *) ckalloc(len + 1);
170    strcpy(snackDumpFile, str);
171  }
172  if (debugLevel > 0) {
173    patchLevelStr = Tcl_GetVar(interp, "snack::patchLevel", TCL_GLOBAL_ONLY);
174    Tcl_Write(snackDebugChannel, "Snack patch level: ", 19);
175    Tcl_Write(snackDebugChannel, patchLevelStr, strlen(patchLevelStr));
176    Tcl_Write(snackDebugChannel, "\n", 1);
177    Tcl_Flush(snackDebugChannel);
178  }
179
180  return TCL_OK;
181}
182
183#ifdef SNACK_CSLU_TOOLKIT
184extern int fromCSLUshWaveCmd(Sound *s, Tcl_Interp *interp, int objc,
185			     Tcl_Obj *CONST objv[]);
186extern int toCSLUshWaveCmd(Sound *s, Tcl_Interp *interp, int objc,
187			   Tcl_Obj *CONST objv[]);
188#endif
189
190int useOldObjAPI = 0;
191
192int
193Snack_setUseOldObjAPI(ClientData cdata, Tcl_Interp *interp, int objc,
194		      Tcl_Obj *CONST objv[])
195{
196  useOldObjAPI = 1;
197
198  return TCL_OK;
199}
200
201
202static int initialized = 0;
203int littleEndian = 0;
204
205#ifdef __cplusplus
206extern "C" SnackStubs *snackStubs;
207#else
208extern SnackStubs *snackStubs;
209#endif
210
211extern Tcl_HashTable *filterHashTable;
212extern Tcl_HashTable *hsetHashTable;
213extern Tcl_HashTable *arHashTable;
214
215#if defined(Tcl_InitHashTable) && defined(USE_TCL_STUBS)
216#undef Tcl_InitHashTable
217#define Tcl_InitHashTable (tclStubsPtr->tcl_InitHashTable)
218#endif
219
220extern char defaultOutDevice[];
221int defaultSampleRate = 16000;
222
223int
224Snack_Init(Tcl_Interp *interp)
225{
226  Tcl_CmdInfo infoPtr;
227  CONST84 char *version;
228  Tcl_HashTable *soundHashTable;
229  union {
230    char c[sizeof(short)];
231    short s;
232  } order;
233  char rates[100];
234
235#ifdef USE_TCL_STUBS
236  if (Tcl_InitStubs(interp, "8", 0) == NULL) {
237    return TCL_ERROR;
238  }
239#endif
240
241  version = Tcl_GetVar(interp, "tcl_version",
242		       (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
243
244  if (strcmp(version, "8.0") == 0) {
245    useOldObjAPI = 1;
246  }
247
248#ifdef TCL_81_API
249  if (Tcl_PkgProvideEx(interp, "snack", SNACK_VERSION,
250		       (ClientData) &snackStubs) != TCL_OK) {
251    return TCL_ERROR;
252  }
253#else
254  if (Tcl_PkgProvide(interp, "snack", SNACK_VERSION) != TCL_OK) {
255    return TCL_ERROR;
256  }
257#endif
258
259  if (Tcl_GetCommandInfo(interp, "button", &infoPtr) != 0) {
260#ifdef USE_TK_STUBS
261    if (Tk_InitStubs(interp, "8", 0) == NULL) {
262      return TCL_ERROR;
263    }
264#endif
265
266    if (initialized == 0) {
267      Tk_CreateItemType(&snackWaveType);
268      Tk_CreateItemType(&snackSpectrogramType);
269      Tk_CreateItemType(&snackSectionType);
270    }
271#if !defined(MAC) && !defined(MAC_OSX_TCL)
272    Tk_DefineBitmap(interp, Tk_GetUid("play"),   (char *) play_bits,
273		    play_width, play_height);
274    Tk_DefineBitmap(interp, Tk_GetUid("record"), (char *) rec_bits,
275		    rec_width, rec_height);
276    Tk_DefineBitmap(interp, Tk_GetUid("stop"),   (char *) stop_bits,
277		    stop_width, stop_height);
278    Tk_DefineBitmap(interp, Tk_GetUid("pause"),  (char *) pause_bits,
279		    pause_width, pause_height);
280#endif
281    Tk_DefineBitmap(interp, Tk_GetUid("snackPlay"), (char *) play_bits,
282		    play_width, play_height);
283    Tk_DefineBitmap(interp, Tk_GetUid("snackRecord"), (char *) rec_bits,
284		    rec_width, rec_height);
285    Tk_DefineBitmap(interp, Tk_GetUid("snackStop"), (char *) stop_bits,
286		    stop_width, stop_height);
287    Tk_DefineBitmap(interp, Tk_GetUid("snackPause"), (char *) pause_bits,
288		    pause_width, pause_height);
289    Tk_DefineBitmap(interp, Tk_GetUid("snackPlayNext"), (char *) playnext_bits,
290		    playnext_width, playnext_height);
291    Tk_DefineBitmap(interp, Tk_GetUid("snackPlayPrev"), (char *) playprev_bits,
292		    playprev_width, playprev_height);
293    waveTagsOption.parseProc = Tk_CanvasTagsParseProc;
294    waveTagsOption.printProc = Tk_CanvasTagsPrintProc;
295    spegTagsOption.parseProc = Tk_CanvasTagsParseProc;
296    spegTagsOption.printProc = Tk_CanvasTagsPrintProc;
297    sectTagsOption.parseProc = Tk_CanvasTagsParseProc;
298    sectTagsOption.printProc = Tk_CanvasTagsPrintProc;
299  }
300
301  soundHashTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
302  filterHashTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
303  hsetHashTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
304  arHashTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
305
306  Tcl_CreateObjCommand(interp, "sound", Snack_SoundCmd,
307		       (ClientData) soundHashTable, (Tcl_CmdDeleteProc *)NULL);
308
309  Tcl_CreateObjCommand(interp, "snack::sound", Snack_SoundCmd,
310		       (ClientData) soundHashTable, Snack_SoundDeleteCmd);
311
312  Tcl_CreateObjCommand(interp, "audio", Snack_AudioCmd,
313		       NULL, (Tcl_CmdDeleteProc *)NULL);
314
315  Tcl_CreateObjCommand(interp, "snack::audio", Snack_AudioCmd,
316		       NULL, Snack_AudioDeleteCmd);
317
318  Tcl_CreateObjCommand(interp, "snack::mixer", Snack_MixerCmd,
319		       NULL, Snack_MixerDeleteCmd);
320
321  Tcl_CreateObjCommand(interp, "snack::filter", Snack_FilterCmd,
322		       (ClientData) filterHashTable, Snack_FilterDeleteCmd);
323
324  Tcl_CreateObjCommand(interp, "snack::hset", Snack_HSetCmd,
325		       (ClientData) hsetHashTable, Snack_HSetDeleteCmd);
326
327  Tcl_CreateObjCommand(interp, "snack::ca", Snack_arCmd,
328		       (ClientData) arHashTable, Snack_arDeleteCmd);
329
330  Tcl_CreateObjCommand(interp, "snack::isyn", isynCmd,
331		       NULL, (Tcl_CmdDeleteProc *)NULL);
332
333  Tcl_CreateObjCommand(interp, "snack::osyn", osynCmd,
334		       NULL, (Tcl_CmdDeleteProc *)NULL);
335
336  Tcl_CreateObjCommand(interp, "snack::debug",
337		       (Tcl_ObjCmdProc*) Snack_DebugCmd,
338		       NULL, (Tcl_CmdDeleteProc *)NULL);
339
340  Tcl_CreateObjCommand(interp, "snack::setUseOldObjAPI",
341		       (Tcl_ObjCmdProc*) Snack_setUseOldObjAPI,
342		       NULL, (Tcl_CmdDeleteProc *)NULL);
343
344  snackDebugChannel = Tcl_GetStdChannel(TCL_STDERR);
345  debugInterp = interp;
346
347  Tcl_SetVar(interp, "snack::patchLevel", SNACK_PATCH_LEVEL, TCL_GLOBAL_ONLY);
348  Tcl_SetVar(interp, "snack::version",    SNACK_VERSION,     TCL_GLOBAL_ONLY);
349
350  Tcl_InitHashTable(soundHashTable, TCL_STRING_KEYS);
351  Tcl_InitHashTable(filterHashTable, TCL_STRING_KEYS);
352  Tcl_InitHashTable(hsetHashTable, TCL_STRING_KEYS);
353  Tcl_InitHashTable(arHashTable, TCL_STRING_KEYS);
354
355  if (initialized == 0) {
356    SnackDefineFileFormats(interp);
357    SnackCreateFilterTypes(interp);
358
359    SnackAudioInit();
360
361    Tcl_CreateExitHandler(Snack_ExitProc, (ClientData) NULL);
362
363    initialized = 1;
364  }
365#ifdef SNACK_CSLU_TOOLKIT
366  Snack_AddSubCmd(SNACK_SOUND_CMD, "fromCSLUshWave",
367		  (Snack_CmdProc *) fromCSLUshWaveCmd, NULL);
368  Snack_AddSubCmd(SNACK_SOUND_CMD, "toCSLUshWave",
369		  (Snack_CmdProc *) toCSLUshWaveCmd, NULL);
370#endif
371
372  /* Compute the byte order of this machine. */
373
374  order.s = 1;
375  if (order.c[0] == 1) {
376    littleEndian = 1;
377  }
378
379  /* Determine a default sample rate for this machine, usually 16kHz. */
380
381  SnackAudioGetRates(defaultOutDevice, rates, 100);
382  if (strstr(rates, "16000") != NULL ||
383      sscanf(rates, "%d", &defaultSampleRate) != 1) {
384    defaultSampleRate = 16000;
385  }
386
387  return TCL_OK;
388}
389
390int
391Snack_SafeInit(Tcl_Interp *interp)
392{
393  return Snack_Init(interp);
394}
395
396void
397Snack_WriteLog(char *str)
398{
399  if (snackDebugChannel == NULL) {
400    snackDebugChannel = Tcl_OpenFileChannel(debugInterp, "_debug.txt", "w",
401					    420);
402  }
403  Tcl_Write(snackDebugChannel, str, strlen(str));
404  Tcl_Flush(snackDebugChannel);
405}
406
407void
408Snack_WriteLogInt(char *str, int num)
409{
410  char buf[20];
411
412  if (snackDebugChannel == NULL) {
413    snackDebugChannel = Tcl_OpenFileChannel(debugInterp, "_debug.txt", "w",
414					    420);
415  }
416  Tcl_Write(snackDebugChannel, str, strlen(str));
417  sprintf(buf, " %d", num);
418  Tcl_Write(snackDebugChannel, buf, strlen(buf));
419  Tcl_Write(snackDebugChannel, "\n", 1);
420  Tcl_Flush(snackDebugChannel);
421}
422
423/*
424static double snack_t0;
425
426void
427TimerStart() {
428  snack_t0 = SnackCurrentTime();
429}
430
431void
432TimerStop() {
433  char buf[20];
434  sprintf(buf, "%f", SnackCurrentTime()-snack_t0);
435  Tcl_Write(snackDebugChannel, buf, strlen(buf));
436  Tcl_Write(snackDebugChannel, "\n", 1);
437  Tcl_Flush(snackDebugChannel);
438}
439*/
440