/contrib/ntp/libntp/audio.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 496 lines · 375 code · 55 blank · 66 comment · 92 complexity · 7edae80175e836bcab549ee9d3b1f7a6 MD5 · raw file

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