jkAudIO_sndio.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. /*
  2. * Copyright (C) 1997-2004 Kare Sjolander <kare@speech.kth.se>
  3. * Copyright (C) 2010 Jacob Meuser <jakemsr@openbsd.org>
  4. *
  5. * This file is part of the Snack Sound Toolkit.
  6. * The latest version can be found at http://www.speech.kth.se/snack/
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  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 <string.h>
  29. #include <ctype.h>
  30. #include <stdlib.h>
  31. #include <glob.h>
  32. #include <poll.h>
  33. #include <errno.h>
  34. #include <sndio.h>
  35. /* for mixer functions */
  36. #include <soundcard.h>
  37. #define MIXER_NAME "/dev/mixer"
  38. extern void Snack_WriteLog(char *s);
  39. extern void Snack_WriteLogInt(char *s, int n);
  40. #ifndef min
  41. #define min(a,b) ((a)<(b)?(a):(b))
  42. #define max(a,b) ((a)>(b)?(a):(b))
  43. #endif
  44. static int mfd = 0;
  45. static struct MixerLink mixerLinks[SOUND_MIXER_NRDEVICES][2];
  46. static int littleEndian = 0;
  47. void
  48. onmove_cb(void *addr, int delta)
  49. {
  50. ADesc *A = addr;
  51. A->hardpos += delta * A->bytesPerSample * A->nChannels;
  52. if (A->debug > 9) Snack_WriteLogInt(" Leave onmove_cb\n", delta);
  53. }
  54. int
  55. SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq,
  56. int nchannels, int encoding)
  57. {
  58. struct sio_par par;
  59. char cmode[8];
  60. unsigned smode;
  61. A->debug = 0;
  62. if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioOpen\n");
  63. switch (mode) {
  64. case RECORD:
  65. snprintf(cmode, sizeof(cmode), "record");
  66. smode = SIO_REC;
  67. break;
  68. case PLAY:
  69. snprintf(cmode, sizeof(cmode), "play");
  70. smode = SIO_PLAY;
  71. break;
  72. default:
  73. Tcl_AppendResult(interp, "Invalid mode", NULL);
  74. return TCL_ERROR;
  75. break;
  76. }
  77. // We always want to use the default device
  78. A->hdl = sio_open(NULL, smode, 0);
  79. if (A->hdl == NULL) {
  80. Tcl_AppendResult(interp, "Could not open sndio device for ", cmode, NULL);
  81. return TCL_ERROR;
  82. }
  83. A->mode = mode;
  84. sio_initpar(&par);
  85. A->convert = 0;
  86. switch (encoding) {
  87. case LIN16:
  88. par.le = littleEndian ? 1 : 0;
  89. par.sig = 1;
  90. par.bits = 16;
  91. par.bps = 2;
  92. break;
  93. case LIN24:
  94. par.le = littleEndian ? 1 : 0;
  95. par.sig = 1;
  96. par.bits = 24;
  97. par.bps = 4;
  98. break;
  99. case ALAW:
  100. par.le = littleEndian ? 1 : 0;
  101. par.sig = 1;
  102. par.bits = 16;
  103. par.bps = 2;
  104. A->convert = ALAW;
  105. break;
  106. case MULAW:
  107. par.le = littleEndian ? 1 : 0;
  108. par.sig = 1;
  109. par.bits = 16;
  110. par.bps = 2;
  111. A->convert = MULAW;
  112. break;
  113. case LIN8OFFSET:
  114. par.sig = 0;
  115. par.bits = 8;
  116. par.bps = 1;
  117. break;
  118. case LIN8:
  119. par.sig = 1;
  120. par.bits = 8;
  121. par.bps = 1;
  122. break;
  123. }
  124. switch (smode) {
  125. case SIO_REC:
  126. par.rchan = nchannels;
  127. break;
  128. case SIO_PLAY:
  129. par.pchan = nchannels;
  130. break;
  131. }
  132. par.rate = freq;
  133. if (!sio_setpar(A->hdl, &par)) {
  134. Tcl_AppendResult(interp, "Failed setting parameters.", NULL);
  135. return TCL_ERROR;
  136. }
  137. if (!sio_getpar(A->hdl, &A->par)) {
  138. Tcl_AppendResult(interp, "Failed getting parameters.", NULL);
  139. return TCL_ERROR;
  140. }
  141. if (par.bits != A->par.bits ||
  142. par.sig != A->par.sig ||
  143. par.le != A->par.le ||
  144. par.bps != A->par.bps) {
  145. Tcl_AppendResult(interp, "Format not supported.", NULL);
  146. return TCL_ERROR;
  147. }
  148. if ((smode == SIO_REC && par.rchan != A->par.rchan) ||
  149. (smode == SIO_PLAY && par.pchan != A->par.pchan)) {
  150. Tcl_AppendResult(interp, "Number of channels not supported.", NULL);
  151. return TCL_ERROR;
  152. }
  153. if (par.rate != A->par.rate) {
  154. Tcl_AppendResult(interp, "Sample frequency not supported.", NULL);
  155. return TCL_ERROR;
  156. }
  157. A->hardpos = A->softpos = 0;
  158. sio_onmove(A->hdl, onmove_cb, A);
  159. if (!sio_start(A->hdl)) {
  160. Tcl_AppendResult(interp, "Could not start sndio.", NULL);
  161. return TCL_ERROR;
  162. }
  163. A->frag_size = A->par.round * A->par.bps *
  164. (mode == PLAY ? A->par.pchan : A->par.rchan);
  165. A->nChannels = smode == SIO_REC ? A->par.rchan : A->par.pchan;
  166. A->bytesPerSample = A->par.bps;
  167. A->warm = 0;
  168. if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioOpen", A->frag_size);
  169. return TCL_OK;
  170. }
  171. int
  172. SnackAudioClose(ADesc *A)
  173. {
  174. if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioClose\n");
  175. sio_close(A->hdl);
  176. if (A->debug > 1) Snack_WriteLog(" Exit SnackAudioClose\n");
  177. return(0);
  178. }
  179. long
  180. SnackAudioPause(ADesc *A)
  181. {
  182. long res = SnackAudioPlayed(A);
  183. if (A->debug > 9) Snack_WriteLog(" Enter SnackAudioPause\n");
  184. /* nothing to do */
  185. return(res);
  186. }
  187. void
  188. SnackAudioResume(ADesc *A)
  189. {
  190. if (A->debug > 9) Snack_WriteLog(" Enter SnackAudioResume\n");
  191. /* nothing to do */
  192. }
  193. void
  194. SnackAudioFlush(ADesc *A)
  195. {
  196. if (A->debug > 9) Snack_WriteLog(" Enter SnackAudioFlush\n");
  197. /* nothing to do */
  198. }
  199. static char zeroBlock[16];
  200. void
  201. SnackAudioPost(ADesc *A)
  202. {
  203. int n;
  204. if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioPost\n");
  205. if (A->warm == 1) {
  206. int i;
  207. for (i = 0; i < A->frag_size / (A->bytesPerSample * A->nChannels); i++) {
  208. n = sio_write(A->hdl, zeroBlock, A->bytesPerSample * A->nChannels);
  209. A->softpos += n;
  210. }
  211. A->warm = 2;
  212. }
  213. if (A->debug > 1) Snack_WriteLog(" Exit SnackAudioPost\n");
  214. }
  215. int
  216. SnackAudioRead(ADesc *A, void *buf, int nFrames)
  217. {
  218. int n = 2;
  219. if (A->debug > 1) Snack_WriteLogInt(" Enter SnackAudioRead", nFrames);
  220. while (nFrames > n * 2) n *= 2;
  221. nFrames = n;
  222. if (A->convert) {
  223. int n = 0, i, res;
  224. short s[2];
  225. for (i = 0; i < nFrames * A->nChannels; i += A->nChannels) {
  226. res = sio_read(A->hdl, &s, A->nChannels * sizeof(short));
  227. A->softpos += res;
  228. if (res <= 0) return(n / (A->bytesPerSample * A->nChannels));
  229. if (A->convert == ALAW) {
  230. ((unsigned char *)buf)[i] = Snack_Lin2Alaw(s[0]);
  231. if (A->nChannels == 2) {
  232. ((unsigned char *)buf)[i+1] = Snack_Lin2Alaw(s[1]);
  233. }
  234. } else {
  235. ((unsigned char *)buf)[i] = Snack_Lin2Mulaw(s[0]);
  236. if (A->nChannels == 2) {
  237. ((unsigned char *)buf)[i+1] = Snack_Lin2Mulaw(s[1]);
  238. }
  239. }
  240. n += res;
  241. }
  242. return(n / (A->bytesPerSample * A->nChannels));
  243. } else {
  244. int n = sio_read(A->hdl, (unsigned char *)buf,
  245. nFrames * A->bytesPerSample * A->nChannels);
  246. A->softpos += n;
  247. if (n > 0) n /= (A->bytesPerSample * A->nChannels);
  248. if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioRead", n);
  249. return(n);
  250. }
  251. }
  252. int
  253. SnackAudioWrite(ADesc *A, void *buf, int nFrames)
  254. {
  255. if (A->debug > 1) Snack_WriteLogInt(" Enter SnackAudioWrite\n", nFrames);
  256. if (A->warm == 0) A->warm = 1;
  257. if (A->convert) {
  258. int n = 0, i, res;
  259. short s;
  260. for (i = 0; i < nFrames * A->nChannels; i++) {
  261. if (A->convert == ALAW) {
  262. s = Snack_Alaw2Lin(((unsigned char *)buf)[i]);
  263. } else {
  264. s = Snack_Mulaw2Lin(((unsigned char *)buf)[i]);
  265. }
  266. res = sio_write(A->hdl, &s, sizeof(short));
  267. A->softpos += res;
  268. if (res <= 0) return(n / (A->bytesPerSample * A->nChannels));
  269. n += res;
  270. }
  271. return(n / (A->bytesPerSample * A->nChannels));
  272. } else {
  273. int n = sio_write(A->hdl, buf, nFrames * A->bytesPerSample * A->nChannels);
  274. A->softpos += n;
  275. if (A->debug > 9) Snack_WriteLogInt(" SnackAudioWrite wrote \n", n);
  276. if (n > 0) n /= (A->bytesPerSample * A->nChannels);
  277. return(n);
  278. }
  279. }
  280. void
  281. SnackSndioUpdatePos(ADesc *A)
  282. {
  283. struct pollfd pfd;
  284. int n, revents;
  285. n = sio_pollfd(A->hdl, &pfd, A->mode == PLAY ? POLLOUT : POLLIN);
  286. while (poll(&pfd, n, 0) < 0 && errno == EINTR)
  287. ; /* nothing */
  288. revents = sio_revents(A->hdl, &pfd);
  289. }
  290. int
  291. SnackAudioReadable(ADesc *A)
  292. {
  293. int all, used, avail;
  294. SnackSndioUpdatePos(A);
  295. all = A->par.bufsz * A->bytesPerSample * A->nChannels;
  296. used = A->hardpos < 0 ? 0 : A->hardpos - A->softpos;
  297. avail = used < all ? used : all;
  298. /* XXX this is what the OSS backend does, not sure why */
  299. if (avail > 60*44100*4) avail = 0;
  300. if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioReadable", avail);
  301. return (avail / (A->bytesPerSample * A->nChannels));
  302. }
  303. int
  304. SnackAudioWriteable(ADesc *A)
  305. {
  306. int all, used, avail;
  307. SnackSndioUpdatePos(A);
  308. all = A->par.bufsz * A->bytesPerSample * A->nChannels;
  309. used = A->hardpos < 0 ? A->softpos: A->softpos - A->hardpos;
  310. avail = all - used;
  311. if (A->debug > 9) Snack_WriteLogInt(" Leave SnackAudioWriteable\n", avail);
  312. return (avail / (A->bytesPerSample * A->nChannels));
  313. }
  314. long
  315. SnackAudioPlayed(ADesc *A)
  316. {
  317. long res;
  318. res = A->softpos / (A->nChannels * A->bytesPerSample);
  319. return(res);
  320. }
  321. void
  322. SnackAudioInit()
  323. {
  324. union {
  325. char c[sizeof(short)];
  326. short s;
  327. } order;
  328. /* Compute the byte order of this machine. */
  329. order.s = 1;
  330. if (order.c[0] == 1) {
  331. littleEndian = 1;
  332. }
  333. if ((mfd = open(MIXER_NAME, O_RDWR, 0)) == -1) {
  334. fprintf(stderr, "Unable to open mixer %s\n", MIXER_NAME);
  335. }
  336. }
  337. void
  338. SnackAudioFree()
  339. {
  340. int i, j;
  341. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  342. for (j = 0; j < 2; j++) {
  343. if (mixerLinks[i][j].mixer != NULL) {
  344. ckfree(mixerLinks[i][j].mixer);
  345. }
  346. if (mixerLinks[i][j].mixerVar != NULL) {
  347. ckfree(mixerLinks[i][j].mixerVar);
  348. }
  349. }
  350. if (mixerLinks[i][0].jack != NULL) {
  351. ckfree(mixerLinks[i][0].jack);
  352. }
  353. if (mixerLinks[i][0].jackVar != NULL) {
  354. ckfree((char *)mixerLinks[i][0].jackVar);
  355. }
  356. }
  357. close(mfd);
  358. }
  359. void
  360. ASetRecGain(int gain)
  361. {
  362. int g = min(max(gain, 0), 100);
  363. int recsrc = 0;
  364. g = g * 256 + g;
  365. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc);
  366. if (recsrc & SOUND_MASK_LINE) {
  367. ioctl(mfd, SOUND_MIXER_WRITE_LINE, &g);
  368. } else {
  369. ioctl(mfd, SOUND_MIXER_WRITE_MIC, &g);
  370. }
  371. }
  372. void
  373. ASetPlayGain(int gain)
  374. {
  375. int g = min(max(gain, 0), 100);
  376. int pcm_gain = 25700;
  377. g = g * 256 + g;
  378. ioctl(mfd, SOUND_MIXER_WRITE_VOLUME, &g);
  379. ioctl(mfd, SOUND_MIXER_WRITE_PCM, &pcm_gain);
  380. }
  381. int
  382. AGetRecGain()
  383. {
  384. int g = 0, left, right, recsrc = 0;
  385. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc);
  386. if (recsrc & SOUND_MASK_LINE) {
  387. ioctl(mfd, SOUND_MIXER_READ_LINE, &g);
  388. } else {
  389. ioctl(mfd, SOUND_MIXER_READ_MIC, &g);
  390. }
  391. left = g & 0xff;
  392. right = (g & 0xff00) / 256;
  393. g = (left + right) / 2;
  394. return(g);
  395. }
  396. int
  397. AGetPlayGain()
  398. {
  399. int g = 0, left, right;
  400. ioctl(mfd, SOUND_MIXER_READ_VOLUME, &g);
  401. left = g & 0xff;
  402. right = (g & 0xff00) / 256;
  403. g = (left + right) / 2;
  404. return(g);
  405. }
  406. int
  407. SnackAudioGetEncodings(char *device)
  408. {
  409. struct sio_hdl *hdl;
  410. struct sio_cap cap;
  411. // we always want to use the default device
  412. hdl = sio_open(NULL, SIO_PLAY, 0);
  413. if (hdl == NULL) {
  414. }
  415. return(LIN16);
  416. }
  417. void
  418. SnackAudioGetRates(char *device, char *buf, int n)
  419. {
  420. int freq, pos=0, i;
  421. int f[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 };
  422. for (i = 0; i < 8; i++) {
  423. freq = f[i];
  424. pos += sprintf(&buf[pos], "%d ", freq);
  425. }
  426. }
  427. int
  428. SnackAudioMaxNumberChannels(char *device)
  429. {
  430. return(2);
  431. }
  432. int
  433. SnackAudioMinNumberChannels(char *device)
  434. {
  435. return(1);
  436. }
  437. void
  438. SnackMixerGetInputJackLabels(char *buf, int n)
  439. {
  440. char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  441. int i, recMask, pos = 0;
  442. if (mfd != -1) {
  443. ioctl(mfd, SOUND_MIXER_READ_RECMASK, &recMask);
  444. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  445. if ((1 << i) & recMask) {
  446. pos += sprintf(&buf[pos], "%s", jackLabels[i]);
  447. pos += sprintf(&buf[pos], " ");
  448. }
  449. }
  450. } else {
  451. buf[0] = '\0';
  452. }
  453. buf[n-1] = '\0';
  454. }
  455. void
  456. SnackMixerGetOutputJackLabels(char *buf, int n)
  457. {
  458. buf[0] = '\0';
  459. }
  460. void
  461. SnackMixerGetInputJack(char *buf, int n)
  462. {
  463. char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  464. int i, recSrc = 0, pos = 0;
  465. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
  466. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  467. if ((1 << i) & recSrc) {
  468. pos += sprintf(&buf[pos], "%s", jackLabels[i]);
  469. while (isspace(buf[pos-1])) pos--;
  470. pos += sprintf(&buf[pos], " ");
  471. }
  472. }
  473. if(isspace(buf[pos-1])) pos--;
  474. buf[pos] = '\0';
  475. /*printf("SnackMixerGetInputJack %x, %s\n", recSrc, buf);*/
  476. }
  477. int
  478. SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status)
  479. {
  480. char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  481. int i, recSrc = 0, currSrc;
  482. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  483. if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) {
  484. recSrc = 1 << i;
  485. break;
  486. }
  487. }
  488. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &currSrc);
  489. /* printf("SnackMixerSetInputJack1 %x %s %s\n", currSrc, jack, status);*/
  490. if (strcmp(status, "1") == 0) {
  491. recSrc |= currSrc;
  492. } else {
  493. recSrc = (currSrc & ~recSrc);
  494. }
  495. /* printf("SnackMixerSetInputJack2 %x\n", recSrc);*/
  496. if (ioctl(mfd, SOUND_MIXER_WRITE_RECSRC, &recSrc) == -1) {
  497. return 1;
  498. } else {
  499. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
  500. /* printf("SnackMixerSetInputJack3 %x\n", recSrc);*/
  501. return 0;
  502. }
  503. return 1;
  504. }
  505. void
  506. SnackMixerGetOutputJack(char *buf, int n)
  507. {
  508. buf[0] = '\0';
  509. }
  510. void
  511. SnackMixerSetOutputJack(char *jack, char *status)
  512. {
  513. }
  514. static int dontTrace = 0;
  515. static char *
  516. JackVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1,
  517. CONST84 char *name2, int flags)
  518. {
  519. MixerLink *mixLink = (MixerLink *) clientData;
  520. char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  521. int i, recSrc = 0, status = 0;
  522. CONST84 char *stringValue;
  523. Tcl_Obj *obj, *var;
  524. if (dontTrace) return (char *) NULL;
  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. stringValue = Tcl_GetVar(interp, mixLink->jackVar, TCL_GLOBAL_ONLY);
  550. if (stringValue != NULL) {
  551. SnackMixerSetInputJack(interp, mixLink->jack, stringValue);
  552. }
  553. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
  554. /*printf("JackVarProc2 %x\n", recSrc);*/
  555. dontTrace = 1;
  556. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  557. if (mixerLinks[i][0].jackVar != NULL) {
  558. if ((1 << i) & recSrc) {
  559. status = 1;
  560. } else {
  561. status = 0;
  562. }
  563. obj = Tcl_NewIntObj(status);
  564. var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1);
  565. Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY |TCL_PARSE_PART1);
  566. }
  567. }
  568. dontTrace = 0;
  569. return (char *) NULL;
  570. }
  571. void
  572. SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var)
  573. {
  574. char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  575. int i, recSrc = 0, status;
  576. CONST84 char *value;
  577. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
  578. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  579. if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) {
  580. if ((1 << i) & recSrc) {
  581. status = 1;
  582. } else {
  583. status = 0;
  584. }
  585. mixerLinks[i][0].jack = SnackStrDup(jack);
  586. mixerLinks[i][0].jackVar = SnackStrDup(Tcl_GetStringFromObj(var, NULL));
  587. value = Tcl_GetVar(interp, mixerLinks[i][0].jackVar, TCL_GLOBAL_ONLY);
  588. if (value != NULL) {
  589. SnackMixerSetInputJack(interp, mixerLinks[i][0].jack, value);
  590. } else {
  591. Tcl_Obj *obj = Tcl_NewIntObj(status);
  592. Tcl_ObjSetVar2(interp, var, NULL, obj,
  593. TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
  594. }
  595. Tcl_TraceVar(interp, mixerLinks[i][0].jackVar,
  596. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  597. JackVarProc, (ClientData) &mixerLinks[i][0]);
  598. break;
  599. }
  600. }
  601. }
  602. void
  603. SnackMixerGetChannelLabels(char *line, char *buf, int n)
  604. {
  605. char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  606. int i, devMask;
  607. ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask);
  608. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  609. if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
  610. if (devMask & (1 << i)) {
  611. sprintf(buf, "Left Right");
  612. } else {
  613. sprintf(buf, "Mono");
  614. }
  615. break;
  616. }
  617. }
  618. }
  619. void
  620. SnackMixerGetVolume(char *line, int channel, char *buf, int n)
  621. {
  622. char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  623. int i, vol = 0, devMask, isStereo = 0, left, right;
  624. buf[0] = '\0';
  625. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  626. if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
  627. ioctl(mfd, MIXER_READ(i), &vol);
  628. ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask);
  629. if (devMask & (1 << i)) {
  630. isStereo = 1;
  631. }
  632. break;
  633. }
  634. }
  635. left = vol & 0xff;
  636. right = (vol & 0xff00) >> 8;
  637. if (isStereo) {
  638. if (channel == 0) {
  639. sprintf(buf, "%d", left);
  640. } else if (channel == 1) {
  641. sprintf(buf, "%d", right);
  642. } else if (channel == -1) {
  643. sprintf(buf, "%d", (left + right)/2);
  644. }
  645. } else {
  646. sprintf(buf, "%d", left);
  647. }
  648. }
  649. void
  650. SnackMixerSetVolume(char *line, int channel, int volume)
  651. {
  652. char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  653. int tmp = min(max(volume, 0), 100), i, oldVol = 0;
  654. int vol = (tmp << 8) + tmp;
  655. if (channel == 0) {
  656. vol = tmp;
  657. }
  658. if (channel == 1) {
  659. vol = tmp << 8;
  660. }
  661. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  662. if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
  663. ioctl(mfd, MIXER_READ(i), &oldVol);
  664. if (channel == 0) {
  665. vol = (oldVol & 0xff00) | (vol & 0x00ff);
  666. }
  667. if (channel == 1) {
  668. vol = (vol & 0xff00) | (oldVol & 0x00ff);
  669. }
  670. ioctl(mfd, MIXER_WRITE(i), &vol);
  671. break;
  672. }
  673. }
  674. }
  675. static char *
  676. VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1,
  677. CONST84 char *name2, int flags)
  678. {
  679. MixerLink *mixLink = (MixerLink *) clientData;
  680. CONST84 char *stringValue;
  681. if (flags & TCL_TRACE_UNSETS) {
  682. if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  683. Tcl_Obj *obj, *var;
  684. char tmp[VOLBUFSIZE];
  685. SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE);
  686. obj = Tcl_NewIntObj(atoi(tmp));
  687. var = Tcl_NewStringObj(mixLink->mixerVar, -1);
  688. Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
  689. Tcl_TraceVar(interp, mixLink->mixerVar,
  690. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  691. VolumeVarProc, mixLink);
  692. }
  693. return (char *) NULL;
  694. }
  695. stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY);
  696. if (stringValue != NULL) {
  697. SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue));
  698. }
  699. return (char *) NULL;
  700. }
  701. void
  702. SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n,
  703. Tcl_Obj *CONST objv[])
  704. {
  705. char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  706. int i, j, channel;
  707. CONST84 char *value;
  708. char tmp[VOLBUFSIZE];
  709. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  710. if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) {
  711. for (j = 0; j < n; j++) {
  712. if (n == 1) {
  713. channel = -1;
  714. } else {
  715. channel = j;
  716. }
  717. mixerLinks[i][j].mixer = SnackStrDup(line);
  718. mixerLinks[i][j].mixerVar = SnackStrDup(Tcl_GetStringFromObj(objv[j+3],NULL));
  719. mixerLinks[i][j].channel = j;
  720. value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY);
  721. if (value != NULL) {
  722. SnackMixerSetVolume(line, channel, atoi(value));
  723. } else {
  724. Tcl_Obj *obj;
  725. SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE);
  726. obj = Tcl_NewIntObj(atoi(tmp));
  727. Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj,
  728. TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
  729. }
  730. Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar,
  731. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  732. VolumeVarProc, (ClientData) &mixerLinks[i][j]);
  733. }
  734. }
  735. }
  736. }
  737. void
  738. SnackMixerUpdateVars(Tcl_Interp *interp)
  739. {
  740. int i, j, recSrc, status;
  741. char tmp[VOLBUFSIZE];
  742. Tcl_Obj *obj, *var;
  743. ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc);
  744. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  745. for (j = 0; j < 2; j++) {
  746. if (mixerLinks[i][j].mixerVar != NULL) {
  747. SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel,
  748. tmp, VOLBUFSIZE);
  749. obj = Tcl_NewIntObj(atoi(tmp));
  750. var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1);
  751. Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1);
  752. }
  753. }
  754. if (mixerLinks[i][0].jackVar != NULL) {
  755. if ((1 << i) & recSrc) {
  756. status = 1;
  757. } else {
  758. status = 0;
  759. }
  760. obj = Tcl_NewIntObj(status);
  761. var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1);
  762. Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1);
  763. }
  764. }
  765. }
  766. void
  767. SnackMixerGetLineLabels(char *buf, int n)
  768. {
  769. char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
  770. int i, devMask, pos = 0;
  771. if (mfd != -1) {
  772. ioctl(mfd, SOUND_MIXER_READ_DEVMASK, &devMask);
  773. for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  774. if ((1 << i) & devMask && pos < n-8) {
  775. pos += sprintf(&buf[pos], "%s", mixLabels[i]);
  776. pos += sprintf(&buf[pos], " ");
  777. }
  778. }
  779. } else {
  780. buf[0] = '\0';
  781. }
  782. buf[n-1] = '\0';
  783. }
  784. int
  785. SnackGetOutputDevices(char **arr, int n)
  786. {
  787. return SnackGetInputDevices(arr, n);
  788. }
  789. int
  790. SnackGetInputDevices(char **arr, int n)
  791. {
  792. int i, j = 0;
  793. glob_t globt;
  794. glob("/dev/audio*", 0, NULL, &globt);
  795. //glob("/dev/audio*", GLOB_APPEND, NULL, &globt);
  796. for (i = 0; i < globt.gl_pathc; i++) {
  797. if (j < n) {
  798. arr[j++] = (char *) SnackStrDup("default");
  799. }
  800. }
  801. globfree(&globt);
  802. return(1);
  803. }
  804. int
  805. SnackGetMixerDevices(char **arr, int n)
  806. {
  807. int i, j = 0;
  808. glob_t globt;
  809. glob("/dev/mixer*", 0, NULL, &globt);
  810. for (i = 0; i < globt.gl_pathc; i++) {
  811. if (j < n) {
  812. arr[j++] = (char *) SnackStrDup(globt.gl_pathv[i]);
  813. }
  814. }
  815. globfree(&globt);
  816. return(j);
  817. }