home *** CD-ROM | disk | FTP | other *** search
/ Windows 95 Secrets / Secrets2.iso / Audio / WAV / Maplay4 / _SETUP.1 / hpux_obuffer.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-08  |  11.9 KB  |  473 lines

  1. /* hpux_obuffer.cc
  2.  
  3.    Obuffer implementation for HP-UX
  4.  
  5.    Bugfixes in adaptation for maplay 1.2+ by :
  6.    Earle F. Philhower, III (earle@geocities.com)
  7.  
  8.    There is another implementation by John Fehr called
  9.    hpux_obuffer.cc.old that uses the audio server. */
  10.  
  11. /*
  12.  *  @(#) obuffer_hp.cc 1.4, last edit: 02 Mar 1995 18:37:33
  13.  *  @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
  14.  *  @(#) Berlin University of Technology
  15.  *
  16.  *  Many thanks to:
  17.  *  -> John Brezak (brezak@apollo.hp.com)
  18.  *     for his first HP implementation using the audio server
  19.  *     (the current version does not use the audio server to keep
  20.  *      ethernets on low traffic for playing DOOM :-)
  21.  *
  22.  *  This program is free software; you can redistribute it and/or modify
  23.  *  it under the terms of the GNU General Public License as published by
  24.  *  the Free Software Foundation; either version 2 of the License, or
  25.  *  (at your option) any later version.
  26.  *
  27.  *  This program is distributed in the hope that it will be useful,
  28.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  29.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  30.  *  GNU General Public License for more details.
  31.  *
  32.  *  You should have received a copy of the GNU General Public License
  33.  *  along with this program; if not, write to the Free Software
  34.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  35.  */
  36.  
  37. /*
  38.  *  New in maplay 1.3
  39.  */
  40.  
  41. #ifdef HPUX
  42.  
  43. #include <stdio.h>
  44. #include <iostream.h>
  45. #include <unistd.h>
  46. #include <stdlib.h>
  47. #include <signal.h>
  48. #include <sys/time.h>
  49. extern "C" {
  50. #include <sys/audio.h>
  51. }
  52.  
  53. #include "all.h"
  54. #include "header.h"
  55. #include "obuffer.h"
  56. #include "args.h"
  57.  
  58. // statics of class HpuxObuffer:
  59. uint32        HpuxObuffer::ringbuffersize;
  60. int16           *HpuxObuffer::buffer;
  61. char           *HpuxObuffer::head;
  62. int16           *HpuxObuffer::tail[2];
  63. uint32        HpuxObuffer::channels;
  64. BOOL        HpuxObuffer::handler_activated;
  65. BOOL        HpuxObuffer::buffer_empty;
  66. itimerval    HpuxObuffer::timerval;
  67. BOOL        HpuxObuffer::drain_buffer;
  68. int        HpuxObuffer::audio_fd = -1;
  69.  
  70.  
  71. HpuxObuffer::HpuxObuffer (uint32 number_of_channels, MPEG_Args *maplay_args)
  72. {
  73. #ifdef DEBUG
  74.   if (!number_of_channels || number_of_channels > MAXCHANNELS)
  75.   {
  76.     cerr << "HpuxObuffer: 0 < number of channels < " << MAXCHANNELS << "!\n";
  77.     exit (1);
  78.   }
  79. #endif
  80.   if (audio_fd < 0)
  81.   {
  82.     cerr << "Internal error: HpuxObuffer::audio has to be initialized\n"
  83.           << "by HpuxObuffer::class_suitable()!\n";
  84.     exit (1);
  85.   }
  86.  
  87.   // configure the audio device:
  88.   if (ioctl (audio_fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT))
  89.   {
  90.     perror ("ioctl AUDIO_SET_DATA_FORMAT on /dev/audio");
  91.     exit (1);
  92.   }
  93.   if (ioctl (audio_fd, AUDIO_SET_SAMPLE_RATE,
  94.                   maplay_args->MPEGheader->frequency ()))
  95.   {
  96.     perror ("ioctl AUDIO_SET_SAMPLE_RATE on /dev/audio");
  97.     exit (1);
  98.   }
  99.   if (ioctl (audio_fd, AUDIO_SET_CHANNELS, number_of_channels))
  100.   {
  101.     perror ("ioctl AUDIO_SET_CHANNELS on /dev/audio");
  102.     exit (1);
  103.   }
  104.   int output_channel = 0;
  105.   if (maplay_args->use_speaker)
  106.     output_channel |= AUDIO_OUT_SPEAKER;
  107.   if (maplay_args->use_headphone)
  108.     output_channel |= AUDIO_OUT_HEADPHONE;
  109.   if (maplay_args->use_line_out)
  110.     output_channel |= AUDIO_OUT_LINE;
  111.   if (ioctl (audio_fd, AUDIO_SET_OUTPUT, output_channel))
  112.   {
  113.     perror ("ioctl AUDIO_SET_OUTPUT on /dev/audio");
  114.     exit (1);
  115.   }
  116.  
  117.   if (maplay_args->volume >= 0.0 && maplay_args->volume <= 1.0)
  118.   {
  119.     // set volume:
  120.     audio_describe description;
  121.     audio_gains gains;
  122.     if (ioctl (audio_fd, AUDIO_DESCRIBE, &description))
  123.     {
  124.       perror ("ioctl AUDIO_DESCRIBE on /dev/audio");
  125.       exit (1);
  126.     }
  127.     if (ioctl (audio_fd, AUDIO_GET_GAINS, &gains))
  128.     {
  129.       perror ("ioctl AUDIO_GET_GAINS on /dev/audio");
  130.       exit (1);
  131.     }
  132.     gains.transmit_gain = (int)((float)description.min_transmit_gain +
  133.                 (float)(description.max_transmit_gain
  134.                     - description.min_transmit_gain) * maplay_args->volume);
  135.     if (ioctl (audio_fd, AUDIO_SET_GAINS, &gains))
  136.     {
  137.       perror ("ioctl AUDIO_GET_GAINS on /dev/audio");
  138.       exit (1);
  139.     }
  140.   }
  141.  
  142.   // set device internal buffer size:
  143.   if (ioctl (audio_fd, AUDIO_SET_TXBUFSIZE, TXBUFSIZE))
  144.   {
  145.     perror ("ioctl AUDIO_SET_TXBUFSIZE on /dev/audio");
  146.     exit (1);
  147.   }
  148.  
  149.   // initialize ringbuffer:
  150.   channels = number_of_channels;
  151.   if (number_of_channels > 1)
  152.     ringbuffersize = 512 * 1024;    // 512 KB ringbuffer
  153.   else
  154.     ringbuffersize = 256 * 1024;    // 256 KB ringbuffer
  155.   buffer = (int16 *)malloc (ringbuffersize);
  156.   head = (char *)buffer;
  157.   tail[0] = buffer;
  158.   tail[1] = buffer + 1;
  159.   buffer_empty = TRUE;
  160.   drain_buffer = FALSE;
  161.  
  162.   // prepare for SIGALRM timeouts:
  163.   timerval.it_interval.tv_sec = 0;
  164.   timerval.it_value.tv_sec = 0;
  165.   if (number_of_channels == 1)
  166.   {
  167.     // call signal_handler() three times a second:
  168.     timerval.it_interval.tv_usec = 1000000 / 3;
  169.     timerval.it_value.tv_usec =    1000000 / 3;
  170.   }
  171.   else
  172.   {
  173.     // call signal_handler() six times a second:
  174.     timerval.it_interval.tv_usec = 1000000 / 6;
  175.     timerval.it_value.tv_usec =    1000000 / 6;
  176.   }
  177.   sigvec my_sigvec;
  178.   my_sigvec.sv_handler = (void (*)(int))signal_handler;
  179.   my_sigvec.sv_mask = 0;
  180.   my_sigvec.sv_flags = 0;
  181.   if (sigvector (SIGALRM, &my_sigvec, NULL))
  182.   {
  183.     perror ("signal (SIGARLM, signal_handler)");
  184.     exit (1);
  185.   }
  186.   handler_activated = FALSE;
  187. }
  188.  
  189.  
  190. HpuxObuffer::~HpuxObuffer (void)
  191. {
  192.   // disable timer:
  193.   static itimerval no_timer = { { 0, 0 }, { 0, 0 } };
  194.   setitimer (ITIMER_REAL, &no_timer, NULL);
  195.  
  196.   // wait until ringbuffer is empty:
  197.   drain_buffer = TRUE;
  198.   while (!buffer_empty)
  199.     signal_handler ();
  200.  
  201.   if (ioctl (audio_fd, AUDIO_DRAIN, NULL))
  202.   {
  203.     perror ("ioctl AUDIO_DRAIN on /dev/audio");
  204.     exit (1);
  205.   }
  206.   close (audio_fd);
  207.   delete buffer;
  208. }
  209.  
  210.  
  211. void HpuxObuffer::append (uint32 channel, int16 value)
  212. /* Remark:
  213.    Because signal_handler() can interrupt the normal program execution at
  214.    (nearly) any time in any statement and synchronization mechanisms like
  215.    sigblock() create too much overload, some values are precomputed first
  216.    and assigned to the target variable later. */
  217. {
  218. #ifdef DEBUG
  219.   if (channel >= channels)
  220.   {
  221.     cerr << "illegal channelnumber in HpuxObuffer::append()!\n";
  222.     exit (1);
  223.   }
  224. #endif
  225.   *tail[channel] = value;
  226.   if ((char *)(tail[channel] + channels) >= (char *)buffer + ringbuffersize)
  227.   {
  228.     register int16 *tmp = buffer + channel;        // fold into ringbuffer
  229.     tail[channel] = tmp;
  230.   }
  231.   else
  232.     tail[channel] += channels;
  233.   buffer_empty = FALSE;
  234.   if ((char *)*tail == head)
  235.   {
  236.     // tail bites into head (buffer is full)
  237.     if (!handler_activated)
  238.     {
  239.       // call handler by hand to (re)start playback:
  240.       do
  241.     signal_handler ();
  242.       while (!buffer_empty && (char *)*tail == head);
  243.  
  244.       // activate timer:
  245.       if (setitimer (ITIMER_REAL, &timerval, NULL))
  246.       {
  247.     perror ("setitimer");
  248.     exit (1);
  249.       }
  250.       handler_activated = TRUE;
  251.     }
  252.     else
  253.     {
  254.       // wait for a SIGALRM signal:
  255.       sigset_t sigset;
  256.       sigfillset (&sigset);
  257.       sigdelset (&sigset, SIGALRM);
  258.       sigdelset (&sigset, SIGINT);
  259.       sigdelset (&sigset, SIGQUIT);
  260.       sigdelset (&sigset, SIGSTOP);
  261.       do
  262.     if (sigsuspend (&sigset) < 0 && errno != EINTR)
  263.     {
  264.       perror ("sigsuspend");
  265.       exit (1);
  266.     }
  267.       while (!buffer_empty && (char *)*tail == head);
  268.     }
  269.   }
  270. }
  271.  
  272.  
  273. void HpuxObuffer::signal_handler (void)
  274. {
  275.   extern BOOL verbose_mode;
  276.   int length, length2, max_length, written_bytes;
  277.   char *write_end;
  278.   audio_status status;
  279.  
  280.   // determine size of empty room in device internal buffer:
  281.   if (ioctl (audio_fd, AUDIO_GET_STATUS, &status))
  282.   {
  283.     perror ("ioctl AUDIO_GET_STATUS on /dev/audio");
  284.     exit (1);
  285.   }
  286.   max_length = TXBUFSIZE - status.transmit_buffer_count;
  287.   if (!max_length)
  288.     return;
  289.  
  290.   if (buffer_empty)
  291.   {
  292. pause:
  293.     // disable timer:
  294.     static itimerval no_timer = { { 0, 0 }, { 0, 0 } };
  295.     setitimer (ITIMER_REAL, &no_timer, NULL);
  296.     handler_activated = FALSE;
  297.     if (verbose_mode)
  298.       write (2, "pausing playback...\n", 20);
  299.     return;
  300.   }
  301.  
  302.   write_end = (char *)(tail[channels - 1] - (channels - 1));
  303.   if (write_end == (char *)buffer)
  304.     write_end = (char *)buffer + ringbuffersize;
  305.  
  306.   if (write_end > head)
  307.   {
  308.     length = write_end - head;
  309.     if (!drain_buffer && length < 32 * 1024)
  310.       goto pause;
  311.     if (length > max_length)
  312.       length = max_length;
  313.     if ((written_bytes = write (audio_fd, head, length)) < 0)
  314.     {
  315.       perror ("write to /dev/audio");
  316.       exit (1);
  317.     }
  318.  
  319. #ifdef AUDIO_DEBUG
  320.     timeval actual_time;
  321.     gettimeofday (&actual_time, NULL);
  322.     char string[100];
  323.     sprintf (string, "Written bytes (1):  %6d at time %09d.%06d\n",
  324.          written_bytes, actual_time.tv_sec, actual_time.tv_usec);
  325.     write (2, string, strlen (string));
  326. #endif
  327.  
  328.     if ((head += written_bytes) == write_end)
  329.       buffer_empty = TRUE;
  330.     if (head == (char *)buffer + ringbuffersize)
  331.       head = (char *)buffer;
  332.   }
  333.   else
  334.   {
  335.     length  = (char *)buffer + ringbuffersize - head;
  336.     length2 = write_end - (char *)buffer;
  337.     if (!drain_buffer && length + length2 < 32 * 1024)
  338.       goto pause;
  339.     if (length + length2 > max_length)
  340.       if (length > max_length)
  341.       {
  342.     length = max_length;
  343.     length2 = 0;
  344.       }
  345.       else
  346.     length2 = max_length - length;
  347.     if ((written_bytes = write (audio_fd, head, length)) < 0)
  348.     {
  349.       perror ("write to /dev/audio");
  350.       exit (1);
  351.     }
  352.  
  353. #ifdef AUDIO_DEBUG
  354.     timeval actual_time;
  355.     gettimeofday (&actual_time, NULL);
  356.     char string[100];
  357.     sprintf (string, "Written bytes (2):  %6d at time %09d.%06d\n",
  358.          written_bytes, actual_time.tv_sec, actual_time.tv_usec);
  359.     write (2, string, strlen (string));
  360. #endif
  361.  
  362.     if (written_bytes == length && length2)
  363.     {
  364.       if ((written_bytes = write (audio_fd, (char *)buffer, length2)) < 0)
  365.       {
  366.     perror ("write to /dev/audio");
  367.     exit (1);
  368.       }
  369.  
  370. #ifdef AUDIO_DEBUG
  371.       timeval actual_time;
  372.       gettimeofday (&actual_time, NULL);
  373.       char string[100];
  374.       sprintf (string, "Written bytes (2+): %6d at time %09d.%06d\n",
  375.            written_bytes, actual_time.tv_sec, actual_time.tv_usec);
  376.       write (2, string, strlen (string));
  377. #endif
  378.  
  379.       head = (char *)buffer + written_bytes;
  380.       if (head == write_end)
  381.     buffer_empty = TRUE;
  382.     }
  383.     else
  384.       if ((head += written_bytes) == (char *)buffer + ringbuffersize)
  385.       {
  386.     if (head == write_end)
  387.       buffer_empty = TRUE;
  388.     head = (char *)buffer;
  389.       }
  390.   }
  391. }
  392.  
  393.  
  394. int HpuxObuffer::open_audio_device(MPEG_Args *maplay_args)
  395. {
  396.   int fd;
  397.  
  398.   if ((fd = open ("/dev/audio", O_WRONLY | O_NDELAY, 0)) < 0)
  399.     if (errno == EBUSY)
  400.     {
  401.       BOOL verbose_mode = maplay_args->verbose_mode;
  402.       BOOL wait_if_busy = maplay_args->wait_if_busy;
  403.  
  404.       if (wait_if_busy)
  405.       {
  406.     if (verbose_mode)
  407.       cerr << "Audio device is busy, waiting...\n";
  408.     while (errno = 0, (fd = open ("/dev/audio", O_WRONLY, 0)) < 0 &&
  409.            (errno == EBUSY || errno == EINTR))
  410.       sleep (3);
  411.     if (errno && errno != EBUSY)
  412.     {
  413.       perror ("Can't open /dev/audio for writing");
  414.       exit (1);
  415.     }
  416.     if (verbose_mode)
  417.       cerr << "Starting playback...\n";
  418.       }
  419.       else
  420.       {
  421.     cerr << "Sorry, the audio device is busy!\n";
  422.     exit (1);
  423.       }
  424.     }
  425.     else
  426.     {
  427.       perror ("Can't open /dev/audio for writing");
  428.       exit (1);
  429.     }
  430.  
  431.   // turn NDELAY mode on, because write() must never block maplay:
  432.   int flags;
  433.   if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
  434.   {
  435.     perror ("fcntl F_GETFL on /dev/audio failed");
  436.     exit (1);
  437.   }
  438.   flags |= O_NDELAY;
  439.   if (fcntl (fd, F_SETFL, flags) < 0)
  440.   {
  441.     perror ("fcntl F_SETFL on /dev/audio failed");
  442.     exit (1);
  443.   }
  444.  
  445.   return fd;
  446. }
  447.  
  448.  
  449. BOOL HpuxObuffer::class_suitable(MPEG_Args *maplay_args)
  450. {
  451.   audio_fd = open_audio_device(maplay_args);
  452.   return(TRUE);
  453. }
  454.  
  455. Obuffer *create_obuffer(MPEG_Args *maplay_args)
  456. {
  457.   Obuffer *buffer;
  458.   enum e_mode mode = maplay_args->MPEGheader->mode();
  459.   enum e_channels which_channels = maplay_args->which_c;
  460.  
  461.   if (HpuxObuffer::class_suitable(maplay_args))
  462.     if (mode == single_channel || which_channels != both)
  463.       buffer = new HpuxObuffer (1, maplay_args);
  464.     else
  465.       buffer = new HpuxObuffer (2, maplay_args);
  466.   else
  467.     return(NULL);
  468.  
  469.   return(buffer);
  470. }
  471.  
  472. #endif // HPUX
  473.