1 /*
2  *   cdda - CD Digital Audio support
3  *
4  *   Copyright (C) 1993-2004  Ti Kan
5  *   E-mail: xmcd@amb.org
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 /*
23  *   Digital/Compaq/HP Tru64 UNIX (OSF1) and OpenVMS
24  *   Multi-media Extension subset (MME) audio driver support
25  */
26 #ifndef lint
27 static char *_wr_osf1_c_ident_ = "@(#)wr_osf1.c	7.50 04/03/24";
28 #endif
29 
30 #include "common_d/appenv.h"
31 #include "common_d/util.h"
32 #include "libdi_d/libdi.h"
33 #include "cdda_d/cdda.h"
34 #include "cdda_d/common.h"
35 
36 #if defined(CDDA_WR_OSF1) && defined(CDDA_SUPPORTED)
37 
38 #ifdef HAS_MME
39 #include <mme/mme_api.h>
40 #endif
41 #include "cdda_d/wr_gen.h"
42 #include "cdda_d/wr_osf1.h"
43 
44 
45 extern appdata_t	app_data;
46 extern FILE		*errfp;
47 
48 #ifdef HAS_MME
49 STATIC HWAVEOUT		osf1_devhandle = 0;	/* Audio device handle */
50 STATIC UINT		osf1_devid;		/* Audio device ID */
51 STATIC LPWAVEHDR	osf1_wavehdrp = NULL;	/* Wave header pointer */
52 #endif	/* HAS_MME */
53 
54 STATIC int		osf1_wsemid,		/* Semaphores identifier */
55 			osf1_curr_vol0,		/* Current left volume */
56 			osf1_curr_vol1;		/* Current right volume */
57 STATIC pid_t		osf1_pipe_pid = -1;	/* Pipe to program pid */
58 STATIC cd_state_t	*osf1_wcd;		/* Shared memory pointer */
59 STATIC bool_t		osf1_write_dev,		/* Write to audio device */
60 			osf1_write_file,	/* Write to output file */
61 			osf1_write_pipe,	/* Pipe to program */
62 			osf1_file_be = FALSE,	/* Big endian output file */
63 			osf1_vol_supp = FALSE,	/* Volume control supported */
64 			osf1_bal_supp = FALSE;	/* Balance control supported */
65 STATIC gen_desc_t	*osf1_file_desc = NULL,	/* Output file desc */
66 			*osf1_pipe_desc = NULL;	/* Pipe to program desc */
67 STATIC unsigned int	osf1_file_mode;		/* Output file mode */
68 
69 
70 #ifdef HAS_MME
71 /*
72  * osf1aud_wave_cb
73  *	Callback function used to notify that playing is done on a
74  *	block of audio data.
75  *
76  * Args:
77  *	waveout - waveout handle
78  *	msg - message code
79  *	instance - instance number
80  *	p1 - parameter 1
81  *	p2 - parameter 2
82  *
83  * Return:
84  *	Nothing.
85  */
86 /*ARGSUSED*/
87 STATIC void
osf1aud_wave_cb(HANDLE waveout,UINT msg,DWORD instance,LPARAM p1,LPARAM p2)88 osf1aud_wave_cb(HANDLE waveout, UINT msg, DWORD instance, LPARAM p1, LPARAM p2)
89 {
90 	switch (msg) {
91 	case WOM_OPEN:
92 	case WOM_CLOSE:
93 	case WOM_DONE:
94 		break;
95 	default:
96 		DBGPRN(DBG_SND)(errfp,
97 			"osf1aud_wave_cb: unknown message %u received.\n",
98 			msg);
99 		break;
100 	}
101 }
102 
103 
104 /*
105  * osf1aud_scale_vol
106  *	Scale volume from xmcd 0-100 to MME audio device values
107  *
108  * Args:
109  *	vol - xmcd volume 0-100
110  *
111  * Return:
112  *	Device volume value
113  */
114 STATIC int
osf1aud_scale_vol(int vol)115 osf1aud_scale_vol(int vol)
116 {
117 	int	n;
118 
119 	n = ((vol * MME_MAX_VOL) / 100);
120 	if (((vol * MME_MAX_VOL) % 100) >= 50)
121 		n++;
122 
123 	/* Range checking */
124 	if (n > MME_MAX_VOL)
125 		n = MME_MAX_VOL;
126 
127 	return (n);
128 }
129 
130 
131 /*
132  * osf1aud_unscale_vol
133  *	Scale volume from MME audio device values to xmcd 0-100
134  *
135  * Args:
136  *	vol - device volume value
137  *
138  * Return:
139  *	xmcd volume 0-100
140  */
141 STATIC int
osf1aud_unscale_vol(int vol)142 osf1aud_unscale_vol(int vol)
143 {
144 	int	n;
145 
146 	n = (vol * 100) / MME_MAX_VOL;
147 	if (((vol * 100) % MME_MAX_VOL) >= (MME_MAX_VOL >> 1))
148 		n++;
149 
150 	/* Range checking */
151 	if (n < 0)
152 		n = 0;
153 	else if (n > 100)
154 		n = 100;
155 
156 	return (n);
157 }
158 
159 
160 /*
161  * osf1aud_gethw_vol
162  *	Get hardware volume control settings and update global variables.
163  *
164  * Args:
165  *	None.
166  *
167  * Return:
168  *	Nothing.
169  */
170 STATIC void
osf1aud_gethw_vol(void)171 osf1aud_gethw_vol(void)
172 {
173 	MMRESULT	status;
174 	LPDWORD		vol;
175 	int		vol0,
176 			vol1,
177 			val;
178 
179 	if (!osf1_vol_supp)
180 		/* Just do nothing */
181 		return;
182 
183 	vol = (LPDWORD) mmeAllocMem(sizeof(DWORD));
184 	if (vol == NULL) {
185 		(void) sprintf(osf1_wcd->i->msgbuf,
186 			    "osf1aud_gethw_vol: Out of memory");
187 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
188 		return;
189 	}
190 	(void) memset(vol, 0, sizeof(DWORD));
191 
192 	status = waveOutGetVolume(osf1_devid, vol);
193 	if (status != MMSYSERR_NOERROR) {
194 		mmeFreeMem(vol);
195 		(void) sprintf(osf1_wcd->i->msgbuf,
196 			    "osf1aud_gethw_vol: "
197 			    "waveOutGetVolume failed (status=%d)", status);
198 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
199 	}
200 
201 	if (osf1_bal_supp) {
202 		vol0 = (*vol & 0x0000ffff);
203 		vol1 = (*vol & 0xffff0000) >> 16;
204 	}
205 	else
206 		vol0 = vol1 = (*vol & 0x0000ffff);
207 
208 	mmeFreeMem(vol);
209 
210 	vol0 = osf1aud_unscale_vol(vol0);
211 	vol1 = osf1aud_unscale_vol(vol1);
212 
213 	if (vol0 == osf1_curr_vol0 && vol1 == osf1_curr_vol1)
214 		/* No change */
215 		return;
216 
217 	osf1_curr_vol0 = vol0;
218 	osf1_curr_vol1 = vol1;
219 
220 	/* Update volume and balance */
221 	if (vol0 == vol1) {
222 		osf1_wcd->i->vol = util_untaper_vol(osf1_curr_vol0);
223 		osf1_wcd->i->vol_left = 100;
224 		osf1_wcd->i->vol_right = 100;
225 	}
226 	else if (vol0 > vol1) {
227 		osf1_wcd->i->vol = util_untaper_vol(osf1_curr_vol0);
228 		osf1_wcd->i->vol_left = 100;
229 		osf1_wcd->i->vol_right =
230 			(osf1_curr_vol1 * 100) / osf1_curr_vol0;
231 		val = osf1_curr_vol0 >> 1;
232 		if (((osf1_curr_vol1 * 100) % osf1_curr_vol0) >= val)
233 		osf1_wcd->i->vol_right++;
234 	}
235 	else {
236 		osf1_wcd->i->vol = util_untaper_vol(osf1_curr_vol1);
237 		osf1_wcd->i->vol_right = 100;
238 		osf1_wcd->i->vol_left =
239 			(osf1_curr_vol0 * 100) / osf1_curr_vol1;
240 		val = osf1_curr_vol1 >> 1;
241 		if (((osf1_curr_vol0 * 100) % osf1_curr_vol1) >= val)
242 			osf1_wcd->i->vol_left++;
243 	}
244 }
245 
246 
247 /*
248  * osf1aud_sethw_vol
249  *	Set hardware volume control settings.
250  *
251  * Args:
252  *	vol0 - Left channel volume
253  *	vol1 - Right channel volume
254  *
255  * Return:
256  *	Nothing.
257  */
258 STATIC void
osf1aud_sethw_vol(int vol0,int vol1)259 osf1aud_sethw_vol(int vol0, int vol1)
260 {
261 	MMRESULT	status;
262 	DWORD		vol;
263 	int		hvol0,
264 			hvol1;
265 
266 	if (!osf1_vol_supp)
267 		/* Just do nothing */
268 		return;
269 
270 	hvol0 = osf1aud_scale_vol(vol0);
271 	hvol1 = osf1aud_scale_vol(vol1);
272 
273 	if (osf1_bal_supp)
274 		vol = ((hvol1 & 0xffff) << 16) | (hvol0 & 0xffff);
275 	else
276 		vol = (hvol0 & 0xffff);
277 
278 	status = waveOutSetVolume(osf1_devid, vol);
279 	if (status != MMSYSERR_NOERROR) {
280 		(void) sprintf(osf1_wcd->i->msgbuf,
281 			    "osf1aud_sethw_vol: "
282 			    "waveOutSetVolume failed (status=%d)", status);
283 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
284 	}
285 
286 	osf1_curr_vol0 = vol0;
287 	osf1_curr_vol1 = vol1;
288 }
289 
290 #endif	/* HAS_MME */
291 
292 
293 /*
294  * osf1aud_open_dev
295  *	Opens the audio device.
296  *
297  * Args:
298  *	None.
299  *
300  * Return:
301  *	FALSE - failure
302  *	TRUE  - success
303  */
304 STATIC bool_t
osf1aud_open_dev(void)305 osf1aud_open_dev(void)
306 {
307 #ifndef HAS_MME
308 	(void) sprintf(osf1_wcd->i->msgbuf,
309 		    "osf1aud_open_dev: MMS audio support not compiled-in");
310 	DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
311 	return FALSE;
312 #else
313 	MMRESULT	status;
314 	LPWAVEOUTCAPS	caps;
315 	char		*cp;
316 
317 	if (!gen_set_eid(osf1_wcd))
318 		return FALSE;
319 
320 	if (waveOutGetNumDevs() < 1) {
321 		(void) sprintf(osf1_wcd->i->msgbuf,
322 			    "osf1aud_open_dev: audio disabled:\n%s",
323 			    "No MMS audio device available.");
324 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
325 		(void) gen_reset_eid(osf1_wcd);
326 		return FALSE;
327 	}
328 
329 	caps = (LPWAVEOUTCAPS) mmeAllocMem(sizeof(WAVEOUTCAPS));
330 	if (caps == NULL) {
331 		(void) sprintf(osf1_wcd->i->msgbuf,
332 			    "osf1aud_open_dev: Out of memory");
333 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
334 		(void) gen_reset_eid(osf1_wcd);
335 		return FALSE;
336 	}
337 	(void) memset(caps, 0, sizeof(WAVEOUTCAPS));
338 
339 	/* Use AUDIODEV environment for device ID if specified by user,
340 	 * otherwise use 0.
341 	 */
342 	if ((cp = getenv("AUDIODEV")) != NULL)
343 		osf1_devid = (UINT) atoi(cp);
344 	else
345 		osf1_devid = 0;
346 
347 	status = waveOutGetDevCaps(osf1_devid, caps, sizeof(WAVEOUTCAPS));
348 	if (status != MMSYSERR_NOERROR) {
349 		mmeFreeMem(caps);
350 		(void) sprintf(osf1_wcd->i->msgbuf,
351 			    "osf1aud_open_dev: "
352 			    "waveOutGetDevCaps failed (status=%d)", status);
353 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
354 		(void) gen_reset_eid(osf1_wcd);
355 		return FALSE;
356 	}
357 
358 	if ((caps->dwFormats & WAVE_FORMAT_4S16) == 0) {
359 		mmeFreeMem(caps);
360 		(void) sprintf(osf1_wcd->i->msgbuf,
361 			    "osf1aud_open_dev: The audio device does not\n"
362 			    "support 44.1 kHz Stereo 16-bit audio.");
363 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
364 		(void) gen_reset_eid(osf1_wcd);
365 		return FALSE;
366 	}
367 
368 	if ((caps->dwSupport & WAVECAPS_VOLUME) != 0) {
369 		osf1_vol_supp = TRUE;
370 	}
371 	if ((caps->dwSupport & WAVECAPS_LRVOLUME) != 0) {
372 		osf1_vol_supp = TRUE;
373 		osf1_bal_supp = TRUE;
374 	}
375 
376 	mmeFreeMem(caps);
377 
378 	DBGPRN(DBG_GEN|DBG_SND)(errfp,
379 		"osf1aud_open_dev: MME wave out: vol_supp=%d bal_supp=%d\n",
380 		(int) osf1_vol_supp, (int) osf1_bal_supp
381 	);
382 
383 	(void) gen_reset_eid(osf1_wcd);
384 	return TRUE;
385 #endif	/* HAS_MME */
386 }
387 
388 
389 /*
390  * osf1aud_close_dev
391  *	Closes the audio device.
392  *
393  * Args:
394  *	None.
395  *
396  * Return:
397  *	FALSE - failure
398  *	TRUE  - success
399  */
400 STATIC bool_t
osf1aud_close_dev(void)401 osf1aud_close_dev(void)
402 {
403 #ifndef HAS_MME
404 	return FALSE;
405 #else
406 	MMRESULT	status;
407 
408 	if (!gen_set_eid(osf1_wcd))
409 		return FALSE;
410 
411 	if ((status = waveOutReset(osf1_devhandle)) != MMSYSERR_NOERROR) {
412 		DBGPRN(DBG_SND)(errfp, "osf1aud_close_dev: "
413 			    "waveOutReset failed (status=%d)\n", status);
414 	}
415 	if ((status = waveOutClose(osf1_devhandle)) != MMSYSERR_NOERROR) {
416 		DBGPRN(DBG_SND)(errfp, "osf1aud_close_dev: "
417 			    "waveOutClose failed (status=%d)\n", status);
418 	}
419 
420 	DBGPRN(DBG_GEN|DBG_SND)(errfp, "\nClosed [audio:%u]\n", osf1_devid);
421 
422 	osf1_devhandle = 0;
423 	mmeFreeMem(osf1_wavehdrp);
424 	osf1_wavehdrp = NULL;
425 
426 	(void) gen_reset_eid(osf1_wcd);
427 	return ((bool_t) status == MMSYSERR_NOERROR);
428 #endif	/* HAS_MME */
429 }
430 
431 
432 /*
433  * osf1aud_config
434  *	Sets up audio device for playing CD quality audio.
435  *
436  * Args:
437  *	None.
438  *
439  * Return:
440  *	FALSE - failure
441  *	TRUE  - success
442  */
443 STATIC bool_t
osf1aud_config(void)444 osf1aud_config(void)
445 {
446 #ifndef HAS_MME
447 	(void) sprintf(osf1_wcd->i->msgbuf,
448 		    "osf1aud_config: MMS audio support not compiled-in");
449 	DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
450 	return FALSE;
451 #else
452 	MMRESULT	status;
453 	LPPCMWAVEFORMAT	wp;
454 
455 	wp = (LPPCMWAVEFORMAT) mmeAllocMem(sizeof(PCMWAVEFORMAT));
456 	if (wp == NULL) {
457 		(void) sprintf(osf1_wcd->i->msgbuf,
458 			    "osf1aud_config: Out of memory");
459 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
460 		return FALSE;
461 	}
462 
463 	wp->wf.nSamplesPerSec = 44100;
464 	wp->wf.nChannels = 2;
465 	wp->wBitsPerSample = 16;
466 	wp->wf.wFormatTag = WAVE_FORMAT_PCM;
467 
468 	status = waveOutOpen(
469 		&osf1_devhandle,
470 		osf1_devid,
471 		(LPWAVEFORMAT) wp,
472 		osf1aud_wave_cb,
473 		0,
474 		WAVE_ALLOWSYNC | CALLBACK_FUNCTION
475 	);
476 	if (status != MMSYSERR_NOERROR) {
477 		(void) sprintf(osf1_wcd->i->msgbuf,
478 			    "osf1aud_config: waveOutOpen failed (status=%d)",
479 			    status);
480 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
481 		return FALSE;
482 	}
483 
484 	mmeFreeMem(wp);
485 
486 	osf1_wavehdrp = (LPWAVEHDR) mmeAllocMem(sizeof(WAVEHDR));
487 	if (osf1_wavehdrp == NULL) {
488 		(void) waveOutClose(osf1_devhandle);
489 		osf1_devhandle = 0;
490 		(void) sprintf(osf1_wcd->i->msgbuf,
491 			    "osf1aud_config: Out of memory");
492 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
493 		return FALSE;
494 	}
495 
496 	osf1_wavehdrp->lpData = (LPSTR)
497 				mmeAllocMem(osf1_wcd->cds->chunk_bytes);
498 	if (osf1_wavehdrp->lpData == NULL) {
499 		(void) waveOutClose(osf1_devhandle);
500 		osf1_devhandle = 0;
501 		mmeFreeMem(osf1_wavehdrp);
502 		osf1_wavehdrp = NULL;
503 		(void) sprintf(osf1_wcd->i->msgbuf,
504 			    "osf1aud_config: Out of memory");
505 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
506 		return FALSE;
507 	}
508 
509 	/* Retrieve current volume settings */
510 	osf1_curr_vol0 = osf1_curr_vol1 = -1;
511 	osf1aud_gethw_vol();
512 
513 	DBGPRN(DBG_GEN|DBG_SND)(errfp, "\nOpened [audio:%u]\n", osf1_devid);
514 
515 	return TRUE;
516 #endif	/* HAS_MME */
517 }
518 
519 
520 /*
521  * osf1aud_write_chunk
522  *      Write a chunk of audio data to the audio device.  This function
523  *      assumes 16-bit, 2-channel interleaved frames.
524  *
525  * Args:
526  *      data - Pointer to the data buffer
527  *      len - Number of bytes to write
528  *
529  * Return:
530  *      FALSE - failure
531  *      TRUE  - success
532  */
533 STATIC bool_t
osf1aud_write_chunk(sword16_t * data,size_t len)534 osf1aud_write_chunk(sword16_t *data, size_t len)
535 {
536 #ifndef HAS_MME
537 	(void) sprintf(osf1_wcd->i->msgbuf,
538 		    "osf1aud_write_chunk: MMS audio support not compiled-in");
539 	DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
540 	return FALSE;
541 #else
542 	MMRESULT	status;
543 
544 	osf1_wavehdrp->dwBufferLength = len;
545 	(void) memcpy(osf1_wavehdrp->lpData, (byte_t *) data, len);
546 
547 	DBGPRN(DBG_SND)(errfp, "\nWrite [audio:%u]: %d bytes\n",
548 			osf1_devid, len);
549 
550 	status = waveOutWrite(osf1_devhandle, osf1_wavehdrp, sizeof(WAVEHDR));
551 	if (status != MMSYSERR_NOERROR) {
552 		(void) sprintf(osf1_wcd->i->msgbuf,
553 			    "osf1aud_write_chunk: "
554 			    "waveOutWrite failed (status=%d)", status);
555 		DBGPRN(DBG_SND)(errfp, "%s\n", osf1_wcd->i->msgbuf);
556 		return FALSE;
557 	}
558 	mmeWaitForCallbacks();
559 	mmeProcessCallbacks();
560 
561 	return TRUE;
562 #endif	/* HAS_MME */
563 }
564 
565 
566 /*
567  * osf1aud_update
568  *	Updates hardware volume and balance settings from GUI settings.
569  *
570  * Args:
571  *	None.
572  *
573  * Return:
574  *	Nothing.
575  */
osf1aud_update(void)576 osf1aud_update(void)
577 {
578 #ifdef HAS_MME
579 	int		val,
580 			vol0,
581 			vol1;
582 	static int	curr_vol0 = -1,
583 			curr_vol1 = -1;
584 
585 	app_data.vol_taper = osf1_wcd->i->vol_taper;
586 
587 	/* Update volume and balance */
588 	if (osf1_wcd->i->vol_left == osf1_wcd->i->vol_right) {
589 		vol0 = util_taper_vol(osf1_wcd->i->vol);
590 		vol1 = vol0;
591 	}
592 	else if (osf1_wcd->i->vol_left > osf1_wcd->i->vol_right) {
593 		val = (osf1_wcd->i->vol * osf1_wcd->i->vol_right) / 100;
594 		if (((osf1_wcd->i->vol * osf1_wcd->i->vol_right) % 100) >= 50)
595 			val++;
596 		vol0 = util_taper_vol(osf1_wcd->i->vol);
597 		vol1 = util_taper_vol(val);
598 	}
599 	else {
600 		val = (osf1_wcd->i->vol * osf1_wcd->i->vol_left) / 100;
601 		if (((osf1_wcd->i->vol * osf1_wcd->i->vol_left) % 100) >= 50)
602 			val++;
603 		vol0 = util_taper_vol(val);
604 		vol1 = util_taper_vol(osf1_wcd->i->vol);
605 	}
606 
607 	if (vol0 == curr_vol0 && vol1 == curr_vol1)
608 		/* No change */
609 		return;
610 
611 	curr_vol0 = vol0;
612 	curr_vol1 = vol1;
613 
614 	/* Apply new settings */
615 	osf1aud_sethw_vol(vol0, vol1);
616 #endif	/* HAS_MME */
617 }
618 
619 
620 /*
621  * osf1aud_status
622  *	Updates volume and balance settings from audio device.
623  *
624  * Args:
625  *	None.
626  *
627  * Return:
628  *	Nothing.
629  */
630 STATIC void
osf1aud_status(void)631 osf1aud_status(void)
632 {
633 #ifdef HAS_MME
634 	/* Retrieve current volume settings */
635 	osf1aud_gethw_vol();
636 #endif	/* HAS_MME */
637 }
638 
639 
640 /*
641  * osf1aud_write
642  *	Function responsible for writing from the buffer to the audio
643  *	device (and file if saving).
644  *
645  * Args:
646  *	s - Pointer to the curstat_t structure
647  *
648  * Return:
649  *	FALSE - failure
650  *	TRUE  - success
651  */
652 STATIC bool_t
osf1aud_write(curstat_t * s)653 osf1aud_write(curstat_t *s)
654 {
655 	byte_t	*in_data,
656 		*sw_data,
657 		*le_data,
658 		*be_data,
659 		*dev_data,
660 		*file_data,
661 		*pipe_data;
662 	char	*fname = NULL;
663 	int	trk_idx = -1,
664 		hbcnt = 1;
665 	time_t	start_time,
666 		tot_time;
667 	bool_t	ret;
668 
669 	/* Get memory for audio device write buffer */
670 	le_data = (byte_t *) MEM_ALLOC("le_data", osf1_wcd->cds->chunk_bytes);
671 	if (le_data == NULL) {
672 		(void) sprintf(osf1_wcd->i->msgbuf,
673 				"osf1aud_write: out of memory");
674 		DBGPRN(DBG_GEN)(errfp, "%s\n", osf1_wcd->i->msgbuf);
675 		return FALSE;
676 	}
677 
678 	/* Get memory for audio file write buffer */
679 	be_data = (byte_t *) MEM_ALLOC("be_data", osf1_wcd->cds->chunk_bytes);
680 	if (be_data == NULL) {
681 		MEM_FREE(le_data);
682 		(void) sprintf(osf1_wcd->i->msgbuf,
683 				"osf1aud_write: out of memory");
684 		DBGPRN(DBG_GEN)(errfp, "%s\n", osf1_wcd->i->msgbuf);
685 		return FALSE;
686 	}
687 
688 	(void) memset(le_data, 0, osf1_wcd->cds->chunk_bytes);
689 	(void) memset(be_data, 0, osf1_wcd->cds->chunk_bytes);
690 
691 	in_data = app_data.cdda_bigendian ? be_data : le_data;
692 	sw_data = app_data.cdda_bigendian ? le_data : be_data;
693 
694 	/* Start time */
695 	start_time = time(NULL);
696 
697 	/* While not stopped */
698 	ret = TRUE;
699 	while (osf1_wcd->i->state != CDSTAT_COMPLETED) {
700 		/* Get lock */
701 		cdda_waitsem(osf1_wsemid, LOCK);
702 
703 		/* End if finished and nothing in the buffer */
704 		if (osf1_wcd->cdb->occupied == 0 && osf1_wcd->i->cdda_done) {
705 			osf1_wcd->i->state = CDSTAT_COMPLETED;
706 			cdda_postsem(osf1_wsemid, LOCK);
707 			break;
708 		}
709 
710 		/* Wait until there is something there */
711 		while (osf1_wcd->cdb->occupied <= 0 &&
712 		       osf1_wcd->i->state != CDSTAT_COMPLETED) {
713 			cdda_postsem(osf1_wsemid, LOCK);
714 			cdda_waitsem(osf1_wsemid, DATA);
715 			cdda_waitsem(osf1_wsemid, LOCK);
716 		}
717 
718 		/* Break if stopped */
719 		if (osf1_wcd->i->state == CDSTAT_COMPLETED) {
720 			cdda_postsem(osf1_wsemid, LOCK);
721 			break;
722 		}
723 
724 		/* Set the current track and track length info */
725 		cdda_wr_setcurtrk(osf1_wcd);
726 
727 		/* Copy a chunk from the nextout slot into the data buffers */
728 		(void) memcpy(
729 			in_data,
730 			&osf1_wcd->cdb->b[
731 			    osf1_wcd->cds->chunk_bytes * osf1_wcd->cdb->nextout
732 			],
733 			osf1_wcd->cds->chunk_bytes
734 		);
735 
736 		/* Update pointers */
737 		osf1_wcd->cdb->nextout++;
738 		osf1_wcd->cdb->nextout %= osf1_wcd->cds->buffer_chunks;
739 		osf1_wcd->cdb->occupied--;
740 
741 		/* Set heartbeat and interval, update stats */
742 		if (--hbcnt == 0) {
743 			cdda_heartbeat(&osf1_wcd->i->writer_hb,CDDA_HB_WRITER);
744 
745 			tot_time = time(NULL) - start_time;
746 			if (tot_time > 0 && osf1_wcd->i->frm_played > 0) {
747 				osf1_wcd->i->frm_per_sec = (int)
748 					((float) osf1_wcd->i->frm_played /
749 					 (float) tot_time) + 0.5;
750 			}
751 
752 			hbcnt = cdda_heartbeat_interval(
753 				osf1_wcd->i->frm_per_sec
754 			);
755 		}
756 
757 		/* Signal room available */
758 		cdda_postsem(osf1_wsemid, ROOM);
759 
760 		/* Release lock */
761 		cdda_postsem(osf1_wsemid, LOCK);
762 
763 		/* Change channel routing and attenuation if necessary */
764 		gen_chroute_att(osf1_wcd->i->chroute,
765 				osf1_wcd->i->att,
766 				(sword16_t *)(void *) in_data,
767 				(size_t) osf1_wcd->cds->chunk_bytes);
768 
769 		/* Prepare byte-swapped version of the data */
770 		gen_byteswap(in_data, sw_data,
771 			     (size_t) osf1_wcd->cds->chunk_bytes);
772 
773 		/* Write to device */
774 		if (osf1_write_dev) {
775 			/* Always send little endian data */
776 			dev_data = le_data;
777 
778 			if (!osf1aud_write_chunk((sword16_t *) dev_data,
779 					osf1_wcd->cds->chunk_bytes)) {
780 				osf1_wcd->i->state = CDSTAT_COMPLETED;
781 				cdda_postsem(osf1_wsemid, LOCK);
782 				ret = FALSE;
783 				break;
784 			}
785 
786 			/* Update volume and balance settings */
787 			osf1aud_update();
788 
789 			if ((osf1_wcd->i->frm_played % FRAME_PER_SEC) == 0)
790 				osf1aud_status();
791 		}
792 
793 		/* Write to file */
794 		if (osf1_write_file) {
795 			file_data = osf1_file_be ? be_data : le_data;
796 
797 			/* Open new track file if necessary */
798 			if (!app_data.cdda_trkfile) {
799 				fname = s->trkinfo[0].outfile;
800 			}
801 			else if (trk_idx != osf1_wcd->i->trk_idx) {
802 				trk_idx = osf1_wcd->i->trk_idx;
803 
804 				if (osf1_file_desc != NULL &&
805 				    !gen_close_file(osf1_file_desc)) {
806 					osf1_wcd->i->state = CDSTAT_COMPLETED;
807 					cdda_postsem(osf1_wsemid, LOCK);
808 					ret = FALSE;
809 					break;
810 				}
811 
812 				fname = s->trkinfo[trk_idx].outfile;
813 				osf1_file_desc = gen_open_file(
814 					osf1_wcd,
815 					fname,
816 					O_WRONLY | O_TRUNC | O_CREAT,
817 					(mode_t) osf1_file_mode,
818 					app_data.cdda_filefmt,
819 					osf1_wcd->i->trk_len
820 				);
821 				if (osf1_file_desc == NULL) {
822 					osf1_wcd->i->state = CDSTAT_COMPLETED;
823 					cdda_postsem(osf1_wsemid, LOCK);
824 					ret = FALSE;
825 					break;
826 				}
827 			}
828 
829 			if (!gen_write_chunk(osf1_file_desc, file_data,
830 					(size_t) osf1_wcd->cds->chunk_bytes)) {
831 				osf1_wcd->i->state = CDSTAT_COMPLETED;
832 				cdda_postsem(osf1_wsemid, LOCK);
833 				ret = FALSE;
834 				break;
835 			}
836 		}
837 
838 		/* Pipe to program */
839 		if (osf1_write_pipe) {
840 			pipe_data = osf1_file_be ? be_data : le_data;
841 
842 			if (!gen_write_chunk(osf1_pipe_desc, pipe_data,
843 					(size_t) osf1_wcd->cds->chunk_bytes)) {
844 				osf1_wcd->i->state = CDSTAT_COMPLETED;
845 				cdda_postsem(osf1_wsemid, LOCK);
846 				ret = FALSE;
847 				break;
848 			}
849 		}
850 
851 		osf1_wcd->i->frm_played += osf1_wcd->cds->chunk_blocks;
852 
853 		while (osf1_wcd->i->state == CDSTAT_PAUSED)
854 			util_delayms(400);
855 
856 		/* Update debug level */
857 		app_data.debug = osf1_wcd->i->debug;
858 	}
859 
860 	/* Wake up reader */
861 	cdda_postsem(osf1_wsemid, ROOM);
862 
863 	/* Free memory */
864 	MEM_FREE(le_data);
865 	MEM_FREE(be_data);
866 
867 	return (ret);
868 }
869 
870 
871 /*
872  * osf1_winit
873  *	Pre-playback support check function
874  *
875  * Args:
876  *	None.
877  *
878  * Return:
879  *	Bitmask of supported features
880  */
881 word32_t
osf1_winit(void)882 osf1_winit(void)
883 {
884 #ifdef HAS_MME
885 	return (CDDA_WRITEDEV | CDDA_WRITEFILE | CDDA_WRITEPIPE);
886 #else
887 	return (CDDA_WRITEFILE | CDDA_WRITEPIPE);
888 #endif
889 }
890 
891 
892 /*
893  * osf1_write
894  *	Opens audio device/file, attaches shared memory and semaphores.
895  *	Continuously reads data from shared memory and writes it
896  *	to the audio device/file.
897  *
898  * Args:
899  *	s - Pointer to the curstat_t structure
900  *
901  * Return:
902  *	FALSE - failure
903  *	TRUE  - success
904  */
905 bool_t
osf1_write(curstat_t * s)906 osf1_write(curstat_t *s)
907 {
908 	size_t	estlen;
909 
910 	if (!PLAYMODE_IS_CDDA(app_data.play_mode)) {
911 		(void) fprintf(errfp, "osf1_write: Nothing to do.\n");
912 		return FALSE;
913 	}
914 
915 #ifdef HAS_MME
916 	osf1_write_dev  = (bool_t) ((app_data.play_mode & PLAYMODE_CDDA) != 0);
917 #else
918 	osf1_write_dev  = FALSE;
919 #endif
920 	osf1_write_file = (bool_t) ((app_data.play_mode & PLAYMODE_FILE) != 0);
921 	osf1_write_pipe = (bool_t) ((app_data.play_mode & PLAYMODE_PIPE) != 0);
922 
923 	/* Generic services initialization */
924 	gen_write_init();
925 
926 	/* Allocate memory */
927 	osf1_wcd = (cd_state_t *) MEM_ALLOC("cd_state_t", sizeof(cd_state_t));
928 	if (osf1_wcd == NULL) {
929 		(void) fprintf(errfp, "osf1_write: out of memory\n");
930 		return FALSE;
931 	}
932 	(void) memset(osf1_wcd, 0, sizeof(cd_state_t));
933 
934 	/* Initialize shared memory and semaphores */
935 	if ((osf1_wsemid = cdda_initipc(osf1_wcd)) < 0)
936 		return FALSE;
937 
938 	(void) sscanf(app_data.cdinfo_filemode, "%o", &osf1_file_mode);
939 	/* Make sure file is at least accessible by user */
940 	osf1_file_mode |= S_IRUSR | S_IWUSR;
941 	osf1_file_mode &= ~(S_ISUID | S_ISGID | S_IWGRP | S_IWOTH);
942 
943 	estlen = (osf1_wcd->i->end_lba - osf1_wcd->i->start_lba + 1) *
944 		 CDDA_BLKSZ;
945 
946 	/* Set heartbeat */
947 	cdda_heartbeat(&osf1_wcd->i->writer_hb, CDDA_HB_WRITER);
948 
949 	if (osf1_write_file || osf1_write_pipe) {
950 		/* Open file and/or pipe */
951 		if (!app_data.cdda_trkfile && osf1_write_file) {
952 			osf1_file_desc = gen_open_file(
953 				osf1_wcd,
954 				s->trkinfo[0].outfile,
955 				O_WRONLY | O_TRUNC | O_CREAT,
956 				(mode_t) osf1_file_mode,
957 				app_data.cdda_filefmt,
958 				estlen
959 			);
960 			if (osf1_file_desc == NULL)
961 				return FALSE;
962 		}
963 
964 		if (osf1_write_pipe) {
965 			osf1_pipe_desc = gen_open_pipe(
966 				osf1_wcd,
967 				app_data.pipeprog,
968 				&osf1_pipe_pid,
969 				app_data.cdda_filefmt,
970 				estlen
971 			);
972 			if (osf1_pipe_desc == NULL)
973 				return FALSE;
974 
975 			DBGPRN(DBG_SND)(errfp,
976 					  "\nosf1aud_write: Pipe to [%s]: "
977 					  "chunk_blks=%d\n",
978 					  app_data.pipeprog,
979 					  osf1_wcd->cds->chunk_blocks);
980 		}
981 
982 		/* Set endian */
983 		osf1_file_be = gen_filefmt_be(app_data.cdda_filefmt);
984 	}
985 
986 #ifdef HAS_MME
987 	if (osf1_write_dev) {
988 		/* Open audio device */
989 		if (!osf1aud_open_dev())
990 			return FALSE;
991 
992 		/* Configure audio */
993 		if (!osf1aud_config())
994 			return FALSE;
995 	}
996 #endif
997 
998 	/* Do the writing */
999 	if (!osf1aud_write(s))
1000 		return FALSE;
1001 
1002 	return TRUE;
1003 }
1004 
1005 
1006 /*
1007  * osf1_wdone
1008  *	Post-playback cleanup function
1009  *
1010  * Args:
1011  *	killreader - whether to terminate the reader thread or process
1012  *
1013  * Return:
1014  *	Nothing.
1015  */
1016 void
osf1_wdone(bool_t killreader)1017 osf1_wdone(bool_t killreader)
1018 {
1019 	DBGPRN(DBG_GEN)(errfp, "\nosf1_wdone: Cleaning up writer\n");
1020 
1021 #ifdef HAS_MME
1022 	if (osf1_wavehdrp != NULL)
1023 		(void) osf1aud_close_dev();
1024 #endif
1025 
1026 	if (osf1_file_desc != NULL) {
1027 		(void) gen_close_file(osf1_file_desc);
1028 		osf1_file_desc = NULL;
1029 	}
1030 
1031 	if (osf1_pipe_desc != NULL) {
1032 		(void) gen_close_pipe(osf1_pipe_desc);
1033 		osf1_pipe_desc = NULL;
1034 	}
1035 
1036 	cdda_yield();
1037 
1038 	if (osf1_pipe_pid > 0) {
1039 		(void) kill(osf1_pipe_pid, SIGTERM);
1040 		osf1_pipe_pid = -1;
1041 	}
1042 
1043 	if (osf1_wcd != NULL) {
1044 		if (osf1_wcd->i != NULL) {
1045 			if (killreader && osf1_wcd->i->reader != (thid_t) 0) {
1046 				cdda_kill(osf1_wcd->i->reader, SIGTERM);
1047 				osf1_wcd->i->state = CDSTAT_COMPLETED;
1048 			}
1049 
1050 			/* Reset frames played counter */
1051 			osf1_wcd->i->frm_played = 0;
1052 		}
1053 
1054 		MEM_FREE(osf1_wcd);
1055 		osf1_wcd = NULL;
1056 	}
1057 
1058 	osf1_write_dev = osf1_write_file = osf1_write_pipe = FALSE;
1059 }
1060 
1061 
1062 /*
1063  * osf1_winfo
1064  *	Append method identification to informational string.
1065  *
1066  * Args:
1067  *	str - The informational string
1068  *
1069  * Return:
1070  *	Nothing.
1071  */
1072 void
osf1_winfo(char * str)1073 osf1_winfo(char *str)
1074 {
1075 	(void) strcat(str, "Tru64 UNIX (OSF1) MMS audio driver\n");
1076 }
1077 
1078 #endif	/* CDDA_WR_OSF1 CDDA_SUPPORTED */
1079 
1080