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 "tcl.h"
23#include "jkAudIO.h"
24#include "jkSound.h"
25#include <stdio.h>
26#include <fcntl.h>
27#include <unistd.h>
28#include <sys/ioctl.h>
29#include <alsa/asoundlib.h>
30#include <string.h>
31#include <ctype.h>
32#include <stdlib.h>
33#define DEVICE_NAME "default"
34
35static char *defaultDeviceName = DEVICE_NAME;
36extern void Snack_WriteLog(char *s);
37extern void Snack_WriteLogInt(char *s, int n);
38
39#ifndef min
40#define min(a,b) ((a)<(b)?(a):(b))
41#define max(a,b) ((a)>(b)?(a):(b))
42#endif
43
44static int mfd = 0;
45/*
46static struct MixerLink mixerLinks[SOUND_MIXER_NRDEVICES][2];
47*/
48static int littleEndian = 0;
49
50static int minNumChan = 1;
51
52int
53SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq,
54	       int nchannels, int encoding)
55{
56  int format;
57  int nformat;
58  int channels;
59  int speed;
60  int mask;
61
62  snd_pcm_hw_params_t *hw_params;
63
64  if (A->debug > 1) Snack_WriteLog("  Enter SnackAudioOpen\n");
65
66  if (device == NULL) {
67    device = defaultDeviceName;
68  }
69  if (strlen(device) == 0) {
70    device = defaultDeviceName;
71  }
72
73  A->mode = mode;
74  switch (mode) {
75  case RECORD:
76    if (snd_pcm_open(&A->handle, device, SND_PCM_STREAM_CAPTURE, 0) < 0) {
77      Tcl_AppendResult(interp, "Could not open ", device, " for read.",
78		       NULL);
79      return TCL_ERROR;
80    }
81    break;
82  case PLAY:
83    if (snd_pcm_open(&A->handle, device, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
84      Tcl_AppendResult(interp, "Could not open ", device, " for write.",
85		       NULL);
86      return TCL_ERROR;
87    }
88    break;
89  }
90
91  switch (encoding) {
92  case LIN16:
93    if (littleEndian) {
94      format = SND_PCM_FORMAT_S16_LE;
95    } else {
96      format = SND_PCM_FORMAT_S16_BE;
97    }
98    A->bytesPerSample = sizeof(short);
99    break;
100  case LIN24:
101    if (littleEndian) {
102      format = SND_PCM_FORMAT_S32_LE;
103    } else {
104      format = SND_PCM_FORMAT_S32_BE;
105    }
106    A->bytesPerSample = sizeof(int);
107    break;
108  case ALAW:
109    format = SND_PCM_FORMAT_A_LAW;
110    A->bytesPerSample = sizeof(char);
111    break;
112  case MULAW:
113      format = SND_PCM_FORMAT_MU_LAW;
114      A->bytesPerSample = sizeof(char);
115    break;
116  case LIN8OFFSET:
117    format = SND_PCM_FORMAT_U8;
118    A->bytesPerSample = sizeof(char);
119    break;
120  case LIN8:
121    format = SND_PCM_FORMAT_S8;
122    A->bytesPerSample = sizeof(char);
123    break;
124  }
125
126  snd_pcm_hw_params_malloc(&hw_params);
127  snd_pcm_hw_params_any(A->handle, hw_params);
128  snd_pcm_hw_params_set_access(A->handle, hw_params,
129			       SND_PCM_ACCESS_RW_INTERLEAVED);
130  snd_pcm_hw_params_set_format(A->handle, hw_params, format);
131  snd_pcm_hw_params_set_rate_near(A->handle, hw_params, &freq, 0);
132  snd_pcm_hw_params_set_channels(A->handle, hw_params, nchannels);
133
134  if (snd_pcm_hw_params(A->handle, hw_params) < 0) {
135    Tcl_AppendResult(interp, "Failed setting HW params.", NULL);
136    return TCL_ERROR;
137  }
138  snd_pcm_hw_params_free(hw_params);
139  snd_pcm_prepare(A->handle);
140  if (A->mode == RECORD) {
141    snd_pcm_start(A->handle);
142  }
143
144  A->freq = freq;
145  A->nWritten = 0;
146  A->nPlayed = 0;
147
148  if (A->debug > 1) Snack_WriteLogInt("  Exit SnackAudioOpen", A->debug);
149
150  return TCL_OK;
151}
152
153int
154SnackAudioClose(ADesc *A)
155{
156  if (A->debug > 1) Snack_WriteLog("  Enter SnackAudioClose\n");
157
158  snd_pcm_drop(A->handle);
159  snd_pcm_close(A->handle);
160
161  if (A->debug > 1) Snack_WriteLog("  Exit SnackAudioClose\n");
162
163  return(0);
164}
165
166long
167SnackAudioPause(ADesc *A)
168{
169  if (A->mode == RECORD) {
170    snd_pcm_drop(A->handle);
171    return(-1);
172  } else {
173    long res = SnackAudioPlayed(A);
174    A->nPlayed = res;
175    snd_pcm_drop(A->handle);
176    return(res);
177  }
178}
179
180void
181SnackAudioResume(ADesc *A)
182{
183  if (A->mode == RECORD) {
184  } else {
185    snd_pcm_prepare(A->handle);
186  }
187}
188
189void
190SnackAudioFlush(ADesc *A)
191{
192  if (A->mode == RECORD) {
193  } else {
194    snd_pcm_drop(A->handle);
195    snd_pcm_prepare(A->handle);
196  }
197}
198
199void
200SnackAudioPost(ADesc *A)
201{
202  int i;
203  static char buf[64];
204
205  if (A->debug > 1) Snack_WriteLog("  Enter SnackAudioPost\n");
206
207  for (i = 0; i < 1000; i++) {
208    snd_pcm_writei(A->handle, &buf, 1);
209  }
210
211  if (A->debug > 1) Snack_WriteLog("  Enter SnackAudioPost\n");
212}
213
214int
215SnackAudioRead(ADesc *A, void *buf, int nFrames)
216{
217  int n;
218
219  if (A->debug > 1) Snack_WriteLogInt("  Enter SnackAudioRead", nFrames);
220
221  n = snd_pcm_readi(A->handle, buf, nFrames);
222
223  if (A->debug > 1) Snack_WriteLogInt("  Exit SnackAudioRead", n);
224
225  return(n);
226}
227
228int
229SnackAudioWrite(ADesc *A, void *buf, int nFrames)
230{
231  int n;
232
233  n = snd_pcm_writei(A->handle, buf, nFrames);
234  A->nWritten += n;
235
236  return(n);
237}
238
239int
240SnackAudioReadable(ADesc *A)
241{
242  int avail;
243
244  if (A->debug > 1) Snack_WriteLog("  Enter SnackAudioReadable\n");
245
246  avail = snd_pcm_avail_update(A->handle);
247
248  if (A->debug > 1) Snack_WriteLogInt("  Exit SnackAudioReadable",avail);
249
250  if (avail < 0)
251    avail = 0;
252
253  return (avail);
254}
255
256int
257SnackAudioWriteable(ADesc *A)
258{
259  int avail = snd_pcm_avail_update(A->handle);
260
261  if (avail < 0)
262    avail = 0;
263
264  return (avail);
265}
266
267long
268SnackAudioPlayed(ADesc *A)
269{
270  long avail = _snd_pcm_mmap_hw_ptr(A->handle);
271
272  if (avail < 0)
273    avail = 0;
274
275  return (avail+A->nPlayed);
276}
277
278void
279SnackAudioInit()
280{
281  union {
282    char c[sizeof(short)];
283    short s;
284  } order;
285  int afd, format, channels, nchannels;
286  /*
287  int i, n;
288  char *arr[MAX_NUM_DEVICES];
289  */
290
291  /* Compute the byte order of this machine. */
292
293  order.s = 1;
294  if (order.c[0] == 1) {
295    littleEndian = 1;
296  }
297}
298
299void
300SnackAudioFree()
301{
302  int i, j;
303  /*
304  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
305    for (j = 0; j < 2; j++) {
306      if (mixerLinks[i][j].mixer != NULL) {
307	ckfree(mixerLinks[i][j].mixer);
308      }
309      if (mixerLinks[i][j].mixerVar != NULL) {
310	ckfree(mixerLinks[i][j].mixerVar);
311      }
312    }
313    if (mixerLinks[i][0].jack != NULL) {
314      ckfree(mixerLinks[i][0].jack);
315    }
316    if (mixerLinks[i][0].jackVar != NULL) {
317      ckfree(mixerLinks[i][0].jackVar);
318    }
319  }
320  */
321}
322
323void
324ASetRecGain(int gain)
325{
326  int g = min(max(gain, 0), 100);
327  int recsrc = 0;
328  /*
329  g = g * 256 + g;
330  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc);
331  if (recsrc & SOUND_MASK_LINE) {
332    ioctl(mfd, SOUND_MIXER_WRITE_LINE, &g);
333  } else {
334    ioctl(mfd, SOUND_MIXER_WRITE_MIC, &g);
335  }
336  */
337}
338
339void
340ASetPlayGain(int gain)
341{
342  int g = min(max(gain, 0), 100);
343  int pcm_gain = 25700;
344  /*
345  g = g * 256 + g;
346  ioctl(mfd, SOUND_MIXER_WRITE_VOLUME, &g);
347  ioctl(mfd, SOUND_MIXER_WRITE_PCM, &pcm_gain);
348  */
349}
350
351int
352AGetRecGain()
353{
354  int g = 0, left, right, recsrc = 0;
355/*
356  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc);
357  if (recsrc & SOUND_MASK_LINE) {
358    ioctl(mfd, SOUND_MIXER_READ_LINE, &g);
359  } else {
360    ioctl(mfd, SOUND_MIXER_READ_MIC, &g);
361  }
362  left  =  g & 0xff;
363  right = (g & 0xff00) / 256;
364  g = (left + right) / 2;
365*/
366  return(g);
367}
368
369int
370AGetPlayGain()
371{
372  int g = 0, left, right;
373  /*
374  ioctl(mfd, SOUND_MIXER_READ_VOLUME, &g);
375  left  =  g & 0xff;
376  right = (g & 0xff00) / 256;
377  g = (left + right) / 2;
378  */
379  return(g);
380}
381
382int
383SnackAudioGetEncodings(char *device)
384{
385  int afd, mask;
386  /*
387  if ((afd = open(DEVICE_NAME, O_WRONLY, 0)) == -1) {
388    return(0);
389  }
390  if (ioctl(afd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
391    return(0);
392  }
393  close(afd);
394
395  if (mask & AFMT_S16_LE || mask & AFMT_S16_BE) {*/
396    return(LIN16);
397    /*  } else {
398    return(0);
399    }*/
400}
401
402void
403SnackAudioGetRates(char *device, char *buf, int n)
404{
405  strncpy(buf, "8000 11025 16000 22050 32000 44100 48000", n);
406  buf[n-1] = '\0';
407}
408
409int
410SnackAudioMaxNumberChannels(char *device)
411{
412  return(2);
413}
414
415int
416SnackAudioMinNumberChannels(char *device)
417{
418  return(minNumChan);
419}
420
421void
422SnackMixerGetInputJackLabels(char *buf, int n)
423{/*
424  char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
425  int i, recMask, pos = 0;
426
427  if (mfd != -1) {
428    ioctl(mfd, SOUND_MIXER_READ_RECMASK, &recMask);
429    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
430      if ((1 << i) & recMask) {
431	pos += sprintf(&buf[pos], "%s", jackLabels[i]);
432	pos += sprintf(&buf[pos], " ");
433      }
434    }
435  } else {
436    buf[0] = '\0';
437  }
438  buf[n-1] = '\0';*/
439}
440
441void
442SnackMixerGetOutputJackLabels(char *buf, int n)
443{
444  buf[0] = '\0';
445}
446
447void
448SnackMixerGetInputJack(char *buf, int n)
449{/*
450  char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
451  int i, recSrc = 0, pos = 0;
452
453  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
454  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
455    if ((1 << i) & recSrc) {
456      pos += sprintf(&buf[pos], "%s", jackLabels[i]);
457      while (isspace(buf[pos-1])) pos--;
458      pos += sprintf(&buf[pos], " ");
459    }
460  }
461  if(isspace(buf[pos-1])) pos--;
462  buf[pos] = '\0';*/
463  /*printf("SnackMixerGetInputJack %x, %s\n", recSrc, buf);*/
464}
465
466int
467SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status)
468{/*
469  char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
470  int i, recSrc = 0, currSrc;
471
472  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
473    if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) {
474      recSrc = 1 << i;
475      break;
476    }
477  }
478
479  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &currSrc);
480 */
481/*  printf("SnackMixerSetInputJack1 %x %s %s\n", currSrc, jack, status);*/
482  /*
483  if (strcmp(status, "1") == 0) {
484    recSrc |= currSrc;
485  } else {
486    recSrc = (currSrc & ~recSrc);
487    }*/
488/*  printf("SnackMixerSetInputJack2 %x\n", recSrc);*/
489  /*
490  if (ioctl(mfd, SOUND_MIXER_WRITE_RECSRC, &recSrc) == -1) {
491    return 1;
492  } else {
493  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);*/
494/*    printf("SnackMixerSetInputJack3 %x\n", recSrc);*/
495  /*   return 0;
496  }*/
497  return 1;
498}
499
500void
501SnackMixerGetOutputJack(char *buf, int n)
502{
503  buf[0] = '\0';
504}
505
506void
507SnackMixerSetOutputJack(char *jack, char *status)
508{
509}
510
511static int dontTrace = 0;
512
513static char *
514JackVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1,
515	    CONST84 char *name2, int flags)
516{/*
517  MixerLink *mixLink = (MixerLink *) clientData;
518  char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
519  int i, recSrc = 0, status = 0;
520  CONST84 char *stringValue;
521  Tcl_Obj *obj, *var;
522
523  if (dontTrace) return (char *) NULL;
524
525  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);*/
526/*printf("JackVarProc %x %s %s\n", recSrc, name1, name2);*/
527  /*  if (flags & TCL_TRACE_UNSETS) {
528    if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
529      for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
530	if (strncasecmp(mixLink->jack, jackLabels[i], strlen(mixLink->jack))
531	    == 0) {
532	  if ((1 << i) & recSrc) {
533	    status = 1;
534	  } else {
535	    status = 0;
536	  }
537	  break;
538	}
539      }
540      obj = Tcl_NewIntObj(status);
541      var = Tcl_NewStringObj(mixLink->jackVar, -1);
542      Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
543      Tcl_TraceVar(interp, mixLink->jackVar,
544		   TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
545		   JackVarProc, mixLink);
546    }
547    return (char *) NULL;
548  }
549
550  stringValue = Tcl_GetVar(interp, mixLink->jackVar, TCL_GLOBAL_ONLY);
551  if (stringValue != NULL) {
552    SnackMixerSetInputJack(interp, mixLink->jack, stringValue);
553  }
554
555  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);*/
556  /*printf("JackVarProc2 %x\n", recSrc);*//*
557  dontTrace = 1;
558  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
559    if (mixerLinks[i][0].jackVar != NULL) {
560      if ((1 << i) & recSrc) {
561	status = 1;
562      } else {
563	status = 0;
564      }
565      obj = Tcl_NewIntObj(status);
566      var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1);
567      Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY |TCL_PARSE_PART1);
568    }
569  }
570  dontTrace = 0;
571					  */
572  return (char *) NULL;
573}
574
575void
576SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var)
577{/*
578  char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
579  int i, recSrc = 0, status;
580  CONST84 char *value;
581
582  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
583
584  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
585    if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) {
586      if ((1 << i) & recSrc) {
587	status = 1;
588      } else {
589	status = 0;
590      }
591      mixerLinks[i][0].jack = SnackStrDup(jack);
592      mixerLinks[i][0].jackVar = SnackStrDup(Tcl_GetStringFromObj(var, NULL));
593      value = Tcl_GetVar(interp, mixerLinks[i][0].jackVar, TCL_GLOBAL_ONLY);
594      if (value != NULL) {
595	SnackMixerSetInputJack(interp, mixerLinks[i][0].jack, value);
596      } else {
597	Tcl_Obj *obj = Tcl_NewIntObj(status);
598	Tcl_ObjSetVar2(interp, var, NULL, obj,
599		       TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
600
601      }
602      Tcl_TraceVar(interp, mixerLinks[i][0].jackVar,
603		   TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
604		   JackVarProc, (ClientData) &mixerLinks[i][0]);
605      break;
606    }
607    }*/
608}
609
610void
611SnackMixerGetChannelLabels(char *line, char *buf, int n)
612{/*
613  char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
614  int i, devMask;
615
616  ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask);
617  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
618    if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
619      if (devMask & (1 << i)) {
620	sprintf(buf, "Left Right");
621      } else {
622	sprintf(buf, "Mono");
623      }
624      break;
625    }
626    }*/
627}
628
629void
630SnackMixerGetVolume(char *line, int channel, char *buf, int n)
631{/*
632  char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
633  int i, vol = 0, devMask, isStereo = 0, left, right;
634
635  buf[0] = '\0';
636
637  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
638    if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
639      ioctl(mfd, MIXER_READ(i), &vol);
640      ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask);
641      if (devMask & (1 << i)) {
642	isStereo = 1;
643      }
644      break;
645    }
646  }
647  left  =  vol & 0xff;
648  right = (vol & 0xff00) >> 8;
649  if (isStereo) {
650    if (channel == 0) {
651      sprintf(buf, "%d", left);
652    } else if (channel == 1) {
653      sprintf(buf, "%d", right);
654    } else if (channel == -1) {
655      sprintf(buf, "%d", (left + right)/2);
656    }
657  } else {
658    sprintf(buf, "%d", left);
659    }*/
660}
661
662void
663SnackMixerSetVolume(char *line, int channel, int volume)
664{/*
665  char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
666  int tmp = min(max(volume, 0), 100), i, oldVol = 0;
667  int vol = (tmp << 8) + tmp;
668
669  if (channel == 0) {
670    vol = tmp;
671  }
672  if (channel == 1) {
673    vol = tmp << 8;
674  }
675
676  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
677    if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
678      ioctl(mfd, MIXER_READ(i), &oldVol);
679      if (channel == 0) {
680	vol = (oldVol & 0xff00) | (vol & 0x00ff);
681      }
682      if (channel == 1) {
683	vol = (vol & 0xff00) | (oldVol & 0x00ff);
684      }
685      ioctl(mfd, MIXER_WRITE(i), &vol);
686      break;
687    }
688    }*/
689}
690
691static char *
692VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1,
693	      CONST84 char *name2, int flags)
694{/*
695  MixerLink *mixLink = (MixerLink *) clientData;
696  CONST84 char *stringValue;
697
698  if (flags & TCL_TRACE_UNSETS) {
699    if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
700      Tcl_Obj *obj, *var;
701      char tmp[VOLBUFSIZE];
702
703      SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE);
704      obj = Tcl_NewIntObj(atoi(tmp));
705      var = Tcl_NewStringObj(mixLink->mixerVar, -1);
706      Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
707      Tcl_TraceVar(interp, mixLink->mixerVar,
708		   TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
709		   VolumeVarProc, mixLink);
710    }
711    return (char *) NULL;
712  }
713
714  stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY);
715  if (stringValue != NULL) {
716    SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue));
717  }
718 */
719  return (char *) NULL;
720}
721
722void
723SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n,
724		     Tcl_Obj *CONST objv[])
725{/*
726  char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
727  int i, j, channel;
728  CONST84 char *value;
729  char tmp[VOLBUFSIZE];
730
731  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
732    if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
733      for (j = 0; j < n; j++) {
734	if (n == 1) {
735	  channel = -1;
736	} else {
737	  channel = j;
738	}
739	mixerLinks[i][j].mixer = SnackStrDup(line);
740	mixerLinks[i][j].mixerVar = SnackStrDup(Tcl_GetStringFromObj(objv[j+3],NULL));
741	mixerLinks[i][j].channel = j;
742	value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY);
743	if (value != NULL) {
744	  SnackMixerSetVolume(line, channel, atoi(value));
745	} else {
746	  Tcl_Obj *obj;
747	  SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE);
748	  obj = Tcl_NewIntObj(atoi(tmp));
749	  Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj,
750			 TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
751	}
752	Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar,
753		     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
754		     VolumeVarProc, (ClientData) &mixerLinks[i][j]);
755      }
756    }
757    }*/
758}
759
760void
761SnackMixerUpdateVars(Tcl_Interp *interp)
762{/*
763  int i, j, recSrc, status;
764  char tmp[VOLBUFSIZE];
765  Tcl_Obj *obj, *var;
766
767  ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
768  for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
769    for (j = 0; j < 2; j++) {
770      if (mixerLinks[i][j].mixerVar != NULL) {
771	SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel,
772			    tmp, VOLBUFSIZE);
773	obj = Tcl_NewIntObj(atoi(tmp));
774	var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1);
775	Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1);
776      }
777    }
778    if (mixerLinks[i][0].jackVar != NULL) {
779      if ((1 << i) & recSrc) {
780	status = 1;
781      } else {
782	status = 0;
783      }
784      obj = Tcl_NewIntObj(status);
785      var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1);
786      Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
787    }
788    }*/
789}
790
791void
792SnackMixerGetLineLabels(char *buf, int n)
793{/*
794  char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
795  int i, devMask, pos = 0;
796
797  if (mfd != -1) {
798    ioctl(mfd, SOUND_MIXER_READ_DEVMASK, &devMask);
799    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
800      if ((1 << i) & devMask && pos < n-8) {
801	pos += sprintf(&buf[pos], "%s", mixLabels[i]);
802	pos += sprintf(&buf[pos], " ");
803      }
804    }
805  } else {
806    buf[0] = '\0';
807    }*/
808  buf[n-1] = '\0';
809}
810
811int
812SnackGetOutputDevices(char **arr, int n)
813{
814  return SnackGetInputDevices(arr, n);
815}
816
817int
818SnackGetInputDevices(char **arr, int n)
819{
820  int i = -1, j = 0;
821  char devicename[20];
822
823  arr[j++] = (char *) SnackStrDup("default");
824  while (snd_card_next(&i) == 0 && i > -1) {
825    if (j < n) {
826      snprintf(devicename, 20, "plughw:%d", i);
827      arr[j++] = (char *) SnackStrDup(devicename);
828    } else {
829      break;
830    }
831  }
832  return(j);
833}
834
835int
836SnackGetMixerDevices(char **arr, int n)
837{
838  int i = -1, j = 0;
839  char devicename[20];
840
841  while (snd_card_next(&i) == 0 && i > -1) {
842    snprintf(devicename, 20, "hw:%d", i);
843    if (j < n) {
844      arr[j++] = (char *) SnackStrDup(devicename);
845    } else {
846      break;
847    }
848  }
849  return(j);
850}
851