123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983 |
- /*
- * Copyright (C) 1997-2004 Kare Sjolander <kare@speech.kth.se>
- * Copyright (C) 2010 Jacob Meuser <jakemsr@openbsd.org>
- *
- * This file is part of the Snack Sound Toolkit.
- * The latest version can be found at http://www.speech.kth.se/snack/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include "tcl.h"
- #include "jkAudIO.h"
- #include "jkSound.h"
- #include <stdio.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <glob.h>
- #include <poll.h>
- #include <errno.h>
- #include <sndio.h>
- /* for mixer functions */
- #include <soundcard.h>
- #define MIXER_NAME "/dev/mixer"
- extern void Snack_WriteLog(char *s);
- extern void Snack_WriteLogInt(char *s, int n);
- #ifndef min
- #define min(a,b) ((a)<(b)?(a):(b))
- #define max(a,b) ((a)>(b)?(a):(b))
- #endif
- static int mfd = 0;
- static struct MixerLink mixerLinks[SOUND_MIXER_NRDEVICES][2];
- static int littleEndian = 0;
- void
- onmove_cb(void *addr, int delta)
- {
- ADesc *A = addr;
- A->hardpos += delta * A->bytesPerSample * A->nChannels;
- if (A->debug > 9) Snack_WriteLogInt(" Leave onmove_cb\n", delta);
- }
- int
- SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq,
- int nchannels, int encoding)
- {
- struct sio_par par;
- char cmode[8];
- unsigned smode;
- A->debug = 0;
- if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioOpen\n");
- switch (mode) {
- case RECORD:
- snprintf(cmode, sizeof(cmode), "record");
- smode = SIO_REC;
- break;
- case PLAY:
- snprintf(cmode, sizeof(cmode), "play");
- smode = SIO_PLAY;
- break;
- default:
- Tcl_AppendResult(interp, "Invalid mode", NULL);
- return TCL_ERROR;
- break;
- }
- // We always want to use the default device
- A->hdl = sio_open(NULL, smode, 0);
- if (A->hdl == NULL) {
- Tcl_AppendResult(interp, "Could not open sndio device for ", cmode, NULL);
- return TCL_ERROR;
- }
- A->mode = mode;
- sio_initpar(&par);
- A->convert = 0;
- switch (encoding) {
- case LIN16:
- par.le = littleEndian ? 1 : 0;
- par.sig = 1;
- par.bits = 16;
- par.bps = 2;
- break;
- case LIN24:
- par.le = littleEndian ? 1 : 0;
- par.sig = 1;
- par.bits = 24;
- par.bps = 4;
- break;
- case ALAW:
- par.le = littleEndian ? 1 : 0;
- par.sig = 1;
- par.bits = 16;
- par.bps = 2;
- A->convert = ALAW;
- break;
- case MULAW:
- par.le = littleEndian ? 1 : 0;
- par.sig = 1;
- par.bits = 16;
- par.bps = 2;
- A->convert = MULAW;
- break;
- case LIN8OFFSET:
- par.sig = 0;
- par.bits = 8;
- par.bps = 1;
- break;
- case LIN8:
- par.sig = 1;
- par.bits = 8;
- par.bps = 1;
- break;
- }
- switch (smode) {
- case SIO_REC:
- par.rchan = nchannels;
- break;
- case SIO_PLAY:
- par.pchan = nchannels;
- break;
- }
- par.rate = freq;
- if (!sio_setpar(A->hdl, &par)) {
- Tcl_AppendResult(interp, "Failed setting parameters.", NULL);
- return TCL_ERROR;
- }
- if (!sio_getpar(A->hdl, &A->par)) {
- Tcl_AppendResult(interp, "Failed getting parameters.", NULL);
- return TCL_ERROR;
- }
- if (par.bits != A->par.bits ||
- par.sig != A->par.sig ||
- par.le != A->par.le ||
- par.bps != A->par.bps) {
- Tcl_AppendResult(interp, "Format not supported.", NULL);
- return TCL_ERROR;
- }
- if ((smode == SIO_REC && par.rchan != A->par.rchan) ||
- (smode == SIO_PLAY && par.pchan != A->par.pchan)) {
- Tcl_AppendResult(interp, "Number of channels not supported.", NULL);
- return TCL_ERROR;
- }
- if (par.rate != A->par.rate) {
- Tcl_AppendResult(interp, "Sample frequency not supported.", NULL);
- return TCL_ERROR;
- }
- A->hardpos = A->softpos = 0;
- sio_onmove(A->hdl, onmove_cb, A);
- if (!sio_start(A->hdl)) {
- Tcl_AppendResult(interp, "Could not start sndio.", NULL);
- return TCL_ERROR;
- }
- A->frag_size = A->par.round * A->par.bps *
- (mode == PLAY ? A->par.pchan : A->par.rchan);
- A->nChannels = smode == SIO_REC ? A->par.rchan : A->par.pchan;
- A->bytesPerSample = A->par.bps;
- A->warm = 0;
- if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioOpen", A->frag_size);
- return TCL_OK;
- }
- int
- SnackAudioClose(ADesc *A)
- {
- if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioClose\n");
- sio_close(A->hdl);
- if (A->debug > 1) Snack_WriteLog(" Exit SnackAudioClose\n");
- return(0);
- }
- long
- SnackAudioPause(ADesc *A)
- {
- long res = SnackAudioPlayed(A);
- if (A->debug > 9) Snack_WriteLog(" Enter SnackAudioPause\n");
- /* nothing to do */
- return(res);
- }
- void
- SnackAudioResume(ADesc *A)
- {
- if (A->debug > 9) Snack_WriteLog(" Enter SnackAudioResume\n");
- /* nothing to do */
- }
- void
- SnackAudioFlush(ADesc *A)
- {
- if (A->debug > 9) Snack_WriteLog(" Enter SnackAudioFlush\n");
- /* nothing to do */
- }
- static char zeroBlock[16];
- void
- SnackAudioPost(ADesc *A)
- {
- int n;
- if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioPost\n");
- if (A->warm == 1) {
- int i;
- for (i = 0; i < A->frag_size / (A->bytesPerSample * A->nChannels); i++) {
- n = sio_write(A->hdl, zeroBlock, A->bytesPerSample * A->nChannels);
- A->softpos += n;
- }
- A->warm = 2;
- }
- if (A->debug > 1) Snack_WriteLog(" Exit SnackAudioPost\n");
- }
- int
- SnackAudioRead(ADesc *A, void *buf, int nFrames)
- {
- int n = 2;
- if (A->debug > 1) Snack_WriteLogInt(" Enter SnackAudioRead", nFrames);
-
- while (nFrames > n * 2) n *= 2;
- nFrames = n;
- if (A->convert) {
- int n = 0, i, res;
- short s[2];
- for (i = 0; i < nFrames * A->nChannels; i += A->nChannels) {
- res = sio_read(A->hdl, &s, A->nChannels * sizeof(short));
- A->softpos += res;
- if (res <= 0) return(n / (A->bytesPerSample * A->nChannels));
- if (A->convert == ALAW) {
- ((unsigned char *)buf)[i] = Snack_Lin2Alaw(s[0]);
- if (A->nChannels == 2) {
- ((unsigned char *)buf)[i+1] = Snack_Lin2Alaw(s[1]);
- }
- } else {
- ((unsigned char *)buf)[i] = Snack_Lin2Mulaw(s[0]);
- if (A->nChannels == 2) {
- ((unsigned char *)buf)[i+1] = Snack_Lin2Mulaw(s[1]);
- }
- }
- n += res;
- }
- return(n / (A->bytesPerSample * A->nChannels));
- } else {
- int n = sio_read(A->hdl, (unsigned char *)buf,
- nFrames * A->bytesPerSample * A->nChannels);
- A->softpos += n;
- if (n > 0) n /= (A->bytesPerSample * A->nChannels);
- if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioRead", n);
- return(n);
- }
- }
- int
- SnackAudioWrite(ADesc *A, void *buf, int nFrames)
- {
- if (A->debug > 1) Snack_WriteLogInt(" Enter SnackAudioWrite\n", nFrames);
- if (A->warm == 0) A->warm = 1;
- if (A->convert) {
- int n = 0, i, res;
- short s;
- for (i = 0; i < nFrames * A->nChannels; i++) {
- if (A->convert == ALAW) {
- s = Snack_Alaw2Lin(((unsigned char *)buf)[i]);
- } else {
- s = Snack_Mulaw2Lin(((unsigned char *)buf)[i]);
- }
- res = sio_write(A->hdl, &s, sizeof(short));
- A->softpos += res;
- if (res <= 0) return(n / (A->bytesPerSample * A->nChannels));
- n += res;
- }
- return(n / (A->bytesPerSample * A->nChannels));
- } else {
- int n = sio_write(A->hdl, buf, nFrames * A->bytesPerSample * A->nChannels);
- A->softpos += n;
- if (A->debug > 9) Snack_WriteLogInt(" SnackAudioWrite wrote \n", n);
- if (n > 0) n /= (A->bytesPerSample * A->nChannels);
- return(n);
- }
- }
- void
- SnackSndioUpdatePos(ADesc *A)
- {
- struct pollfd pfd;
- int n, revents;
- n = sio_pollfd(A->hdl, &pfd, A->mode == PLAY ? POLLOUT : POLLIN);
- while (poll(&pfd, n, 0) < 0 && errno == EINTR)
- ; /* nothing */
- revents = sio_revents(A->hdl, &pfd);
- }
- int
- SnackAudioReadable(ADesc *A)
- {
- int all, used, avail;
- SnackSndioUpdatePos(A);
- all = A->par.bufsz * A->bytesPerSample * A->nChannels;
- used = A->hardpos < 0 ? 0 : A->hardpos - A->softpos;
- avail = used < all ? used : all;
- /* XXX this is what the OSS backend does, not sure why */
- if (avail > 60*44100*4) avail = 0;
- if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioReadable", avail);
- return (avail / (A->bytesPerSample * A->nChannels));
- }
- int
- SnackAudioWriteable(ADesc *A)
- {
- int all, used, avail;
- SnackSndioUpdatePos(A);
- all = A->par.bufsz * A->bytesPerSample * A->nChannels;
- used = A->hardpos < 0 ? A->softpos: A->softpos - A->hardpos;
- avail = all - used;
- if (A->debug > 9) Snack_WriteLogInt(" Leave SnackAudioWriteable\n", avail);
- return (avail / (A->bytesPerSample * A->nChannels));
- }
- long
- SnackAudioPlayed(ADesc *A)
- {
- long res;
- res = A->softpos / (A->nChannels * A->bytesPerSample);
-
- return(res);
- }
- void
- SnackAudioInit()
- {
- union {
- char c[sizeof(short)];
- short s;
- } order;
- /* Compute the byte order of this machine. */
- order.s = 1;
- if (order.c[0] == 1) {
- littleEndian = 1;
- }
- if ((mfd = open(MIXER_NAME, O_RDWR, 0)) == -1) {
- fprintf(stderr, "Unable to open mixer %s\n", MIXER_NAME);
- }
- }
- void
- SnackAudioFree()
- {
- int i, j;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- for (j = 0; j < 2; j++) {
- if (mixerLinks[i][j].mixer != NULL) {
- ckfree(mixerLinks[i][j].mixer);
- }
- if (mixerLinks[i][j].mixerVar != NULL) {
- ckfree(mixerLinks[i][j].mixerVar);
- }
- }
- if (mixerLinks[i][0].jack != NULL) {
- ckfree(mixerLinks[i][0].jack);
- }
- if (mixerLinks[i][0].jackVar != NULL) {
- ckfree((char *)mixerLinks[i][0].jackVar);
- }
- }
- close(mfd);
- }
- void
- ASetRecGain(int gain)
- {
- int g = min(max(gain, 0), 100);
- int recsrc = 0;
- g = g * 256 + g;
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc);
- if (recsrc & SOUND_MASK_LINE) {
- ioctl(mfd, SOUND_MIXER_WRITE_LINE, &g);
- } else {
- ioctl(mfd, SOUND_MIXER_WRITE_MIC, &g);
- }
- }
- void
- ASetPlayGain(int gain)
- {
- int g = min(max(gain, 0), 100);
- int pcm_gain = 25700;
- g = g * 256 + g;
- ioctl(mfd, SOUND_MIXER_WRITE_VOLUME, &g);
- ioctl(mfd, SOUND_MIXER_WRITE_PCM, &pcm_gain);
- }
- int
- AGetRecGain()
- {
- int g = 0, left, right, recsrc = 0;
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc);
- if (recsrc & SOUND_MASK_LINE) {
- ioctl(mfd, SOUND_MIXER_READ_LINE, &g);
- } else {
- ioctl(mfd, SOUND_MIXER_READ_MIC, &g);
- }
- left = g & 0xff;
- right = (g & 0xff00) / 256;
- g = (left + right) / 2;
- return(g);
- }
- int
- AGetPlayGain()
- {
- int g = 0, left, right;
-
- ioctl(mfd, SOUND_MIXER_READ_VOLUME, &g);
- left = g & 0xff;
- right = (g & 0xff00) / 256;
- g = (left + right) / 2;
- return(g);
- }
- int
- SnackAudioGetEncodings(char *device)
- {
- struct sio_hdl *hdl;
- struct sio_cap cap;
- // we always want to use the default device
- hdl = sio_open(NULL, SIO_PLAY, 0);
- if (hdl == NULL) {
- }
- return(LIN16);
- }
- void
- SnackAudioGetRates(char *device, char *buf, int n)
- {
- int freq, pos=0, i;
- int f[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };
- for (i = 0; i < 8; i++) {
- freq = f[i];
- pos += sprintf(&buf[pos], "%d ", freq);
- }
- }
- int
- SnackAudioMaxNumberChannels(char *device)
- {
- return(2);
- }
- int
- SnackAudioMinNumberChannels(char *device)
- {
- return(1);
- }
- void
- SnackMixerGetInputJackLabels(char *buf, int n)
- {
- char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, recMask, pos = 0;
- if (mfd != -1) {
- ioctl(mfd, SOUND_MIXER_READ_RECMASK, &recMask);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if ((1 << i) & recMask) {
- pos += sprintf(&buf[pos], "%s", jackLabels[i]);
- pos += sprintf(&buf[pos], " ");
- }
- }
- } else {
- buf[0] = '\0';
- }
- buf[n-1] = '\0';
- }
- void
- SnackMixerGetOutputJackLabels(char *buf, int n)
- {
- buf[0] = '\0';
- }
- void
- SnackMixerGetInputJack(char *buf, int n)
- {
- char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, recSrc = 0, pos = 0;
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if ((1 << i) & recSrc) {
- pos += sprintf(&buf[pos], "%s", jackLabels[i]);
- while (isspace(buf[pos-1])) pos--;
- pos += sprintf(&buf[pos], " ");
- }
- }
- if(isspace(buf[pos-1])) pos--;
- buf[pos] = '\0';
- /*printf("SnackMixerGetInputJack %x, %s\n", recSrc, buf);*/
- }
- int
- SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status)
- {
- char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, recSrc = 0, currSrc;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) {
- recSrc = 1 << i;
- break;
- }
- }
-
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &currSrc);
- /* printf("SnackMixerSetInputJack1 %x %s %s\n", currSrc, jack, status);*/
- if (strcmp(status, "1") == 0) {
- recSrc |= currSrc;
- } else {
- recSrc = (currSrc & ~recSrc);
- }
- /* printf("SnackMixerSetInputJack2 %x\n", recSrc);*/
-
- if (ioctl(mfd, SOUND_MIXER_WRITE_RECSRC, &recSrc) == -1) {
- return 1;
- } else {
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
- /* printf("SnackMixerSetInputJack3 %x\n", recSrc);*/
- return 0;
- }
- return 1;
- }
- void
- SnackMixerGetOutputJack(char *buf, int n)
- {
- buf[0] = '\0';
- }
- void
- SnackMixerSetOutputJack(char *jack, char *status)
- {
- }
- static int dontTrace = 0;
- static char *
- JackVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1,
- CONST84 char *name2, int flags)
- {
- MixerLink *mixLink = (MixerLink *) clientData;
- char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, recSrc = 0, status = 0;
- CONST84 char *stringValue;
- Tcl_Obj *obj, *var;
- if (dontTrace) return (char *) NULL;
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
- /*printf("JackVarProc %x %s %s\n", recSrc, name1, name2);*/
- if (flags & TCL_TRACE_UNSETS) {
- if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(mixLink->jack, jackLabels[i], strlen(mixLink->jack))
- == 0) {
- if ((1 << i) & recSrc) {
- status = 1;
- } else {
- status = 0;
- }
- break;
- }
- }
- obj = Tcl_NewIntObj(status);
- var = Tcl_NewStringObj(mixLink->jackVar, -1);
- Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
- Tcl_TraceVar(interp, mixLink->jackVar,
- TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- JackVarProc, mixLink);
- }
- return (char *) NULL;
- }
- stringValue = Tcl_GetVar(interp, mixLink->jackVar, TCL_GLOBAL_ONLY);
- if (stringValue != NULL) {
- SnackMixerSetInputJack(interp, mixLink->jack, stringValue);
- }
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
- /*printf("JackVarProc2 %x\n", recSrc);*/
- dontTrace = 1;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (mixerLinks[i][0].jackVar != NULL) {
- if ((1 << i) & recSrc) {
- status = 1;
- } else {
- status = 0;
- }
- obj = Tcl_NewIntObj(status);
- var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1);
- Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY |TCL_PARSE_PART1);
- }
- }
- dontTrace = 0;
- return (char *) NULL;
- }
- void
- SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var)
- {
- char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, recSrc = 0, status;
- CONST84 char *value;
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) {
- if ((1 << i) & recSrc) {
- status = 1;
- } else {
- status = 0;
- }
- mixerLinks[i][0].jack = SnackStrDup(jack);
- mixerLinks[i][0].jackVar = SnackStrDup(Tcl_GetStringFromObj(var, NULL));
- value = Tcl_GetVar(interp, mixerLinks[i][0].jackVar, TCL_GLOBAL_ONLY);
- if (value != NULL) {
- SnackMixerSetInputJack(interp, mixerLinks[i][0].jack, value);
- } else {
- Tcl_Obj *obj = Tcl_NewIntObj(status);
- Tcl_ObjSetVar2(interp, var, NULL, obj,
- TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
- }
- Tcl_TraceVar(interp, mixerLinks[i][0].jackVar,
- TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- JackVarProc, (ClientData) &mixerLinks[i][0]);
- break;
- }
- }
- }
- void
- SnackMixerGetChannelLabels(char *line, char *buf, int n)
- {
- char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, devMask;
- ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
- if (devMask & (1 << i)) {
- sprintf(buf, "Left Right");
- } else {
- sprintf(buf, "Mono");
- }
- break;
- }
- }
- }
- void
- SnackMixerGetVolume(char *line, int channel, char *buf, int n)
- {
- char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, vol = 0, devMask, isStereo = 0, left, right;
- buf[0] = '\0';
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
- ioctl(mfd, MIXER_READ(i), &vol);
- ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask);
- if (devMask & (1 << i)) {
- isStereo = 1;
- }
- break;
- }
- }
- left = vol & 0xff;
- right = (vol & 0xff00) >> 8;
- if (isStereo) {
- if (channel == 0) {
- sprintf(buf, "%d", left);
- } else if (channel == 1) {
- sprintf(buf, "%d", right);
- } else if (channel == -1) {
- sprintf(buf, "%d", (left + right)/2);
- }
- } else {
- sprintf(buf, "%d", left);
- }
- }
- void
- SnackMixerSetVolume(char *line, int channel, int volume)
- {
- char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int tmp = min(max(volume, 0), 100), i, oldVol = 0;
- int vol = (tmp << 8) + tmp;
- if (channel == 0) {
- vol = tmp;
- }
- if (channel == 1) {
- vol = tmp << 8;
- }
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
- ioctl(mfd, MIXER_READ(i), &oldVol);
- if (channel == 0) {
- vol = (oldVol & 0xff00) | (vol & 0x00ff);
- }
- if (channel == 1) {
- vol = (vol & 0xff00) | (oldVol & 0x00ff);
- }
- ioctl(mfd, MIXER_WRITE(i), &vol);
- break;
- }
- }
- }
- static char *
- VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1,
- CONST84 char *name2, int flags)
- {
- MixerLink *mixLink = (MixerLink *) clientData;
- CONST84 char *stringValue;
-
- if (flags & TCL_TRACE_UNSETS) {
- if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
- Tcl_Obj *obj, *var;
- char tmp[VOLBUFSIZE];
- SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE);
- obj = Tcl_NewIntObj(atoi(tmp));
- var = Tcl_NewStringObj(mixLink->mixerVar, -1);
- Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
- Tcl_TraceVar(interp, mixLink->mixerVar,
- TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- VolumeVarProc, mixLink);
- }
- return (char *) NULL;
- }
- stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY);
- if (stringValue != NULL) {
- SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue));
- }
- return (char *) NULL;
- }
- void
- SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n,
- Tcl_Obj *CONST objv[])
- {
- char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, j, channel;
- CONST84 char *value;
- char tmp[VOLBUFSIZE];
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
- for (j = 0; j < n; j++) {
- if (n == 1) {
- channel = -1;
- } else {
- channel = j;
- }
- mixerLinks[i][j].mixer = SnackStrDup(line);
- mixerLinks[i][j].mixerVar = SnackStrDup(Tcl_GetStringFromObj(objv[j+3],NULL));
- mixerLinks[i][j].channel = j;
- value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY);
- if (value != NULL) {
- SnackMixerSetVolume(line, channel, atoi(value));
- } else {
- Tcl_Obj *obj;
- SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE);
- obj = Tcl_NewIntObj(atoi(tmp));
- Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj,
- TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
- }
- Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar,
- TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
- VolumeVarProc, (ClientData) &mixerLinks[i][j]);
- }
- }
- }
- }
- void
- SnackMixerUpdateVars(Tcl_Interp *interp)
- {
- int i, j, recSrc, status;
- char tmp[VOLBUFSIZE];
- Tcl_Obj *obj, *var;
- ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- for (j = 0; j < 2; j++) {
- if (mixerLinks[i][j].mixerVar != NULL) {
- SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel,
- tmp, VOLBUFSIZE);
- obj = Tcl_NewIntObj(atoi(tmp));
- var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1);
- Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1);
- }
- }
- if (mixerLinks[i][0].jackVar != NULL) {
- if ((1 << i) & recSrc) {
- status = 1;
- } else {
- status = 0;
- }
- obj = Tcl_NewIntObj(status);
- var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1);
- Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
- }
- }
- }
- void
- SnackMixerGetLineLabels(char *buf, int n)
- {
- char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
- int i, devMask, pos = 0;
- if (mfd != -1) {
- ioctl(mfd, SOUND_MIXER_READ_DEVMASK, &devMask);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if ((1 << i) & devMask && pos < n-8) {
- pos += sprintf(&buf[pos], "%s", mixLabels[i]);
- pos += sprintf(&buf[pos], " ");
- }
- }
- } else {
- buf[0] = '\0';
- }
- buf[n-1] = '\0';
- }
- int
- SnackGetOutputDevices(char **arr, int n)
- {
- return SnackGetInputDevices(arr, n);
- }
- int
- SnackGetInputDevices(char **arr, int n)
- {
- int i, j = 0;
- glob_t globt;
-
- glob("/dev/audio*", 0, NULL, &globt);
- //glob("/dev/audio*", GLOB_APPEND, NULL, &globt);
- for (i = 0; i < globt.gl_pathc; i++) {
- if (j < n) {
- arr[j++] = (char *) SnackStrDup("default");
- }
- }
- globfree(&globt);
- return(1);
- }
- int
- SnackGetMixerDevices(char **arr, int n)
- {
- int i, j = 0;
- glob_t globt;
-
- glob("/dev/mixer*", 0, NULL, &globt);
-
- for (i = 0; i < globt.gl_pathc; i++) {
- if (j < n) {
- arr[j++] = (char *) SnackStrDup(globt.gl_pathv[i]);
- }
- }
- globfree(&globt);
- return(j);
- }
|