home *** CD-ROM | disk | FTP | other *** search
- /* hpux_obuffer.cc
-
- Obuffer implementation for HP-UX
-
- Bugfixes in adaptation for maplay 1.2+ by :
- Earle F. Philhower, III (earle@geocities.com)
-
- There is another implementation by John Fehr called
- hpux_obuffer.cc.old that uses the audio server. */
-
- /*
- * @(#) obuffer_hp.cc 1.4, last edit: 02 Mar 1995 18:37:33
- * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
- * @(#) Berlin University of Technology
- *
- * Many thanks to:
- * -> John Brezak (brezak@apollo.hp.com)
- * for his first HP implementation using the audio server
- * (the current version does not use the audio server to keep
- * ethernets on low traffic for playing DOOM :-)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /*
- * New in maplay 1.3
- */
-
- #ifdef HPUX
-
- #include <stdio.h>
- #include <iostream.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <sys/time.h>
- extern "C" {
- #include <sys/audio.h>
- }
-
- #include "all.h"
- #include "header.h"
- #include "obuffer.h"
- #include "args.h"
-
- // statics of class HpuxObuffer:
- uint32 HpuxObuffer::ringbuffersize;
- int16 *HpuxObuffer::buffer;
- char *HpuxObuffer::head;
- int16 *HpuxObuffer::tail[2];
- uint32 HpuxObuffer::channels;
- BOOL HpuxObuffer::handler_activated;
- BOOL HpuxObuffer::buffer_empty;
- itimerval HpuxObuffer::timerval;
- BOOL HpuxObuffer::drain_buffer;
- int HpuxObuffer::audio_fd = -1;
-
-
- HpuxObuffer::HpuxObuffer (uint32 number_of_channels, MPEG_Args *maplay_args)
- {
- #ifdef DEBUG
- if (!number_of_channels || number_of_channels > MAXCHANNELS)
- {
- cerr << "HpuxObuffer: 0 < number of channels < " << MAXCHANNELS << "!\n";
- exit (1);
- }
- #endif
- if (audio_fd < 0)
- {
- cerr << "Internal error: HpuxObuffer::audio has to be initialized\n"
- << "by HpuxObuffer::class_suitable()!\n";
- exit (1);
- }
-
- // configure the audio device:
- if (ioctl (audio_fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT))
- {
- perror ("ioctl AUDIO_SET_DATA_FORMAT on /dev/audio");
- exit (1);
- }
- if (ioctl (audio_fd, AUDIO_SET_SAMPLE_RATE,
- maplay_args->MPEGheader->frequency ()))
- {
- perror ("ioctl AUDIO_SET_SAMPLE_RATE on /dev/audio");
- exit (1);
- }
- if (ioctl (audio_fd, AUDIO_SET_CHANNELS, number_of_channels))
- {
- perror ("ioctl AUDIO_SET_CHANNELS on /dev/audio");
- exit (1);
- }
- int output_channel = 0;
- if (maplay_args->use_speaker)
- output_channel |= AUDIO_OUT_SPEAKER;
- if (maplay_args->use_headphone)
- output_channel |= AUDIO_OUT_HEADPHONE;
- if (maplay_args->use_line_out)
- output_channel |= AUDIO_OUT_LINE;
- if (ioctl (audio_fd, AUDIO_SET_OUTPUT, output_channel))
- {
- perror ("ioctl AUDIO_SET_OUTPUT on /dev/audio");
- exit (1);
- }
-
- if (maplay_args->volume >= 0.0 && maplay_args->volume <= 1.0)
- {
- // set volume:
- audio_describe description;
- audio_gains gains;
- if (ioctl (audio_fd, AUDIO_DESCRIBE, &description))
- {
- perror ("ioctl AUDIO_DESCRIBE on /dev/audio");
- exit (1);
- }
- if (ioctl (audio_fd, AUDIO_GET_GAINS, &gains))
- {
- perror ("ioctl AUDIO_GET_GAINS on /dev/audio");
- exit (1);
- }
- gains.transmit_gain = (int)((float)description.min_transmit_gain +
- (float)(description.max_transmit_gain
- - description.min_transmit_gain) * maplay_args->volume);
- if (ioctl (audio_fd, AUDIO_SET_GAINS, &gains))
- {
- perror ("ioctl AUDIO_GET_GAINS on /dev/audio");
- exit (1);
- }
- }
-
- // set device internal buffer size:
- if (ioctl (audio_fd, AUDIO_SET_TXBUFSIZE, TXBUFSIZE))
- {
- perror ("ioctl AUDIO_SET_TXBUFSIZE on /dev/audio");
- exit (1);
- }
-
- // initialize ringbuffer:
- channels = number_of_channels;
- if (number_of_channels > 1)
- ringbuffersize = 512 * 1024; // 512 KB ringbuffer
- else
- ringbuffersize = 256 * 1024; // 256 KB ringbuffer
- buffer = (int16 *)malloc (ringbuffersize);
- head = (char *)buffer;
- tail[0] = buffer;
- tail[1] = buffer + 1;
- buffer_empty = TRUE;
- drain_buffer = FALSE;
-
- // prepare for SIGALRM timeouts:
- timerval.it_interval.tv_sec = 0;
- timerval.it_value.tv_sec = 0;
- if (number_of_channels == 1)
- {
- // call signal_handler() three times a second:
- timerval.it_interval.tv_usec = 1000000 / 3;
- timerval.it_value.tv_usec = 1000000 / 3;
- }
- else
- {
- // call signal_handler() six times a second:
- timerval.it_interval.tv_usec = 1000000 / 6;
- timerval.it_value.tv_usec = 1000000 / 6;
- }
- sigvec my_sigvec;
- my_sigvec.sv_handler = (void (*)(int))signal_handler;
- my_sigvec.sv_mask = 0;
- my_sigvec.sv_flags = 0;
- if (sigvector (SIGALRM, &my_sigvec, NULL))
- {
- perror ("signal (SIGARLM, signal_handler)");
- exit (1);
- }
- handler_activated = FALSE;
- }
-
-
- HpuxObuffer::~HpuxObuffer (void)
- {
- // disable timer:
- static itimerval no_timer = { { 0, 0 }, { 0, 0 } };
- setitimer (ITIMER_REAL, &no_timer, NULL);
-
- // wait until ringbuffer is empty:
- drain_buffer = TRUE;
- while (!buffer_empty)
- signal_handler ();
-
- if (ioctl (audio_fd, AUDIO_DRAIN, NULL))
- {
- perror ("ioctl AUDIO_DRAIN on /dev/audio");
- exit (1);
- }
- close (audio_fd);
- delete buffer;
- }
-
-
- void HpuxObuffer::append (uint32 channel, int16 value)
- /* Remark:
- Because signal_handler() can interrupt the normal program execution at
- (nearly) any time in any statement and synchronization mechanisms like
- sigblock() create too much overload, some values are precomputed first
- and assigned to the target variable later. */
- {
- #ifdef DEBUG
- if (channel >= channels)
- {
- cerr << "illegal channelnumber in HpuxObuffer::append()!\n";
- exit (1);
- }
- #endif
- *tail[channel] = value;
- if ((char *)(tail[channel] + channels) >= (char *)buffer + ringbuffersize)
- {
- register int16 *tmp = buffer + channel; // fold into ringbuffer
- tail[channel] = tmp;
- }
- else
- tail[channel] += channels;
- buffer_empty = FALSE;
- if ((char *)*tail == head)
- {
- // tail bites into head (buffer is full)
- if (!handler_activated)
- {
- // call handler by hand to (re)start playback:
- do
- signal_handler ();
- while (!buffer_empty && (char *)*tail == head);
-
- // activate timer:
- if (setitimer (ITIMER_REAL, &timerval, NULL))
- {
- perror ("setitimer");
- exit (1);
- }
- handler_activated = TRUE;
- }
- else
- {
- // wait for a SIGALRM signal:
- sigset_t sigset;
- sigfillset (&sigset);
- sigdelset (&sigset, SIGALRM);
- sigdelset (&sigset, SIGINT);
- sigdelset (&sigset, SIGQUIT);
- sigdelset (&sigset, SIGSTOP);
- do
- if (sigsuspend (&sigset) < 0 && errno != EINTR)
- {
- perror ("sigsuspend");
- exit (1);
- }
- while (!buffer_empty && (char *)*tail == head);
- }
- }
- }
-
-
- void HpuxObuffer::signal_handler (void)
- {
- extern BOOL verbose_mode;
- int length, length2, max_length, written_bytes;
- char *write_end;
- audio_status status;
-
- // determine size of empty room in device internal buffer:
- if (ioctl (audio_fd, AUDIO_GET_STATUS, &status))
- {
- perror ("ioctl AUDIO_GET_STATUS on /dev/audio");
- exit (1);
- }
- max_length = TXBUFSIZE - status.transmit_buffer_count;
- if (!max_length)
- return;
-
- if (buffer_empty)
- {
- pause:
- // disable timer:
- static itimerval no_timer = { { 0, 0 }, { 0, 0 } };
- setitimer (ITIMER_REAL, &no_timer, NULL);
- handler_activated = FALSE;
- if (verbose_mode)
- write (2, "pausing playback...\n", 20);
- return;
- }
-
- write_end = (char *)(tail[channels - 1] - (channels - 1));
- if (write_end == (char *)buffer)
- write_end = (char *)buffer + ringbuffersize;
-
- if (write_end > head)
- {
- length = write_end - head;
- if (!drain_buffer && length < 32 * 1024)
- goto pause;
- if (length > max_length)
- length = max_length;
- if ((written_bytes = write (audio_fd, head, length)) < 0)
- {
- perror ("write to /dev/audio");
- exit (1);
- }
-
- #ifdef AUDIO_DEBUG
- timeval actual_time;
- gettimeofday (&actual_time, NULL);
- char string[100];
- sprintf (string, "Written bytes (1): %6d at time %09d.%06d\n",
- written_bytes, actual_time.tv_sec, actual_time.tv_usec);
- write (2, string, strlen (string));
- #endif
-
- if ((head += written_bytes) == write_end)
- buffer_empty = TRUE;
- if (head == (char *)buffer + ringbuffersize)
- head = (char *)buffer;
- }
- else
- {
- length = (char *)buffer + ringbuffersize - head;
- length2 = write_end - (char *)buffer;
- if (!drain_buffer && length + length2 < 32 * 1024)
- goto pause;
- if (length + length2 > max_length)
- if (length > max_length)
- {
- length = max_length;
- length2 = 0;
- }
- else
- length2 = max_length - length;
- if ((written_bytes = write (audio_fd, head, length)) < 0)
- {
- perror ("write to /dev/audio");
- exit (1);
- }
-
- #ifdef AUDIO_DEBUG
- timeval actual_time;
- gettimeofday (&actual_time, NULL);
- char string[100];
- sprintf (string, "Written bytes (2): %6d at time %09d.%06d\n",
- written_bytes, actual_time.tv_sec, actual_time.tv_usec);
- write (2, string, strlen (string));
- #endif
-
- if (written_bytes == length && length2)
- {
- if ((written_bytes = write (audio_fd, (char *)buffer, length2)) < 0)
- {
- perror ("write to /dev/audio");
- exit (1);
- }
-
- #ifdef AUDIO_DEBUG
- timeval actual_time;
- gettimeofday (&actual_time, NULL);
- char string[100];
- sprintf (string, "Written bytes (2+): %6d at time %09d.%06d\n",
- written_bytes, actual_time.tv_sec, actual_time.tv_usec);
- write (2, string, strlen (string));
- #endif
-
- head = (char *)buffer + written_bytes;
- if (head == write_end)
- buffer_empty = TRUE;
- }
- else
- if ((head += written_bytes) == (char *)buffer + ringbuffersize)
- {
- if (head == write_end)
- buffer_empty = TRUE;
- head = (char *)buffer;
- }
- }
- }
-
-
- int HpuxObuffer::open_audio_device(MPEG_Args *maplay_args)
- {
- int fd;
-
- if ((fd = open ("/dev/audio", O_WRONLY | O_NDELAY, 0)) < 0)
- if (errno == EBUSY)
- {
- BOOL verbose_mode = maplay_args->verbose_mode;
- BOOL wait_if_busy = maplay_args->wait_if_busy;
-
- if (wait_if_busy)
- {
- if (verbose_mode)
- cerr << "Audio device is busy, waiting...\n";
- while (errno = 0, (fd = open ("/dev/audio", O_WRONLY, 0)) < 0 &&
- (errno == EBUSY || errno == EINTR))
- sleep (3);
- if (errno && errno != EBUSY)
- {
- perror ("Can't open /dev/audio for writing");
- exit (1);
- }
- if (verbose_mode)
- cerr << "Starting playback...\n";
- }
- else
- {
- cerr << "Sorry, the audio device is busy!\n";
- exit (1);
- }
- }
- else
- {
- perror ("Can't open /dev/audio for writing");
- exit (1);
- }
-
- // turn NDELAY mode on, because write() must never block maplay:
- int flags;
- if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
- {
- perror ("fcntl F_GETFL on /dev/audio failed");
- exit (1);
- }
- flags |= O_NDELAY;
- if (fcntl (fd, F_SETFL, flags) < 0)
- {
- perror ("fcntl F_SETFL on /dev/audio failed");
- exit (1);
- }
-
- return fd;
- }
-
-
- BOOL HpuxObuffer::class_suitable(MPEG_Args *maplay_args)
- {
- audio_fd = open_audio_device(maplay_args);
- return(TRUE);
- }
-
- Obuffer *create_obuffer(MPEG_Args *maplay_args)
- {
- Obuffer *buffer;
- enum e_mode mode = maplay_args->MPEGheader->mode();
- enum e_channels which_channels = maplay_args->which_c;
-
- if (HpuxObuffer::class_suitable(maplay_args))
- if (mode == single_channel || which_channels != both)
- buffer = new HpuxObuffer (1, maplay_args);
- else
- buffer = new HpuxObuffer (2, maplay_args);
- else
- return(NULL);
-
- return(buffer);
- }
-
- #endif // HPUX
-