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