www.pudn.com > helix.src.0812.rar > audlinux_alsa.cpp


/* ***** BEGIN LICENSE BLOCK ***** 
 * Version: RCSL 1.0/RPSL 1.0  
 *   
 * Portions Copyright (c) 1995-2003 RealNetworks, Inc. All Rights Reserved.  
 *       
 * The contents of this file, and the files included with this file, are  
 * subject to the current version of the RealNetworks Public Source License  
 * Version 1.0 (the "RPSL") available at  
 * http://www.helixcommunity.org/content/rpsl unless you have licensed  
 * the file under the RealNetworks Community Source License Version 1.0  
 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,  
 * in which case the RCSL will apply. You may also obtain the license terms  
 * directly from RealNetworks.  You may not use this file except in  
 * compliance with the RPSL or, if you have a valid RCSL with RealNetworks  
 * applicable to this file, the RCSL.  Please see the applicable RPSL or  
 * RCSL for the rights, obligations and limitations governing use of the  
 * contents of the file.   
 *   
 * This file is part of the Helix DNA Technology. RealNetworks is the  
 * developer of the Original Code and owns the copyrights in the portions  
 * it created.  
 *   
 * This file, and the files included with this file, is distributed and made  
 * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER  
 * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,  
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS  
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
 *  
 * Technology Compatibility Kit Test Suite(s) Location: 
 *    http://www.helixcommunity.org/content/tck  
 *  
 * Contributor(s):  ljp  
 *   
 * ***** END LICENSE BLOCK ***** */  
 
#include  
#include  
#include  
#include  
#include  
 
#define ALSA_PCM_NEW_HW_PARAMS_API 
#define ALSA_PCM_NEW_SW_PARAMS_API 
 
#include  
 
#include   
#include  
 
#include "ihxpckts.h" 
#include "hxtick.h" 
#include "hxprefs.h" 
#include "timeval.h" 
#include "hxthread.h" 
#include "audlinux_alsa.h" 
#include "hxstrutl.h" 
 
#include "dllacces.h" 
#include "dllpath.h" 
 
//------------------------------------------ 
// Ctors and Dtors. 
//------------------------------------------ 
CAudioOutLinuxAlsa::CAudioOutLinuxAlsa() : 
  CAudioOutUNIX(), 
  m_ulTickCount(0), 
  m_ulLastBytesPlayed(0), 
  m_ulLastTimeStamp(0), 
  m_ulPausePosition(0), 
  m_bHasHardwarePause(FALSE), 
  m_bHasHardwareResume(FALSE), 
  pcm_handle(0), 
  mixer_handle(0) 
{ 
}; 
 
CAudioOutLinuxAlsa::~CAudioOutLinuxAlsa() 
{ 
#ifdef _DEBUG 
  printf("d\'tor\n"); 
#endif 
  //The mixer is opened independently of the audio device. Make sure 
  //it is closed. 
  _CloseMixer(); 
 
  snd_pcm_hw_params_free(hwparams); 
  //    snd_pcm_status_free(status); 
}; 
 
 
// These Device Specific methods must be implemented 
// by the platform specific sub-classes. 
INT16 CAudioOutLinuxAlsa::_Imp_GetAudioFd(void) 
{ 
  return (INT16) pcm_handle; 
} 
 
 
//Device specific methods to open/close the mixer and audio devices. 
HX_RESULT CAudioOutLinuxAlsa::_OpenAudio() 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  int err=0; 
     
  //Set the tick count to zero 
  m_ulTickCount       = 0; 
  m_ulLastTimeStamp   = 0; 
  m_ulLastBytesPlayed = 0; 
  m_ulPausePosition   = 0; 
 
  //Check the environmental variable to let user overide default device. 
  char *pszOverrideName = getenv( "AUDIO" ); 
  char szDevName[MAX_DEV_NAME]; 
 
  // Use defaults if no environment variable is set. 
  if ( pszOverrideName && strlen(pszOverrideName)>0 ) 
    { 
      SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME ); 
    } 
  else 
    { 
      SafeStrCpy( szDevName, "default", MAX_DEV_NAME ); 
      //        SafeStrCpy( szDevName, "hw:0,0", MAX_DEV_NAME ); 
      //        SafeStrCpy( szDevName, "plughw:0,0", MAX_DEV_NAME ); 
    } 
 
  // Open the audio device if it isn't already open 
  if ( pcm_handle <= 0 ) 
    { 
      if ( snd_pcm_open( &pcm_handle, szDevName, SND_PCM_STREAM_PLAYBACK /*stream*/, 0) < 0) { 
#ifdef _DEBUG 
        fprintf( stderr, "Failed to open audio device %s : %d  errno: %d\n", 
                 szDevName, pcm_handle, errno ); 
#endif 
        retCode = RA_AOE_BADOPEN; 
      } 
    } 
 
  if((err=snd_pcm_nonblock( pcm_handle, 1)) < 0) 
    { 
#ifdef _DEBUG 
      fprintf (stderr, "Cannot set nonblock (%s)\n", 
               snd_strerror (err)); 
#endif 
    } 
 
  //     if((err = snd_pcm_status_malloc( &status)) <0) 
  //       { 
  //           fprintf (stderr, "cannot allocate status parameter structure (%s)\n", 
  //                    snd_strerror (err)); 
  //       } 
 
  if((err = snd_pcm_hw_params_malloc( &hwparams)) < 0) 
    { 
#ifdef _DEBUG 
      fprintf (stderr, "HW parameters malloc failed %s\n", 
               snd_strerror (err)); 
#endif 
    } 
  
  if ((err = snd_pcm_hw_params_any( pcm_handle, hwparams)) < 0) { 
#ifdef _DEBUG 
    fprintf (stderr, "HW parameters init failed %s\n", 
             snd_strerror (err)); 
#endif 
  } 
 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
 
HX_RESULT CAudioOutLinuxAlsa::_CloseAudio() 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  if( pcm_handle > 0 ) 
    //        if (snd_pcm_state( pcm_handle) == SND_PCM_STATE_OPEN) 
    { 
      snd_pcm_close( pcm_handle); 
      pcm_handle = 0;//NO_FILE_DESCRIPTOR; 
#ifdef _DEBUG 
      printf("pcm_handle is now %d\n", pcm_handle); 
#endif 
    } 
  else 
    { 
      retCode = RA_AOE_DEVNOTOPEN; 
    } 
 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
 
HX_RESULT CAudioOutLinuxAlsa::_OpenMixer() 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  int result; 
     
  if(!m_bMixerPresent) 
    { 
      //Let user override default device with environ variable. 
      char *pszOverrideName = getenv( "MIXER" ); 
      char szDevCtlName[MAX_DEV_NAME]; 
 
      // could be "hw:0,0", or "plughw:0,0" or similiar 
      if (pszOverrideName && strlen(pszOverrideName) > 0 ) 
        { 
          SafeStrCpy( szDevCtlName , pszOverrideName, MAX_DEV_NAME ); 
        } 
      else 
        { 
          SafeStrCpy( szDevCtlName , "default", MAX_DEV_NAME );  
          // default for volume 
        } 
 
      if (( result = snd_mixer_open( &mixer_handle, 0)) < 0) 
        { 
#ifdef _DEBUG 
          printf( "Open audio device failed %d", result); 
#endif 
          retCode = RA_AOE_BADOPEN; 
        } 
   
      if (( result = snd_mixer_attach( mixer_handle, szDevCtlName )) < 0) { 
#ifdef _DEBUG 
        printf("Mixer attach %s failed: %s", szDevCtlName, snd_strerror( result)); 
#endif 
        snd_mixer_close( mixer_handle); 
        retCode = RA_AOE_NOTENABLED; 
      } 
   
      if (( result = snd_mixer_selem_register( mixer_handle, NULL, NULL)) < 0) { 
#ifdef _DEBUG 
        printf("Register mixer error: %s", snd_strerror( result)); 
#endif 
        snd_mixer_close( mixer_handle); 
        retCode = RA_AOE_NOTENABLED; 
      } 
 
      if((result = snd_mixer_load( mixer_handle)) < 0 ) 
        { 
          retCode = RA_AOE_NOTENABLED; 
        } 
 
      if (mixer_handle > 0) 
        { 
          m_bMixerPresent = 1; 
          _Imp_GetVolume(); 
        } 
      else 
        { 
          mixer_handle = 0;// NO_FILE_DESCRIPTOR; 
          m_bMixerPresent = 0; 
        } 
    } 
 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinuxAlsa::_CloseMixer() 
{ 
  if( mixer_handle > 0 ) 
    { 
      //Let user override default device with environ variable. 
      char *pszOverrideName = getenv( "MIXER" ); 
      char szDevCtlName[MAX_DEV_NAME]; 
 
      // could be "hw:0,0", or "plughw:0,0" or similiar 
      if (pszOverrideName && strlen(pszOverrideName) > 0 ) 
        { 
          SafeStrCpy( szDevCtlName , pszOverrideName, MAX_DEV_NAME ); 
        } 
      else 
        { 
          SafeStrCpy( szDevCtlName , "default", MAX_DEV_NAME );  
          // default for volume 
        } 
 
      if( snd_mixer_detach( mixer_handle, szDevCtlName) < 0) 
        { 
#ifdef _DEBUG 
          printf("Detach mixer failed\n"); 
#endif 
        } 
   
      if(snd_mixer_close ( mixer_handle) < 0) 
        { 
#ifdef _DEBUG 
          printf("Close mixer failed\n"); 
#endif 
          mixer_handle = 0;//NO_FILE_DESCRIPTOR; 
        } 
    } 
 
  return m_wLastError; 
} 
 
 
//Devic specific method to set the audio device characteristics. Sample rate, 
//bits-per-sample, etc. 
//Method *must* set member vars. m_unSampleRate and m_unNumChannels. 
HX_RESULT CAudioOutLinuxAlsa::_SetDeviceConfig( const HXAudioFormat* pFormat ) 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  int err=0; 
 
  if (snd_pcm_state( pcm_handle) != SND_PCM_STATE_OPEN) 
    return RA_AOE_DEVNOTOPEN; 
 
  if (snd_pcm_hw_params_any( pcm_handle, hwparams) < 0) { 
#ifdef _DEBUG 
    fprintf(stderr, "Configure device failed\n"); 
#endif 
    return  retCode = RA_AOE_NOTENABLED; 
  } 
 
  // SND_PCM_ACCESS_RW_INTERLEAVED or        
  // SND_PCM_ACCESS_RW_NONINTERLEAVED.       
  // There are also access types for MMAPed  
  if (snd_pcm_hw_params_set_access( pcm_handle, hwparams, 
                                    SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { 
#ifdef _DEBUG 
    fprintf(stderr, "Set access failed\n"); 
#endif 
    return  retCode = RA_AOE_NOTENABLED; 
  } 
 
  //  Now set the format. Either 8-bit or 16-bit audio is supported. 
  // alsa supports up to 64 bit 
  int      nSampleWidth  = pFormat->uBitsPerSample; 
  ULONG32  nSampleRate   = pFormat->ulSamplesPerSec; 
  int      numChannels   = pFormat->uChannels; 
  int      nFormat1      = 0; 
  int      nFormat2      = 0; 
 
  snd_pcm_format_t m_format; 
  snd_pcm_uframes_t bufferSz; 
  unsigned long buffer_size, period_size; 
 
  if( nSampleWidth == 16) 
    { 
      m_format = SND_PCM_FORMAT_S16; 
    } 
  else 
    { 
      m_format =  SND_PCM_FORMAT_U8; 
    } 
 
  m_frameBytes = snd_pcm_format_physical_width( m_format); 
 
  if ((err = snd_pcm_hw_params_set_format( pcm_handle, hwparams, 
                                           m_format) < 0)) { 
#ifdef _DEBUG 
    fprintf (stderr, "Set sample format failed %s %d\n", 
             snd_strerror (err), m_format ); 
#endif         
    return (  m_wLastError = RA_AOE_NOTENABLED ); 
  } 
 
  //If we went to 8-bit then 
  if( m_format ==  SND_PCM_FORMAT_U8 ) 
    { 
      nSampleWidth = 8; 
    } 
 
  m_uSampFrameSize = samplesize =nSampleWidth/8; 
 
  if ( nSampleWidth != pFormat->uBitsPerSample ) 
    { 
      ((HXAudioFormat*)pFormat)->uBitsPerSample = nSampleWidth; 
    } 
 
  // Set sample rate. If the exact rate is not supported 
  // by the hardware, use nearest possible rate.          
  unsigned int rrate = (unsigned int) nSampleRate; 
  if(( err = snd_pcm_hw_params_set_rate_near( pcm_handle, hwparams, &rrate, 0) ) < 0 ) 
    { 
#ifdef _DEBUG 
      fprintf(stderr, 
              "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", 
              nSampleRate, rrate); 
#endif 
      return ( m_wLastError = RA_AOE_NOTENABLED ); 
    } 
 
  m_unSampleRate = rrate; 
 
  if ( rrate != pFormat->ulSamplesPerSec ) 
    { 
      ((HXAudioFormat*)pFormat)->ulSamplesPerSec = rrate; 
    } 
 
 
  if (snd_pcm_hw_params_set_channels( pcm_handle, hwparams, numChannels) < 0) { 
#ifdef _DEBUG 
    fprintf(stderr, "Error setting channels.\n"); 
#endif 
    return ( m_wLastError = RA_AOE_NOTENABLED ); 
  } 
 
  m_unNumChannels = channels = numChannels; 
 
  if ( numChannels != pFormat->uChannels ) 
    { 
      ((HXAudioFormat*)pFormat)->uChannels = numChannels; 
    } 
 
 
  int periods = 2;       // Number of periods  
  snd_pcm_uframes_t periodsize = 2048; // FIXME 
  int direct=0; 
  snd_pcm_uframes_t period = 0; 
 
  // The period size == fragment size. 
  // Note that this in frames (frame = nr_channels * sample_width) 
  // So a value of 1024 means 4096 bytes (1024 x 2 x 16-bits) 
         
  //     if((err = snd_pcm_hw_params_get_period_size_max( hwparams, &period,& direct) ) < 0) 
  //       { 
  // #ifdef _DEBUG 
  //           printf("Unable to get period size %ld : %s\n", period,  snd_strerror(err)); 
  // #endif 
  //       } 
  //     direct=0; 
 
  if((err = snd_pcm_hw_params_set_period_size_near( pcm_handle, hwparams, &periodsize, &direct) ) < 0) 
    { 
#ifdef _DEBUG 
      printf("Set period size failed %ld : %s\n", periodsize,  snd_strerror(err)); 
#endif 
    } 
 
 
  //////////////////////////////////// set periods 
  //      printf("set periods\n"); 
  //      if ((err = snd_pcm_hw_params_set_periods_near( pcm_handle, hwparams, &periods, &direct ))  < 0 ) 
  //        { 
  //            printf("Error setting periods  %s\n", snd_strerror(err)); 
  //        } 
  
  /*     if(( err =snd_pcm_hw_params_get_period_size( hwparams, &period_size, NULL)) < 0) 
         { 
         printf("unable to get peroidsize:\n") ; 
         } 
  */   
  //     snd_pcm_hw_params_get_period_size( hwparams, &val); 
 
 
 
  //     if((err = snd_pcm_hw_params_set_buffer_size( pcm_handle, hwparams, bufferSz )) < 0) 
  //      if((err = snd_pcm_hw_params_set_buffer_size_near( pcm_handle, hwparams, bufferSz )) < 0) 
  //        { 
  //            printf("Unable to set buffer size %li : %s\n", bufferSz, snd_strerror(err)); 
  //        } 
  //     printf("get buffer size\n"); 
 
  ////////////////////////// Get buffer size in frames  
  //  m_wBlockSize = m_ulBytesPerGran; 
  if((err = snd_pcm_hw_params_get_buffer_size_max( hwparams, &bufferSz)) < 0)  
    { 
#ifdef _DEBUG 
      printf("Get buffer size failed:\n") ; 
#endif 
      if(  m_ulDeviceBufferSize <= 0) 
        blocksize = m_wBlockSize = m_ulDeviceBufferSize = 8192 * 2; 
    } 
  else 
    { 
      blocksize = bufferSz; 
      m_wBlockSize = blocksize; 
      m_ulDeviceBufferSize  = blocksize *  samplesize * channels; 
    } 
  //    printf("blocksize %d, samplesize %d, channels %d\n", blocksize, samplesize, channels ); 
  //    printf("m_ulDeviceBufferSize %ld\n",  m_ulDeviceBufferSize); 
 
   
  if (snd_pcm_hw_params( pcm_handle, hwparams) < 0) //write device 
    { 
#ifdef _DEBUG 
      fprintf(stderr, "Error setting HW params.\n"); 
#endif 
      return ( m_wLastError = RA_AOE_NOTENABLED ); 
    } 
 
 
  snd_pcm_sw_params_alloca(&swparams); 
 
  if((err = snd_pcm_sw_params_current( pcm_handle, swparams)) <0) 
    { 
#ifdef _DEBUG 
      printf("Current SW params failed: %s\n", snd_strerror(err)); 
#endif 
    } 
 
  //  if ((err = snd_pcm_sw_params_set_avail_min ( pcm_handle, swparams, 2048)) < 0) { 
  //       fprintf (stderr, "cannot set minimum available count (%s)\n", 
  //                snd_strerror (err)); 
  //   } 
 
  if((err = snd_pcm_sw_params_set_start_threshold( pcm_handle, swparams, bufferSz )) < 0) 
    { 
#ifdef _DEBUG 
      printf("Set start threshold mode failed: %s\n", snd_strerror(err)); 
#endif 
    } 
 
  if((err = snd_pcm_sw_params_set_xfer_align( pcm_handle, swparams, 1)) < 0) 
    { 
#ifdef _DEBUG 
      printf("Set transfer align failed: %s\n", snd_strerror(err)); 
#endif 
    }  
 
  if((err = snd_pcm_sw_params_set_tstamp_mode( pcm_handle, swparams,SND_PCM_TSTAMP_MMAP )) < 0 ) 
    { 
#ifdef _DEBUG 
      printf("Set sw params time stamp mode failed: %s\n", snd_strerror(err)); 
#endif 
    } 
 
  if((err = snd_pcm_sw_params( pcm_handle, swparams)) < 0) 
    { 
#ifdef _DEBUG 
      printf("Set sw params failed: %s\n", snd_strerror(err)); 
#endif 
    } 
 
  if ((err=snd_pcm_prepare ( pcm_handle)) < 0) 
    { 
#ifdef _DEBUG 
      fprintf (stderr, "Prepare audio interface failed %s\n", 
               snd_strerror (err)); 
#endif 
      return  retCode = RA_AOE_NOTENABLED; 
    }        
 
 
#ifdef _DEBUG 
  snd_output_t *output = NULL; 
  snd_output_stdio_attach(&output, stdout, 0); 
  printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); 
  snd_pcm_dump( pcm_handle, output); 
  printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); 
  snd_pcm_hw_params_dump( hwparams, output); 
  printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); 
  snd_pcm_sw_params_dump( swparams, output);  
  printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); 
  //     snd_pcm_status_dump( status, output); 
  //     printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"); 
 
  fprintf( stdout, "Device Configured:\n"); 
  fprintf( stdout, "         Sample Rate: %d\n",  m_unSampleRate); 
  fprintf( stdout, "        Sample Width: %d\n",  nSampleWidth); 
  fprintf( stdout , "        Num channels: %d\n",  m_unNumChannels); 
  fprintf( stdout, "          Block size: %d\n",  m_wBlockSize); 
  fprintf( stdout, "  Device buffer size: %lu\n", m_ulDeviceBufferSize); 
 
#endif 
 
  //     snd_pcm_sw_params_free( swparams); 
 
  return RA_AOE_NOERR; 
} 
 
void CAudioOutLinuxAlsa::_SyncUpTimeStamps(ULONG32 writeCount) 
{ 
  snd_pcm_hwsync( pcm_handle); 
  int bytes2  = 0; 
  int err = 0; 
 
  snd_pcm_sframes_t framedelay; 
 
  if((err = snd_pcm_delay( pcm_handle, &framedelay)) < 0) 
    { 
#ifdef _DEBUG 
      fprintf (stderr, "cannot get delay %s\n", snd_strerror (err)); 
#endif 
    } 
 
  bytes2 = snd_pcm_frames_to_bytes( pcm_handle, framedelay); 
              
  if( bytes2 > 0) 
    { 
      m_ulLastBytesPlayed = (UINT64)( m_ulTotalWritten + writeCount - bytes2); 
      m_ulLastTimeStamp   = GetTickCount(); 
    } 
} 
 
 
//Device specific method to write bytes out to the audiodevice and return a 
//count of bytes written. 
HX_RESULT CAudioOutLinuxAlsa::_WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount ) 
{ 
  LONG32 writeCount; 
  HX_RESULT retCode = RA_AOE_NOERR; 
  int err=0; 
  if( m_ulTickCount == 0 ) 
    m_ulTickCount = GetTickCount(); 
 
  snd_pcm_sframes_t num_frames, size; 
 
  lCount = 0; 
 
  num_frames = snd_pcm_bytes_to_frames( pcm_handle, ulBuffLength); //alsa in frames 
 
  // Returns the number of frames actually written.  
  size = snd_pcm_writei( pcm_handle, buffer, num_frames ); 
 
  if(size < 0) 
    { 
      switch (size) 
        { 
        case -EBADFD: 
          { 
#ifdef _DEBUG 
            printf("EBADFD: Device not in the right state \n"); 
#endif 
            retCode = RA_AOE_DEVBUSY; 
          } 
          break; 
        case -EPIPE: //lets handle underruns 
          { 
            if(( size = snd_pcm_prepare( pcm_handle) ) < 0 ) 
              { 
#ifdef _DEBUG 
                printf("EPIPE: Recovery from underrun is difficult: %s\n", snd_strerror( size)); 
#endif 
                retCode = RA_AOE_DEVBUSY; 
              } 
          } 
          break; 
        case -ESTRPIPE: 
          { 
            while (( size = snd_pcm_resume( pcm_handle)) == -EAGAIN) 
              sleep(1); 
            if ( size < 0) 
              { 
                size = snd_pcm_prepare( pcm_handle); 
                if ( size < 0) 
                  { 
#ifdef _DEBUG 
                    printf("ESTRPIPE: Recover from suspend is difficult: %s\n", snd_strerror( size)); 
#endif 
                    retCode = RA_AOE_DEVBUSY; 
                  } 
              } 
          } 
          break; 
        }; 
    } 
 
//  printf("frames written = %d\n", size); 
  writeCount = snd_pcm_frames_to_bytes( pcm_handle, size ); 
 
  if( writeCount < 0 ) 
    { 
      if( errno == EAGAIN ) 
        retCode = RA_AOE_NOERR; 
      if( errno == EINTR ) 
        retCode = RA_AOE_DEVBUSY; 
    } 
  else 
    { 
      _SyncUpTimeStamps( writeCount); 
 
      // XXXRGG: Figure out why writeCount != ulBuffLength 
      // lCount = writeCount; 
      lCount = ulBuffLength; 
    } 
 
  return retCode; 
} 
 
 
UINT64 CAudioOutLinuxAlsa::_GetBytesActualyPlayed(void) const 
{ 
  // Get current playback position in device DMA.  
  int     bytes2 = 0; 
  UINT64  ulTheAnswer = 0; 
 
  if( m_ulTotalWritten > 0 ) 
    { 
      HX_ASSERT( m_unSampleRate!=0 && m_uSampFrameSize!=0 ); 
      ULONG32 ulTick = GetTickCount(); 
      //We need to update the timestamps every so often. 
      //This make sure that if the XServer was blocked, and 
      //we ran dry, that we re-sync up. 
      if( (ulTick - m_ulLastTimeStamp) > 200 ) 
        { 
          ((CAudioOutLinuxAlsa*)this)->_SyncUpTimeStamps(); 
          ulTick = GetTickCount(); 
        } 
      ulTheAnswer = (UINT64)(m_ulLastBytesPlayed+ 
                             ((float)(ulTick-m_ulLastTimeStamp)* 
                              (float)m_unNumChannels/1000.0* 
                              m_unSampleRate*m_uSampFrameSize) +0.5 ); 
    } 
 
  //    printf("BytesActualyPlayed %ld\n", ulTheAnswer); 
  return  ulTheAnswer; 
} 
 
 
//this must return the number of bytes that can be written without blocking. 
HX_RESULT CAudioOutLinuxAlsa::_GetRoomOnDevice(ULONG32& ulBytes) const 
{ 
  HX_RESULT  retCode = RA_AOE_NOERR; 
 
  // FIXME 
 
  ulBytes = m_ulDeviceBufferSize - ( m_ulTotalWritten - _GetBytesActualyPlayed() ); 
  //    printf("RoomOnDevice %ld\n", ulBytes); 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
 
//Device specific method to get/set the devices current volume. 
UINT16 CAudioOutLinuxAlsa::_GetVolume() const 
{ 
 
  UINT16 nRetVolume   = 0; 
  long pmin = 0, pmax = 0; 
  long percentage = 0; 
   
  long frontLeftVolume = 0; 
  long frontRightVolume = 0; 
   
  /*  long frontCenterVolume = 0; 
 
  long rearLeftVolume = 0; 
  long rearRightVolume = 0; 
   
  long wooferVolume = 0; 
 
  snd_mixer_selem_id_t *sid; 
  snd_mixer_selem_id_alloca(&sid); 
  */ 
  snd_mixer_elem_t *mixer_elements2; 
 
  mixer_elements2 = snd_mixer_first_elem( mixer_handle); 
  // grab first elem of mixer 
  if (snd_mixer_selem_has_playback_volume( mixer_elements2)) { 
             
    if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
                                             SND_MIXER_SCHN_FRONT_LEFT, &frontLeftVolume) < 0) 
      frontLeftVolume = 0; 
            
    if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
                                             SND_MIXER_SCHN_FRONT_RIGHT, &frontRightVolume) < 0) 
      frontRightVolume = 0; 
  } 
 
  /*    snd_mixer_selem_get_playback_volume_range( mixer_elements2, &pmin, &pmax); 
        int range = pmax - pmin; 
        int val = frontLeftVolume; 
        frontRightVolume =  frontLeftVolume; 
        val -= pmin; 
 
        //    printf("min %d, max %d, range %d, val %d\n", pmin, pmax, range, val ); 
        percentage = (long)rint((double)val / (double)range * 100); 
  */       
 
  //    printf("fleft %ld, fright %ld, percent %d\n", 
  //           frontLeftVolume, frontRightVolume, percentage); 
 
 
  // Save for future use, demonstrates multichannel mixer access. 
   
 
  //   for ( mixer_elements2 = snd_mixer_first_elem( mixer_handle); 
  //         mixer_elements2; 
  //         mixer_elements2 = snd_mixer_elem_next( mixer_elements2) )  { 
     
  //     snd_mixer_selem_get_id( mixer_elements2, sid); 
     
  // //     if ( !snd_mixer_selem_is_active( mixer_elements2)) 
  // //       continue; 
     
  //     printf("mixer control '%s',%i\n",  
  //     snd_mixer_selem_id_get_name( sid),  
  //     snd_mixer_selem_id_get_index( sid)); 
   
  //        snd_mixer_selem_get_playback_volume_range( mixer_elements2, &pmin, &pmax); 
  //         // if its got mono mixer 
  //        if ( snd_mixer_selem_has_common_volume( mixer_elements2)) //mono 
  //      { 
  //            // check for playback vol mixer          
  //          if (snd_mixer_selem_has_playback_volume( mixer_elements2)) { 
  //                //score!         
  // //           if( snd_mixer_find_selem( mixer_handle,mixer_elements2) == SND_MIXER_SCHN_MONO) 
  // //             { 
  //              snd_mixer_selem_get_playback_volume( mixer_elements2, 
  //                                                   SND_MIXER_SCHN_MONO, &frontLeftVolume); 
  //                //for finding percentage 
                 
  //              int range = pmax - pmin; 
  //              int val = frontLeftVolume; 
  //              frontRightVolume =  frontLeftVolume; 
  //              val -= pmin; 
  //              percentage = (long)rint((double)val / (double)range * 100); 
  //              printf("common min %d, max %d, range %d\n", pmin, pmax, range); 
    
  //          } 
  //      } 
  //        else //stereo 
  //       { 
  // //          printf("get seperate r and l volumes\n");        
  //           if (snd_mixer_selem_has_playback_volume( mixer_elements2)) { 
             
  //               if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
  //                                                        SND_MIXER_SCHN_FRONT_LEFT, &frontLeftVolume) < 0) 
  //                   frontLeftVolume = 0; 
            
  //               if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
  //                                                        SND_MIXER_SCHN_FRONT_RIGHT, &frontRightVolume) < 0) 
  //                   frontRightVolume = 0; 
               
  // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
  // //                                                        SND_MIXER_SCHN_FRONT_CENTER, &frontCenterVolume) < 0) 
  // //                   frontCenterVolume = 0; 
            
  // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
  // //                                                        SND_MIXER_SCHN_REAR_RIGHT, &rearRightVolume) < 0) 
  // //                   rearRightVolume = 0; 
               
  // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
  // //                                                        SND_MIXER_SCHN_REAR_LEFT, &rearLeftVolume) < 0) 
  // //                   rearLeftVolume = 0; 
            
  // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2, 
  // //                                                        SND_MIXER_SCHN_WOOFER, &wooferVolume) < 0) 
  // //                   wooferVolume = 0; 
                 
  //               long range = pmax - pmin; 
  //               long val = frontLeftVolume; 
  //               frontRightVolume =  frontLeftVolume; 
  //               val -= pmin; 
 
  //               percentage = (long)rint(val / range * 100); 
         
  //                 //            printf("min %d, max %d, range %d\n", pmin, pmax, range); 
 
  //               printf("fleft %ld, fright %ld, center %ld, rleft %ld, rright %ld, woofer %ld, percent %d\n", 
  //                      frontLeftVolume, frontRightVolume, frontCenterVolume, rearLeftVolume, 
  //                      rearRightVolume, percentage); 
  //           } 
  // //         snd_mixer_selem_get_playback_volume_range( mixer_elements2, &pmin, &pmax); 
  //       } 
  //     } 
   
 
  //    nLeftVolume  = (nVolume & 0x000000ff);  
  //    nRightVolume = (nVolume & 0x0000ff00) >> 8; 
 
  nRetVolume  = (UINT16)frontLeftVolume; 
 
  //    printf("nRetVolume is %ld\n",nRetVolume); 
 
  return nRetVolume; 
} 
 
 
HX_RESULT CAudioOutLinuxAlsa::_SetVolume(UINT16 unVolume) 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  ///////////////////////// FIXME:  
  long pmin = 0, pmax = 0; 
  long percentage = 0; 
   
  long frontLeftVolume = 0; 
  long frontRightVolume = 0; 
   
  /*  long frontCenterVolume = 0; 
 
  long rearLeftVolume = 0; 
  long rearRightVolume = 0; 
   
  long wooferVolume = 0; 
 
  snd_mixer_selem_id_t *sid; 
  snd_mixer_selem_id_alloca(&sid); 
  */ 
  snd_mixer_elem_t *mixer_elements; 
 
  frontLeftVolume = frontRightVolume = unVolume; 
  //    nNewVolume = (unVolume & 0xff) | ((unVolume &0xff) << 8); 
 
  mixer_elements = snd_mixer_first_elem( mixer_handle); //just grab first elem for now 
 
  if (snd_mixer_selem_has_playback_volume( mixer_elements)) { 
 
    // do this as the volume range in alsa might be more or less than 100 
    // act like its a percentage, the vol range for rmedigi96 is 16383, and my pci128 is 16 
    snd_mixer_selem_get_playback_volume_range( mixer_elements, &pmin, &pmax); 
 
    int range = pmax - pmin; 
    int val = frontLeftVolume; 
 
    //percent to volume 
    frontLeftVolume = rint((double)range * ((double)val*.01)) + pmin;  
   
    frontRightVolume = frontLeftVolume; 
   
    if( snd_mixer_selem_set_playback_volume( mixer_elements, 
                                             SND_MIXER_SCHN_FRONT_LEFT, 
                                             frontLeftVolume) < 0) 
      frontLeftVolume = 0; 
 
    if( snd_mixer_selem_set_playback_volume( mixer_elements, 
                                             SND_MIXER_SCHN_FRONT_RIGHT, 
                                             frontRightVolume) < 0) 
      frontRightVolume = 0; 
  }               
#ifdef _DEBUG 
  printf("setting volume left %ld, right %ld %ld\%\n", frontLeftVolume, frontRightVolume, unVolume ); 
#endif 
 
 
 
 
  // Save for future use, demonstrates multichannel mixer access. 
 
  
  //     for ( mixer_elements = snd_mixer_first_elem( mixer_handle); //ramble through elems 
  //           mixer_elements;  
  //           mixer_elements = snd_mixer_elem_next( mixer_elements) ) 
  //       { 
     
  //           snd_mixer_selem_get_id( mixer_elements, sid); 
     
  //           printf("mixer control '%s',%i\n",  
  //                  snd_mixer_selem_id_get_name( sid),  
  //                  snd_mixer_selem_id_get_index( sid)); 
 
  //           if ( snd_mixer_selem_has_common_volume( mixer_elements)) //is mono 
  //             { 
  // //          // check for playback vol mixer          
  //                 if (snd_mixer_selem_has_playback_volume( mixer_elements)) { 
  //                     snd_mixer_selem_get_playback_volume_range( mixer_elements, &pmin, &pmax); 
 
  //                     int range = pmax - pmin; 
  //                     int val = frontLeftVolume; 
 
  //                     val -= pmin; 
  //                     frontLeftVolume  =  rint((double)range * ((double)val*.01)) + pmin; //percent to volume 
  //                       //frontLeftVolume  = rint((double)val/(double)range * 100); 
  //                       // frontLeftVolume = (long) ( unVolume & 0x000000ff); 
  //                     frontRightVolume = frontLeftVolume; 
  //                       //  frontRightVolume = (long) ( unVolume & 0x0000ff00); 
  //                       //score!        
  //                     if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  //                                                              SND_MIXER_SCHN_MONO, 
  //                                                              frontLeftVolume) < 0) 
  //                         frontLeftVolume = 0; 
  //                     val -= pmin; 
  //                     percentage = (long)rint((double)val/(double)range * 100); 
  //                 } 
  //             } 
  //           else //is stereo 
  //             { 
  // //        if (snd_mixer_selem_has_playback_volume( mixer_elements)) { 
  //                 snd_mixer_selem_get_playback_volume_range( mixer_elements, &pmin, &pmax); 
  //                 printf("vol min %d, max %d\n", pmin, pmax); 
  //                 int range = pmax - pmin; 
  //                 int tmp; 
  //                 int val = frontLeftVolume; 
  //                 frontLeftVolume  =  rint((double)range * ((double)val*.01)) + pmin; //percent to volume 
  //                   //rint((double)val/(double)range * 100); 
   
  // //  frontLeftVolume = (long) ( unVolume & 0x000000ff); 
  //                 frontRightVolume = frontLeftVolume; 
  // //  frontRightVolume = (long) ( unVolume & 0x0000ff00); 
 
   
  //                 if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  //                                                          SND_MIXER_SCHN_FRONT_LEFT, 
  //                                                          frontLeftVolume) < 0) 
  //                     frontLeftVolume = 0; 
 
  //                 if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  //                                                          SND_MIXER_SCHN_FRONT_RIGHT, 
  //                                                          frontRightVolume) < 0) 
  //                     frontRightVolume = 0; 
               
  // //              if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  // //                SND_MIXER_SCHN_FRONT_CENTER, &frontCenterVolume) < 0) 
  // //               frontCenterVolume = 0; 
            
  // //               if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  // //                  SND_MIXER_SCHN_REAR_RIGHT, &rearRightVolume) < 0) 
  // //                  rearRightVolume = 0; 
               
  // //              if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  // //                SND_MIXER_SCHN_REAR_LEFT, &rearLeftVolume) < 0) 
  // //               rearLeftVolume = 0; 
            
  // //               if( snd_mixer_selem_set_playback_volume( mixer_elements, 
  // //                    SND_MIXER_SCHN_WOOFER, &wooferVolume) < 0) 
  // //                   wooferVolume = 0; 
  // //        } 
  //                 int range2 = pmax - pmin; 
  //                 int val2 = frontLeftVolume; 
  //                 val2 -= pmin; 
  //                 percentage = (long)rint((double)val2/(double)range2 * 100); 
         
  //             } 
  //       } 
 
  //     printf("fleft %d, fright %d, center %d, rleft %d, rright %d, woofer %d, percent %d\n", 
  //            frontLeftVolume, frontRightVolume, frontCenterVolume, rearLeftVolume,  
  //            rearRightVolume, percentage); 
 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
 
//Device specific method to drain a device. This should play the remaining 
//bytes in the devices buffer and then return. 
HX_RESULT CAudioOutLinuxAlsa::_Drain() 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  if ( pcm_handle < 0 ) 
    { 
      retCode = RA_AOE_DEVNOTOPEN; 
    } 
  // Stop PCM device after pending frames have been played 
  if( snd_pcm_drain( pcm_handle) < 0) 
    retCode = RA_AOE_GENERAL; 
     
 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
 
//Device specific method to reset device and return it to a state that it 
//can accept new sample rates, num channels, etc. 
HX_RESULT CAudioOutLinuxAlsa::_Reset() 
{ 
  HX_RESULT retCode = RA_AOE_NOERR; 
  m_ulPausePosition = 0; 
 
  if( pcm_handle > 0 ) 
    //    if (snd_pcm_state( pcm_handle) == SND_PCM_STATE_OPEN) 
    { 
      if( snd_pcm_reset( pcm_handle) < 0) 
        { 
          retCode = RA_AOE_GENERAL; 
        } 
    } 
  else 
    { 
      retCode = RA_AOE_DEVNOTOPEN; 
    } 
 
  m_wLastError = retCode; 
  return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinuxAlsa::_CheckFormat( const HXAudioFormat* pFormat ) 
{ 
  int   nBitsPerSample = pFormat->uBitsPerSample; 
  int   ulTmp          = pFormat->ulSamplesPerSec; 
  int   nNumChannels   = pFormat->uChannels; 
  float fTmp           = 0.0; 
  int err=0; 
  snd_pcm_format_t m_format; 
  HX_RESULT retCode = RA_AOE_NOERR; 
 
  if( nBitsPerSample = 16) 
    { 
      m_format = SND_PCM_FORMAT_S16; 
    } 
  else 
    { 
      m_format =  SND_PCM_FORMAT_U8; 
    } 
 
  //Is the device already open? 
  if( pcm_handle > 0 ||  RA_AOE_NOERR != _OpenAudio() ) 
    { 
      retCode = RA_AOE_DEVBUSY; 
      return retCode; 
    } 
 
  //See if the sample rate is supported. 
  if( snd_pcm_hw_params_test_rate ( pcm_handle, hwparams, ulTmp  ,0) < 0) 
    { 
#ifdef _DEBUG 
      fprintf (stderr, "cannot set sample rate (%s)\n", 
               snd_strerror (err)); 
#endif 
      //Not quite the real error, but it is what we need to return. 
      retCode = RA_AOE_BADFORMAT; 
      goto donechecking; 
    } 
 
  //Check num channels. 
  if(snd_pcm_hw_params_test_channels ( pcm_handle, hwparams, nNumChannels) < 0) 
    { 
      retCode = RA_AOE_BADFORMAT; 
      goto donechecking; 
    } 
 
  if ( snd_pcm_hw_params_test_format( pcm_handle, hwparams, m_format  /*SND_PCM_FORMAT_S16*/ ) < 0) 
    { 
      retCode = RA_AOE_BADFORMAT; 
      goto donechecking; 
    } 
 
  //Close the audio device. 
 donechecking: 
  _CloseAudio(); 
  m_wLastError = retCode; 
  return retCode; 
} 
 
 
HX_RESULT CAudioOutLinuxAlsa::_CheckSampleRate( ULONG32 ulSampleRate ) 
{ 
  ULONG32 ulTmp = ulSampleRate; 
 
  m_wLastError = RA_AOE_NOERR; 
  //Is the device already open? 
  if( pcm_handle > 0 || RA_AOE_NOERR != _OpenAudio() ) 
    { 
      m_wLastError = RA_AOE_DEVBUSY; 
    } 
  else 
    { 
      //See if the sample rate is supported. 
      if( snd_pcm_hw_params_test_rate ( pcm_handle, hwparams, (uint)ulTmp, 0) != 1) 
        { 
          //Not quite the real error, but it is what we need to return. 
          m_wLastError = RA_AOE_DEVBUSY; 
        } 
      else if( ulSampleRate != ulTmp ) 
        { 
          //It is NOT supported 
          m_wLastError = RA_AOE_BADFORMAT; 
        } 
 
      _CloseAudio(); 
    } 
 
  return m_wLastError; 
} 
 
 
HX_RESULT CAudioOutLinuxAlsa::_Pause() 
{ 
  m_wLastError = HXR_OK; 
  m_ulPausePosition = m_ulTotalWritten; 
  m_ulTickCount = 0; 
  m_ulLastTimeStamp   = 0; 
  int err=0; 
  if( m_bHasHardwarePause) 
    { 
      if((err = snd_pcm_pause( pcm_handle, 1)) < 0) 
        { 
#ifdef _DEBUG 
          fprintf(stderr, "Error : cannot pause (%s)\n", snd_strerror (err)); 
#endif 
          m_wLastError = RA_AOE_NOTSUPPORTED; 
        } 
    } 
  else 
    { 
      m_wLastError = RA_AOE_NOTSUPPORTED; 
    } 
 
  return m_wLastError; 
} 
 
HX_RESULT CAudioOutLinuxAlsa::_Resume() 
{ 
  m_wLastError = HXR_OK; 
 
  if( m_ulTotalWritten > 0 ) 
    { 
      m_ulTickCount = GetTickCount(); 
      m_ulLastTimeStamp = m_ulTickCount; 
    } 
 
  if( m_bHasHardwarePause &&  m_bHasHardwareResume) 
    { 
      int err=0; 
      if((err = snd_pcm_resume( pcm_handle)) < 0) 
        { 
#ifdef _DEBUG 
          fprintf (stderr, "Error: cannot resume (%s)\n", 
                   snd_strerror (err)); 
#endif 
          m_wLastError = RA_AOE_NOTSUPPORTED; 
        } 
    } 
  else 
    { 
      m_wLastError = RA_AOE_NOTSUPPORTED; 
    } 
     
  return m_wLastError; 
} 
 
BOOL CAudioOutLinuxAlsa::_HardwarePauseSupported() const 
{ 
     
  if( snd_pcm_hw_params_can_resume( hwparams) == 1) 
    { 
      m_bHasHardwareResume = true; 
    } 
  else 
    { 
      m_bHasHardwareResume = false; 
    } 
 
  if( snd_pcm_hw_params_can_pause( hwparams) == 1) 
    { 
      m_bHasHardwarePause = true; 
      return TRUE; 
    } 
  else 
    { 
      m_bHasHardwarePause = false; 
    } 
  return FALSE; 
}