PageRenderTime 52ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/netbsd/src/external/bsd/ntp/dist/libntp/audio.c

https://bitbucket.org/killerpenguinassassins/open_distrib_devel
C | 505 lines | 383 code | 55 blank | 67 comment | 99 complexity | 9ac53ebb9c93a8ab0b8d4b427d81cbf2 MD5 | raw file
Possible License(s): CC0-1.0, MIT, LGPL-2.0, LGPL-3.0, WTFPL, GPL-2.0, BSD-2-Clause, AGPL-3.0, CC-BY-SA-3.0, MPL-2.0, JSON, BSD-3-Clause-No-Nuclear-License-2014, LGPL-2.1, CPL-1.0, AGPL-1.0, 0BSD, ISC, Apache-2.0, GPL-3.0, IPL-1.0, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /* $NetBSD: audio.c,v 1.4 2012/02/01 07:46:22 kardel Exp $ */
  2. /*
  3. * audio.c - audio interface for reference clock audio drivers
  4. */
  5. #ifdef HAVE_CONFIG_H
  6. # include <config.h>
  7. #endif
  8. #if defined(HAVE_SYS_AUDIOIO_H) || defined(HAVE_SUN_AUDIOIO_H) || \
  9. defined(HAVE_SYS_SOUNDCARD_H) || defined(HAVE_MACHINE_SOUNDCARD_H)
  10. #include "audio.h"
  11. #include "ntp_stdlib.h"
  12. #include "ntp_syslog.h"
  13. #ifdef HAVE_UNISTD_H
  14. # include <unistd.h>
  15. #endif
  16. #include <stdio.h>
  17. #include "ntp_string.h"
  18. #ifdef HAVE_SYS_AUDIOIO_H
  19. # include <sys/audioio.h>
  20. #endif /* HAVE_SYS_AUDIOIO_H */
  21. #ifdef HAVE_SUN_AUDIOIO_H
  22. # include <sys/ioccom.h>
  23. # include <sun/audioio.h>
  24. #endif /* HAVE_SUN_AUDIOIO_H */
  25. #ifdef HAVE_SYS_IOCTL_H
  26. # include <sys/ioctl.h>
  27. #endif /* HAVE_SYS_IOCTL_H */
  28. #include <fcntl.h>
  29. #ifdef HAVE_MACHINE_SOUNDCARD_H
  30. # include <machine/soundcard.h>
  31. # define PCM_STYLE_SOUND
  32. #else
  33. # ifdef HAVE_SYS_SOUNDCARD_H
  34. # include <sys/soundcard.h>
  35. # define PCM_STYLE_SOUND
  36. # endif
  37. #endif
  38. #ifdef PCM_STYLE_SOUND
  39. # include <ctype.h>
  40. #endif
  41. /*
  42. * Global variables
  43. */
  44. #ifdef HAVE_SYS_AUDIOIO_H
  45. static struct audio_device device; /* audio device ident */
  46. #endif /* HAVE_SYS_AUDIOIO_H */
  47. #ifdef PCM_STYLE_SOUND
  48. # define INIT_FILE "/etc/ntp.audio"
  49. int agc = SOUND_MIXER_WRITE_RECLEV; /* or IGAIN or LINE */
  50. int monitor = SOUND_MIXER_WRITE_VOLUME; /* or OGAIN */
  51. int devmask = 0;
  52. int recmask = 0;
  53. char cf_c_dev[100], cf_i_dev[100], cf_agc[100], cf_monitor[100];
  54. const char *m_names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
  55. #else /* not PCM_STYLE_SOUND */
  56. static struct audio_info info; /* audio device info */
  57. #endif /* not PCM_STYLE_SOUND */
  58. static int ctl_fd; /* audio control file descriptor */
  59. #ifdef PCM_STYLE_SOUND
  60. static void audio_config_read (int, char **, char **);
  61. static int mixer_name (const char *, int);
  62. int
  63. mixer_name(
  64. const char *m_name,
  65. int m_mask
  66. )
  67. {
  68. int i;
  69. for (i = 0; i < SOUND_MIXER_NRDEVICES; ++i)
  70. if (((1 << i) & m_mask)
  71. && !strcmp(m_names[i], m_name))
  72. break;
  73. return (SOUND_MIXER_NRDEVICES == i)
  74. ? -1
  75. : i
  76. ;
  77. }
  78. /*
  79. * Check:
  80. *
  81. * /etc/ntp.audio# where # is the unit number
  82. * /etc/ntp.audio.# where # is the unit number
  83. * /etc/ntp.audio
  84. *
  85. * for contents of the form:
  86. *
  87. * idev /dev/input_device
  88. * cdev /dev/control_device
  89. * agc pcm_input_device {igain,line,line1,...}
  90. * monitor pcm_monitor_device {ogain,...}
  91. *
  92. * The device names for the "agc" and "monitor" keywords
  93. * can be found by running either the "mixer" program or the
  94. * util/audio-pcm program.
  95. *
  96. * Great hunks of this subroutine were swiped from refclock_oncore.c
  97. */
  98. static void
  99. audio_config_read(
  100. int unit,
  101. char **c_dev, /* Control device */
  102. char **i_dev /* input device */
  103. )
  104. {
  105. FILE *fd;
  106. char device[20], line[100], ab[100];
  107. snprintf(device, sizeof(device), "%s%d", INIT_FILE, unit);
  108. if ((fd = fopen(device, "r")) == NULL) {
  109. printf("audio_config_read: <%s> NO\n", device);
  110. snprintf(device, sizeof(device), "%s.%d", INIT_FILE,
  111. unit);
  112. if ((fd = fopen(device, "r")) == NULL) {
  113. printf("audio_config_read: <%s> NO\n", device);
  114. snprintf(device, sizeof(device), "%s",
  115. INIT_FILE);
  116. if ((fd = fopen(device, "r")) == NULL) {
  117. printf("audio_config_read: <%s> NO\n",
  118. device);
  119. return;
  120. }
  121. }
  122. }
  123. printf("audio_config_read: reading <%s>\n", device);
  124. while (fgets(line, sizeof line, fd)) {
  125. char *cp, *cc, *ca;
  126. int i;
  127. /* Remove comments */
  128. if ((cp = strchr(line, '#')))
  129. *cp = '\0';
  130. /* Remove any trailing spaces */
  131. for (i = strlen(line);
  132. i > 0 && isascii((unsigned char)line[i - 1]) && isspace((unsigned char)line[i - 1]);
  133. )
  134. line[--i] = '\0';
  135. /* Remove leading space */
  136. for (cc = line; *cc && isascii((unsigned char)*cc) && isspace((unsigned char)*cc); cc++)
  137. continue;
  138. /* Stop if nothing left */
  139. if (!*cc)
  140. continue;
  141. /* Uppercase the command and find the arg */
  142. for (ca = cc; *ca; ca++) {
  143. if (isascii((unsigned char)*ca)) {
  144. if (islower((unsigned char)*ca)) {
  145. *ca = toupper((unsigned char)*ca);
  146. } else if (isspace((unsigned char)*ca) || (*ca == '='))
  147. break;
  148. }
  149. }
  150. /* Remove space (and possible =) leading the arg */
  151. for (; *ca && isascii((unsigned char)*ca) && (isspace((unsigned char)*ca) || (*ca == '=')); ca++)
  152. continue;
  153. if (!strncmp(cc, "IDEV", 4) &&
  154. 1 == sscanf(ca, "%99s", ab)) {
  155. strncpy(cf_i_dev, ab, sizeof(cf_i_dev));
  156. printf("idev <%s>\n", ab);
  157. } else if (!strncmp(cc, "CDEV", 4) &&
  158. 1 == sscanf(ca, "%99s", ab)) {
  159. strncpy(cf_c_dev, ab, sizeof(cf_c_dev));
  160. printf("cdev <%s>\n", ab);
  161. } else if (!strncmp(cc, "AGC", 3) &&
  162. 1 == sscanf(ca, "%99s", ab)) {
  163. strncpy(cf_agc, ab, sizeof(cf_agc));
  164. printf("agc <%s> %d\n", ab, i);
  165. } else if (!strncmp(cc, "MONITOR", 7) &&
  166. 1 == sscanf(ca, "%99s", ab)) {
  167. strncpy(cf_monitor, ab, sizeof(cf_monitor));
  168. printf("monitor <%s> %d\n", ab, mixer_name(ab, -1));
  169. }
  170. }
  171. fclose(fd);
  172. return;
  173. }
  174. #endif /* PCM_STYLE_SOUND */
  175. /*
  176. * audio_init - open and initialize audio device
  177. *
  178. * This code works with SunOS 4.x, Solaris 2.x, and PCM; however, it is
  179. * believed generic and applicable to other systems with a minor twid
  180. * or two. All it does is open the device, set the buffer size (Solaris
  181. * only), preset the gain and set the input port. It assumes that the
  182. * codec sample rate (8000 Hz), precision (8 bits), number of channels
  183. * (1) and encoding (ITU-T G.711 mu-law companded) have been set by
  184. * default.
  185. */
  186. int
  187. audio_init(
  188. const char *dname, /* device name */
  189. int bufsiz, /* buffer size */
  190. int unit /* device unit (0-3) */
  191. )
  192. {
  193. #ifdef PCM_STYLE_SOUND
  194. # define ACTL_DEV "/dev/mixer%d"
  195. char actl_dev[30];
  196. # ifdef HAVE_STRUCT_SND_SIZE
  197. struct snd_size s_size;
  198. # endif
  199. # ifdef AIOGFMT
  200. snd_chan_param s_c_p;
  201. # endif
  202. #endif
  203. int fd;
  204. int rval;
  205. const char *actl =
  206. #ifdef PCM_STYLE_SOUND
  207. actl_dev
  208. #else
  209. "/dev/audioctl"
  210. #endif
  211. ;
  212. #ifdef PCM_STYLE_SOUND
  213. snprintf(actl_dev, sizeof(actl_dev), ACTL_DEV, unit);
  214. audio_config_read(unit, &actl, &dname);
  215. /* If we have values for cf_c_dev or cf_i_dev, use them. */
  216. if (*cf_c_dev)
  217. actl = cf_c_dev;
  218. if (*cf_i_dev)
  219. dname = cf_i_dev;
  220. #endif
  221. /*
  222. * Open audio device
  223. */
  224. fd = open(dname, O_RDWR | O_NONBLOCK, 0777);
  225. if (fd < 0) {
  226. msyslog(LOG_ERR, "audio_init: %s %m\n", dname);
  227. return (fd);
  228. }
  229. /*
  230. * Open audio control device.
  231. */
  232. ctl_fd = open(actl, O_RDWR);
  233. if (ctl_fd < 0) {
  234. msyslog(LOG_ERR, "audio_init: invalid control device <%s>\n",
  235. actl);
  236. close(fd);
  237. return(ctl_fd);
  238. }
  239. /*
  240. * Set audio device parameters.
  241. */
  242. #ifdef PCM_STYLE_SOUND
  243. printf("audio_init: <%s> bufsiz %d\n", dname, bufsiz);
  244. rval = fd;
  245. # ifdef HAVE_STRUCT_SND_SIZE
  246. if (ioctl(fd, AIOGSIZE, &s_size) == -1)
  247. printf("audio_init: AIOGSIZE: %s\n", strerror(errno));
  248. else
  249. printf("audio_init: orig: play_size %d, rec_size %d\n",
  250. s_size.play_size, s_size.rec_size);
  251. s_size.play_size = s_size.rec_size = bufsiz;
  252. printf("audio_init: want: play_size %d, rec_size %d\n",
  253. s_size.play_size, s_size.rec_size);
  254. if (ioctl(fd, AIOSSIZE, &s_size) == -1)
  255. printf("audio_init: AIOSSIZE: %s\n", strerror(errno));
  256. else
  257. printf("audio_init: set: play_size %d, rec_size %d\n",
  258. s_size.play_size, s_size.rec_size);
  259. # endif /* HAVE_STRUCT_SND_SIZE */
  260. # ifdef SNDCTL_DSP_SETFRAGMENT
  261. {
  262. int tmp = (16 << 16) + 6; /* 16 fragments, each 2^6 bytes */
  263. if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
  264. printf("audio_init: SNDCTL_DSP_SETFRAGMENT: %s\n",
  265. strerror(errno));
  266. }
  267. # endif /* SNDCTL_DSP_SETFRAGMENT */
  268. # ifdef AIOGFMT
  269. if (ioctl(fd, AIOGFMT, &s_c_p) == -1)
  270. printf("audio_init: AIOGFMT: %s\n", strerror(errno));
  271. else
  272. printf("audio_init: play_rate %lu, rec_rate %lu, play_format %#lx, rec_format %#lx\n",
  273. s_c_p.play_rate, s_c_p.rec_rate, s_c_p.play_format, s_c_p.rec_format);
  274. # endif
  275. /* Grab the device and record masks */
  276. if (ioctl(ctl_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
  277. printf("SOUND_MIXER_READ_DEVMASK: %s\n", strerror(errno));
  278. if (ioctl(ctl_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
  279. printf("SOUND_MIXER_READ_RECMASK: %s\n", strerror(errno));
  280. /* validate and set any specified config file stuff */
  281. if (cf_agc[0] != '\0') {
  282. int i;
  283. i = mixer_name(cf_agc, devmask);
  284. if (i >= 0)
  285. agc = MIXER_WRITE(i);
  286. else
  287. printf("input %s not in recmask %#x\n",
  288. cf_agc, recmask);
  289. }
  290. if (cf_monitor[0] != '\0') {
  291. int i;
  292. /* devmask */
  293. i = mixer_name(cf_monitor, devmask);
  294. if (i >= 0)
  295. monitor = MIXER_WRITE(i);
  296. else
  297. printf("monitor %s not in devmask %#x\n",
  298. cf_monitor, devmask);
  299. }
  300. #else /* not PCM_STYLE_SOUND */
  301. AUDIO_INITINFO(&info);
  302. info.play.gain = AUDIO_MAX_GAIN;
  303. info.play.port = AUDIO_SPEAKER;
  304. # ifdef HAVE_SYS_AUDIOIO_H
  305. info.record.buffer_size = bufsiz;
  306. # endif /* HAVE_SYS_AUDIOIO_H */
  307. rval = ioctl(ctl_fd, (int)AUDIO_SETINFO, (char *)&info);
  308. if (rval < 0) {
  309. msyslog(LOG_ERR, "audio: invalid control device parameters\n");
  310. close(ctl_fd);
  311. close(fd);
  312. return(rval);
  313. }
  314. rval = fd;
  315. #endif /* not PCM_STYLE_SOUND */
  316. return (rval);
  317. }
  318. /*
  319. * audio_gain - adjust codec gains and port
  320. */
  321. int
  322. audio_gain(
  323. int gain, /* volume level (gain) 0-255 */
  324. int mongain, /* input to output mix (monitor gain) 0-255 */
  325. int port /* selected I/O port: 1 mic/2 line in */
  326. )
  327. {
  328. int rval;
  329. static int o_mongain = -1;
  330. static int o_port = -1;
  331. #ifdef PCM_STYLE_SOUND
  332. int l, r;
  333. rval = 0;
  334. r = l = 100 * gain / 255; /* Normalize to 0-100 */
  335. # ifdef DEBUG
  336. if (debug > 1)
  337. printf("audio_gain: gain %d/%d\n", gain, l);
  338. # endif
  339. #if 0 /* not a good idea to do this; connector wiring dependency */
  340. /* figure out what channel(s) to use. just nuke right for now. */
  341. r = 0 ; /* setting to zero nicely mutes the channel */
  342. #endif
  343. l |= r << 8;
  344. if (cf_agc[0] != '\0')
  345. rval = ioctl(ctl_fd, agc, &l);
  346. else
  347. if (2 == port)
  348. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_LINE, &l);
  349. else
  350. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_MIC, &l);
  351. if (-1 == rval) {
  352. printf("audio_gain: agc write: %s\n", strerror(errno));
  353. return rval;
  354. }
  355. if (o_mongain != mongain) {
  356. r = l = 100 * mongain / 255; /* Normalize to 0-100 */
  357. # ifdef DEBUG
  358. if (debug > 1)
  359. printf("audio_gain: mongain %d/%d\n", mongain, l);
  360. # endif
  361. l |= r << 8;
  362. if (cf_monitor[0] != '\0')
  363. rval = ioctl(ctl_fd, monitor, &l );
  364. else
  365. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_VOLUME,
  366. &l);
  367. if (-1 == rval) {
  368. printf("audio_gain: mongain write: %s\n",
  369. strerror(errno));
  370. return (rval);
  371. }
  372. o_mongain = mongain;
  373. }
  374. if (o_port != port) {
  375. # ifdef DEBUG
  376. if (debug > 1)
  377. printf("audio_gain: port %d\n", port);
  378. # endif
  379. l = (1 << ((port == 2) ? SOUND_MIXER_LINE : SOUND_MIXER_MIC));
  380. rval = ioctl(ctl_fd, SOUND_MIXER_WRITE_RECSRC, &l);
  381. if (rval == -1) {
  382. printf("SOUND_MIXER_WRITE_RECSRC: %s\n",
  383. strerror(errno));
  384. return (rval);
  385. }
  386. # ifdef DEBUG
  387. if (debug > 1) {
  388. if (ioctl(ctl_fd, SOUND_MIXER_READ_RECSRC, &l) == -1)
  389. printf("SOUND_MIXER_WRITE_RECSRC: %s\n",
  390. strerror(errno));
  391. else
  392. printf("audio_gain: recsrc is %d\n", l);
  393. }
  394. # endif
  395. o_port = port;
  396. }
  397. #else /* not PCM_STYLE_SOUND */
  398. ioctl(ctl_fd, (int)AUDIO_GETINFO, (char *)&info);
  399. info.record.encoding = AUDIO_ENCODING_ULAW;
  400. info.record.error = 0;
  401. info.record.gain = gain;
  402. if (o_mongain != mongain)
  403. o_mongain = info.monitor_gain = mongain;
  404. if (o_port != port)
  405. o_port = info.record.port = port;
  406. rval = ioctl(ctl_fd, (int)AUDIO_SETINFO, (char *)&info);
  407. if (rval < 0) {
  408. msyslog(LOG_ERR, "audio_gain: %m");
  409. return (rval);
  410. }
  411. rval = info.record.error;
  412. #endif /* not PCM_STYLE_SOUND */
  413. return (rval);
  414. }
  415. /*
  416. * audio_show - display audio parameters
  417. *
  418. * This code doesn't really do anything, except satisfy curiousity and
  419. * verify the ioctl's work.
  420. */
  421. void
  422. audio_show(void)
  423. {
  424. #ifdef PCM_STYLE_SOUND
  425. int recsrc = 0;
  426. printf("audio_show: ctl_fd %d\n", ctl_fd);
  427. if (ioctl(ctl_fd, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
  428. printf("SOUND_MIXER_READ_RECSRC: %s\n", strerror(errno));
  429. #else /* not PCM_STYLE_SOUND */
  430. # ifdef HAVE_SYS_AUDIOIO_H
  431. ioctl(ctl_fd, (int)AUDIO_GETDEV, &device);
  432. printf("audio: name %s, version %s, config %s\n",
  433. device.name, device.version, device.config);
  434. # endif /* HAVE_SYS_AUDIOIO_H */
  435. ioctl(ctl_fd, (int)AUDIO_GETINFO, (char *)&info);
  436. printf(
  437. "audio: rate %d, chan %d, prec %d, code %d, gain %d, mon %d, port %d\n",
  438. info.record.sample_rate, info.record.channels,
  439. info.record.precision, info.record.encoding,
  440. info.record.gain, info.monitor_gain, info.record.port);
  441. printf(
  442. "audio: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n",
  443. info.record.samples, info.record.eof,
  444. info.record.pause, info.record.error,
  445. info.record.waiting, info.record.balance);
  446. #endif /* not PCM_STYLE_SOUND */
  447. }
  448. #else
  449. int audio_bs;
  450. #endif /* HAVE_{SYS_AUDIOIO,SUN_AUDIOIO,MACHINE_SOUNDCARD,SYS_SOUNDCARD}_H */