audlinux_alsa.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:38k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2003 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location:
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s):  ljp <ljp@llornkcor.com>
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include <unistd.h>
  36. #include <fcntl.h>
  37. #include <stdlib.h>
  38. #include <errno.h>
  39. #include <sys/ioctl.h>
  40. #define ALSA_PCM_NEW_HW_PARAMS_API
  41. #define ALSA_PCM_NEW_SW_PARAMS_API
  42. #include <alsa/asoundlib.h>
  43. #include <stdio.h> 
  44. #include <math.h>
  45. #include "ihxpckts.h"
  46. #include "hxtick.h"
  47. #include "hxprefs.h"
  48. #include "timeval.h"
  49. #include "hxthread.h"
  50. #include "audlinux_alsa.h"
  51. #include "hxstrutl.h"
  52. #include "dllacces.h"
  53. #include "dllpath.h"
  54. //------------------------------------------
  55. // Ctors and Dtors.
  56. //------------------------------------------
  57. CAudioOutLinuxAlsa::CAudioOutLinuxAlsa() :
  58.   CAudioOutUNIX(),
  59.   m_ulTickCount(0),
  60.   m_ulLastBytesPlayed(0),
  61.   m_ulLastTimeStamp(0),
  62.   m_ulPausePosition(0),
  63.   m_bHasHardwarePause(FALSE),
  64.   m_bHasHardwareResume(FALSE),
  65.   pcm_handle(0),
  66.   mixer_handle(0)
  67. {
  68. };
  69. CAudioOutLinuxAlsa::~CAudioOutLinuxAlsa()
  70. {
  71. #ifdef _DEBUG
  72.   printf("d'torn");
  73. #endif
  74.   //The mixer is opened independently of the audio device. Make sure
  75.   //it is closed.
  76.   _CloseMixer();
  77.   snd_pcm_hw_params_free(hwparams);
  78.   //    snd_pcm_status_free(status);
  79. };
  80. // These Device Specific methods must be implemented
  81. // by the platform specific sub-classes.
  82. INT16 CAudioOutLinuxAlsa::_Imp_GetAudioFd(void)
  83. {
  84.   return (INT16) pcm_handle;
  85. }
  86. //Device specific methods to open/close the mixer and audio devices.
  87. HX_RESULT CAudioOutLinuxAlsa::_OpenAudio()
  88. {
  89.   HX_RESULT retCode = RA_AOE_NOERR;
  90.   int err=0;
  91.     
  92.   //Set the tick count to zero
  93.   m_ulTickCount       = 0;
  94.   m_ulLastTimeStamp   = 0;
  95.   m_ulLastBytesPlayed = 0;
  96.   m_ulPausePosition   = 0;
  97.   //Check the environmental variable to let user overide default device.
  98.   char *pszOverrideName = getenv( "AUDIO" );
  99.   char szDevName[MAX_DEV_NAME];
  100.   // Use defaults if no environment variable is set.
  101.   if ( pszOverrideName && strlen(pszOverrideName)>0 )
  102.     {
  103.       SafeStrCpy( szDevName, pszOverrideName, MAX_DEV_NAME );
  104.     }
  105.   else
  106.     {
  107.       SafeStrCpy( szDevName, "default", MAX_DEV_NAME );
  108.       //        SafeStrCpy( szDevName, "hw:0,0", MAX_DEV_NAME );
  109.       //        SafeStrCpy( szDevName, "plughw:0,0", MAX_DEV_NAME );
  110.     }
  111.   // Open the audio device if it isn't already open
  112.   if ( pcm_handle <= 0 )
  113.     {
  114.       if ( snd_pcm_open( &pcm_handle, szDevName, SND_PCM_STREAM_PLAYBACK /*stream*/, 0) < 0) {
  115. #ifdef _DEBUG
  116.         fprintf( stderr, "Failed to open audio device %s : %d  errno: %dn",
  117.                  szDevName, pcm_handle, errno );
  118. #endif
  119.         retCode = RA_AOE_BADOPEN;
  120.       }
  121.     }
  122.   if((err=snd_pcm_nonblock( pcm_handle, 1)) < 0)
  123.     {
  124. #ifdef _DEBUG
  125.       fprintf (stderr, "Cannot set nonblock (%s)n",
  126.                snd_strerror (err));
  127. #endif
  128.     }
  129.   //     if((err = snd_pcm_status_malloc( &status)) <0)
  130.   //       {
  131.   //           fprintf (stderr, "cannot allocate status parameter structure (%s)n",
  132.   //                    snd_strerror (err));
  133.   //       }
  134.   if((err = snd_pcm_hw_params_malloc( &hwparams)) < 0)
  135.     {
  136. #ifdef _DEBUG
  137.       fprintf (stderr, "HW parameters malloc failed %sn",
  138.                snd_strerror (err));
  139. #endif
  140.     }
  141.  
  142.   if ((err = snd_pcm_hw_params_any( pcm_handle, hwparams)) < 0) {
  143. #ifdef _DEBUG
  144.     fprintf (stderr, "HW parameters init failed %sn",
  145.              snd_strerror (err));
  146. #endif
  147.   }
  148.   m_wLastError = retCode;
  149.   return m_wLastError;
  150. }
  151. HX_RESULT CAudioOutLinuxAlsa::_CloseAudio()
  152. {
  153.   HX_RESULT retCode = RA_AOE_NOERR;
  154.   if( pcm_handle > 0 )
  155.     //        if (snd_pcm_state( pcm_handle) == SND_PCM_STATE_OPEN)
  156.     {
  157.       snd_pcm_close( pcm_handle);
  158.       pcm_handle = 0;//NO_FILE_DESCRIPTOR;
  159. #ifdef _DEBUG
  160.       printf("pcm_handle is now %dn", pcm_handle);
  161. #endif
  162.     }
  163.   else
  164.     {
  165.       retCode = RA_AOE_DEVNOTOPEN;
  166.     }
  167.   m_wLastError = retCode;
  168.   return m_wLastError;
  169. }
  170. HX_RESULT CAudioOutLinuxAlsa::_OpenMixer()
  171. {
  172.   HX_RESULT retCode = RA_AOE_NOERR;
  173.   int result;
  174.     
  175.   if(!m_bMixerPresent)
  176.     {
  177.       //Let user override default device with environ variable.
  178.       char *pszOverrideName = getenv( "MIXER" );
  179.       char szDevCtlName[MAX_DEV_NAME];
  180.       // could be "hw:0,0", or "plughw:0,0" or similiar
  181.       if (pszOverrideName && strlen(pszOverrideName) > 0 )
  182.         {
  183.           SafeStrCpy( szDevCtlName , pszOverrideName, MAX_DEV_NAME );
  184.         }
  185.       else
  186.         {
  187.           SafeStrCpy( szDevCtlName , "default", MAX_DEV_NAME ); 
  188.           // default for volume
  189.         }
  190.       if (( result = snd_mixer_open( &mixer_handle, 0)) < 0)
  191.         {
  192. #ifdef _DEBUG
  193.           printf( "Open audio device failed %d", result);
  194. #endif
  195.           retCode = RA_AOE_BADOPEN;
  196.         }
  197.   
  198.       if (( result = snd_mixer_attach( mixer_handle, szDevCtlName )) < 0) {
  199. #ifdef _DEBUG
  200.         printf("Mixer attach %s failed: %s", szDevCtlName, snd_strerror( result));
  201. #endif
  202.         snd_mixer_close( mixer_handle);
  203.         retCode = RA_AOE_NOTENABLED;
  204.       }
  205.   
  206.       if (( result = snd_mixer_selem_register( mixer_handle, NULL, NULL)) < 0) {
  207. #ifdef _DEBUG
  208.         printf("Register mixer error: %s", snd_strerror( result));
  209. #endif
  210.         snd_mixer_close( mixer_handle);
  211.         retCode = RA_AOE_NOTENABLED;
  212.       }
  213.       if((result = snd_mixer_load( mixer_handle)) < 0 )
  214.         {
  215.           retCode = RA_AOE_NOTENABLED;
  216.         }
  217.       if (mixer_handle > 0)
  218.         {
  219.           m_bMixerPresent = 1;
  220.           _Imp_GetVolume();
  221.         }
  222.       else
  223.         {
  224.           mixer_handle = 0;// NO_FILE_DESCRIPTOR;
  225.           m_bMixerPresent = 0;
  226.         }
  227.     }
  228.   m_wLastError = retCode;
  229.   return m_wLastError;
  230. }
  231. HX_RESULT CAudioOutLinuxAlsa::_CloseMixer()
  232. {
  233.   if( mixer_handle > 0 )
  234.     {
  235.       //Let user override default device with environ variable.
  236.       char *pszOverrideName = getenv( "MIXER" );
  237.       char szDevCtlName[MAX_DEV_NAME];
  238.       // could be "hw:0,0", or "plughw:0,0" or similiar
  239.       if (pszOverrideName && strlen(pszOverrideName) > 0 )
  240.         {
  241.           SafeStrCpy( szDevCtlName , pszOverrideName, MAX_DEV_NAME );
  242.         }
  243.       else
  244.         {
  245.           SafeStrCpy( szDevCtlName , "default", MAX_DEV_NAME ); 
  246.           // default for volume
  247.         }
  248.       if( snd_mixer_detach( mixer_handle, szDevCtlName) < 0)
  249.         {
  250. #ifdef _DEBUG
  251.           printf("Detach mixer failedn");
  252. #endif
  253.         }
  254.   
  255.       if(snd_mixer_close ( mixer_handle) < 0)
  256.         {
  257. #ifdef _DEBUG
  258.           printf("Close mixer failedn");
  259. #endif
  260.           mixer_handle = 0;//NO_FILE_DESCRIPTOR;
  261.         }
  262.     }
  263.   return m_wLastError;
  264. }
  265. //Devic specific method to set the audio device characteristics. Sample rate,
  266. //bits-per-sample, etc.
  267. //Method *must* set member vars. m_unSampleRate and m_unNumChannels.
  268. HX_RESULT CAudioOutLinuxAlsa::_SetDeviceConfig( const HXAudioFormat* pFormat )
  269. {
  270.   HX_RESULT retCode = RA_AOE_NOERR;
  271.   int err=0;
  272.   if (snd_pcm_state( pcm_handle) != SND_PCM_STATE_OPEN)
  273.     return RA_AOE_DEVNOTOPEN;
  274.   if (snd_pcm_hw_params_any( pcm_handle, hwparams) < 0) {
  275. #ifdef _DEBUG
  276.     fprintf(stderr, "Configure device failedn");
  277. #endif
  278.     return  retCode = RA_AOE_NOTENABLED;
  279.   }
  280.   // SND_PCM_ACCESS_RW_INTERLEAVED or       
  281.   // SND_PCM_ACCESS_RW_NONINTERLEAVED.      
  282.   // There are also access types for MMAPed 
  283.   if (snd_pcm_hw_params_set_access( pcm_handle, hwparams,
  284.                                     SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
  285. #ifdef _DEBUG
  286.     fprintf(stderr, "Set access failedn");
  287. #endif
  288.     return  retCode = RA_AOE_NOTENABLED;
  289.   }
  290.   //  Now set the format. Either 8-bit or 16-bit audio is supported.
  291.   // alsa supports up to 64 bit
  292.   int      nSampleWidth  = pFormat->uBitsPerSample;
  293.   ULONG32  nSampleRate   = pFormat->ulSamplesPerSec;
  294.   int      numChannels   = pFormat->uChannels;
  295.   int      nFormat1      = 0;
  296.   int      nFormat2      = 0;
  297.   snd_pcm_format_t m_format;
  298.   snd_pcm_uframes_t bufferSz;
  299.   unsigned long buffer_size, period_size;
  300.   if( nSampleWidth == 16)
  301.     {
  302.       m_format = SND_PCM_FORMAT_S16;
  303.     }
  304.   else
  305.     {
  306.       m_format =  SND_PCM_FORMAT_U8;
  307.     }
  308.   m_frameBytes = snd_pcm_format_physical_width( m_format);
  309.   if ((err = snd_pcm_hw_params_set_format( pcm_handle, hwparams,
  310.                                            m_format) < 0)) {
  311. #ifdef _DEBUG
  312.     fprintf (stderr, "Set sample format failed %s %dn",
  313.              snd_strerror (err), m_format );
  314. #endif        
  315.     return (  m_wLastError = RA_AOE_NOTENABLED );
  316.   }
  317.   //If we went to 8-bit then
  318.   if( m_format ==  SND_PCM_FORMAT_U8 )
  319.     {
  320.       nSampleWidth = 8;
  321.     }
  322.   m_uSampFrameSize = samplesize =nSampleWidth/8;
  323.   if ( nSampleWidth != pFormat->uBitsPerSample )
  324.     {
  325.       ((HXAudioFormat*)pFormat)->uBitsPerSample = nSampleWidth;
  326.     }
  327.   // Set sample rate. If the exact rate is not supported
  328.   // by the hardware, use nearest possible rate.         
  329.   unsigned int rrate = (unsigned int) nSampleRate;
  330.   if(( err = snd_pcm_hw_params_set_rate_near( pcm_handle, hwparams, &rrate, 0) ) < 0 )
  331.     {
  332. #ifdef _DEBUG
  333.       fprintf(stderr,
  334.               "The rate %d Hz is not supported by your hardware.n==> Using %d Hz instead.n",
  335.               nSampleRate, rrate);
  336. #endif
  337.       return ( m_wLastError = RA_AOE_NOTENABLED );
  338.     }
  339.   m_unSampleRate = rrate;
  340.   if ( rrate != pFormat->ulSamplesPerSec )
  341.     {
  342.       ((HXAudioFormat*)pFormat)->ulSamplesPerSec = rrate;
  343.     }
  344.   if (snd_pcm_hw_params_set_channels( pcm_handle, hwparams, numChannels) < 0) {
  345. #ifdef _DEBUG
  346.     fprintf(stderr, "Error setting channels.n");
  347. #endif
  348.     return ( m_wLastError = RA_AOE_NOTENABLED );
  349.   }
  350.   m_unNumChannels = channels = numChannels;
  351.   if ( numChannels != pFormat->uChannels )
  352.     {
  353.       ((HXAudioFormat*)pFormat)->uChannels = numChannels;
  354.     }
  355.   int periods = 2;       // Number of periods 
  356.   snd_pcm_uframes_t periodsize = 2048; // FIXME
  357.   int direct=0;
  358.   snd_pcm_uframes_t period = 0;
  359.   // The period size == fragment size.
  360.   // Note that this in frames (frame = nr_channels * sample_width)
  361.   // So a value of 1024 means 4096 bytes (1024 x 2 x 16-bits)
  362.         
  363.   //     if((err = snd_pcm_hw_params_get_period_size_max( hwparams, &period,& direct) ) < 0)
  364.   //       {
  365.   // #ifdef _DEBUG
  366.   //           printf("Unable to get period size %ld : %sn", period,  snd_strerror(err));
  367.   // #endif
  368.   //       }
  369.   //     direct=0;
  370.   if((err = snd_pcm_hw_params_set_period_size_near( pcm_handle, hwparams, &periodsize, &direct) ) < 0)
  371.     {
  372. #ifdef _DEBUG
  373.       printf("Set period size failed %ld : %sn", periodsize,  snd_strerror(err));
  374. #endif
  375.     }
  376.   //////////////////////////////////// set periods
  377.   //      printf("set periodsn");
  378.   //      if ((err = snd_pcm_hw_params_set_periods_near( pcm_handle, hwparams, &periods, &direct ))  < 0 )
  379.   //        {
  380.   //            printf("Error setting periods  %sn", snd_strerror(err));
  381.   //        }
  382.  
  383.   /*     if(( err =snd_pcm_hw_params_get_period_size( hwparams, &period_size, NULL)) < 0)
  384.          {
  385.          printf("unable to get peroidsize:n") ;
  386.          }
  387.   */  
  388.   //     snd_pcm_hw_params_get_period_size( hwparams, &val);
  389.   //     if((err = snd_pcm_hw_params_set_buffer_size( pcm_handle, hwparams, bufferSz )) < 0)
  390.   //      if((err = snd_pcm_hw_params_set_buffer_size_near( pcm_handle, hwparams, bufferSz )) < 0)
  391.   //        {
  392.   //            printf("Unable to set buffer size %li : %sn", bufferSz, snd_strerror(err));
  393.   //        }
  394.   //     printf("get buffer sizen");
  395.   ////////////////////////// Get buffer size in frames 
  396.   //  m_wBlockSize = m_ulBytesPerGran;
  397.   if((err = snd_pcm_hw_params_get_buffer_size_max( hwparams, &bufferSz)) < 0) 
  398.     {
  399. #ifdef _DEBUG
  400.       printf("Get buffer size failed:n") ;
  401. #endif
  402.       if(  m_ulDeviceBufferSize <= 0)
  403.         blocksize = m_wBlockSize = m_ulDeviceBufferSize = 8192 * 2;
  404.     }
  405.   else
  406.     {
  407.       blocksize = bufferSz;
  408.       m_wBlockSize = blocksize;
  409.       m_ulDeviceBufferSize  = blocksize *  samplesize * channels;
  410.     }
  411.   //    printf("blocksize %d, samplesize %d, channels %dn", blocksize, samplesize, channels );
  412.   //    printf("m_ulDeviceBufferSize %ldn",  m_ulDeviceBufferSize);
  413.   
  414.   if (snd_pcm_hw_params( pcm_handle, hwparams) < 0) //write device
  415.     {
  416. #ifdef _DEBUG
  417.       fprintf(stderr, "Error setting HW params.n");
  418. #endif
  419.       return ( m_wLastError = RA_AOE_NOTENABLED );
  420.     }
  421.   snd_pcm_sw_params_alloca(&swparams);
  422.   if((err = snd_pcm_sw_params_current( pcm_handle, swparams)) <0)
  423.     {
  424. #ifdef _DEBUG
  425.       printf("Current SW params failed: %sn", snd_strerror(err));
  426. #endif
  427.     }
  428.   //  if ((err = snd_pcm_sw_params_set_avail_min ( pcm_handle, swparams, 2048)) < 0) {
  429.   //       fprintf (stderr, "cannot set minimum available count (%s)n",
  430.   //                snd_strerror (err));
  431.   //   }
  432.   if((err = snd_pcm_sw_params_set_start_threshold( pcm_handle, swparams, bufferSz )) < 0)
  433.     {
  434. #ifdef _DEBUG
  435.       printf("Set start threshold mode failed: %sn", snd_strerror(err));
  436. #endif
  437.     }
  438.   if((err = snd_pcm_sw_params_set_xfer_align( pcm_handle, swparams, 1)) < 0)
  439.     {
  440. #ifdef _DEBUG
  441.       printf("Set transfer align failed: %sn", snd_strerror(err));
  442. #endif
  443.     } 
  444.   if((err = snd_pcm_sw_params_set_tstamp_mode( pcm_handle, swparams,SND_PCM_TSTAMP_MMAP )) < 0 )
  445.     {
  446. #ifdef _DEBUG
  447.       printf("Set sw params time stamp mode failed: %sn", snd_strerror(err));
  448. #endif
  449.     }
  450.   if((err = snd_pcm_sw_params( pcm_handle, swparams)) < 0)
  451.     {
  452. #ifdef _DEBUG
  453.       printf("Set sw params failed: %sn", snd_strerror(err));
  454. #endif
  455.     }
  456.   if ((err=snd_pcm_prepare ( pcm_handle)) < 0)
  457.     {
  458. #ifdef _DEBUG
  459.       fprintf (stderr, "Prepare audio interface failed %sn",
  460.                snd_strerror (err));
  461. #endif
  462.       return  retCode = RA_AOE_NOTENABLED;
  463.     }       
  464. #ifdef _DEBUG
  465.   snd_output_t *output = NULL;
  466.   snd_output_stdio_attach(&output, stdout, 0);
  467.   printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nn");
  468.   snd_pcm_dump( pcm_handle, output);
  469.   printf("n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nn");
  470.   snd_pcm_hw_params_dump( hwparams, output);
  471.   printf("n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nn");
  472.   snd_pcm_sw_params_dump( swparams, output); 
  473.   printf("n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nn");
  474.   //     snd_pcm_status_dump( status, output);
  475.   //     printf("n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nn");
  476.   fprintf( stdout, "Device Configured:n");
  477.   fprintf( stdout, "         Sample Rate: %dn",  m_unSampleRate);
  478.   fprintf( stdout, "        Sample Width: %dn",  nSampleWidth);
  479.   fprintf( stdout , "        Num channels: %dn",  m_unNumChannels);
  480.   fprintf( stdout, "          Block size: %dn",  m_wBlockSize);
  481.   fprintf( stdout, "  Device buffer size: %lun", m_ulDeviceBufferSize);
  482. #endif
  483.   //     snd_pcm_sw_params_free( swparams);
  484.   return RA_AOE_NOERR;
  485. }
  486. void CAudioOutLinuxAlsa::_SyncUpTimeStamps(ULONG32 writeCount)
  487. {
  488.   snd_pcm_hwsync( pcm_handle);
  489.   int bytes2  = 0;
  490.   int err = 0;
  491.   snd_pcm_sframes_t framedelay;
  492.   if((err = snd_pcm_delay( pcm_handle, &framedelay)) < 0)
  493.     {
  494. #ifdef _DEBUG
  495.       fprintf (stderr, "cannot get delay %sn", snd_strerror (err));
  496. #endif
  497.     }
  498.   bytes2 = snd_pcm_frames_to_bytes( pcm_handle, framedelay);
  499.              
  500.   if( bytes2 > 0)
  501.     {
  502.       m_ulLastBytesPlayed = (UINT64)( m_ulTotalWritten + writeCount - bytes2);
  503.       m_ulLastTimeStamp   = GetTickCount();
  504.     }
  505. }
  506. //Device specific method to write bytes out to the audiodevice and return a
  507. //count of bytes written.
  508. HX_RESULT CAudioOutLinuxAlsa::_WriteBytes( UCHAR* buffer, ULONG32 ulBuffLength, LONG32& lCount )
  509. {
  510.   LONG32 writeCount;
  511.   HX_RESULT retCode = RA_AOE_NOERR;
  512.   int err=0;
  513.   if( m_ulTickCount == 0 )
  514.     m_ulTickCount = GetTickCount();
  515.   snd_pcm_sframes_t num_frames, size;
  516.   lCount = 0;
  517.   num_frames = snd_pcm_bytes_to_frames( pcm_handle, ulBuffLength); //alsa in frames
  518.   // Returns the number of frames actually written. 
  519.   size = snd_pcm_writei( pcm_handle, buffer, num_frames );
  520.   if(size < 0)
  521.     {
  522.       switch (size)
  523.         {
  524.         case -EBADFD:
  525.           {
  526. #ifdef _DEBUG
  527.             printf("EBADFD: Device not in the right state n");
  528. #endif
  529.             retCode = RA_AOE_DEVBUSY;
  530.           }
  531.           break;
  532.         case -EPIPE: //lets handle underruns
  533.           {
  534.             if(( size = snd_pcm_prepare( pcm_handle) ) < 0 )
  535.               {
  536. #ifdef _DEBUG
  537.                 printf("EPIPE: Recovery from underrun is difficult: %sn", snd_strerror( size));
  538. #endif
  539.                 retCode = RA_AOE_DEVBUSY;
  540.               }
  541.           }
  542.           break;
  543.         case -ESTRPIPE:
  544.           {
  545.             while (( size = snd_pcm_resume( pcm_handle)) == -EAGAIN)
  546.               sleep(1);
  547.             if ( size < 0)
  548.               {
  549.                 size = snd_pcm_prepare( pcm_handle);
  550.                 if ( size < 0)
  551.                   {
  552. #ifdef _DEBUG
  553.                     printf("ESTRPIPE: Recover from suspend is difficult: %sn", snd_strerror( size));
  554. #endif
  555.                     retCode = RA_AOE_DEVBUSY;
  556.                   }
  557.               }
  558.           }
  559.           break;
  560.         };
  561.     }
  562. //  printf("frames written = %dn", size);
  563.   writeCount = snd_pcm_frames_to_bytes( pcm_handle, size );
  564.   if( writeCount < 0 )
  565.     {
  566.       if( errno == EAGAIN )
  567.         retCode = RA_AOE_NOERR;
  568.       if( errno == EINTR )
  569.         retCode = RA_AOE_DEVBUSY;
  570.     }
  571.   else
  572.     {
  573.       _SyncUpTimeStamps( writeCount);
  574.       // XXXRGG: Figure out why writeCount != ulBuffLength
  575.       // lCount = writeCount;
  576.       lCount = ulBuffLength;
  577.     }
  578.   return retCode;
  579. }
  580. UINT64 CAudioOutLinuxAlsa::_GetBytesActualyPlayed(void) const
  581. {
  582.   // Get current playback position in device DMA. 
  583.   int     bytes2 = 0;
  584.   UINT64  ulTheAnswer = 0;
  585.   if( m_ulTotalWritten > 0 )
  586.     {
  587.       HX_ASSERT( m_unSampleRate!=0 && m_uSampFrameSize!=0 );
  588.       ULONG32 ulTick = GetTickCount();
  589.       //We need to update the timestamps every so often.
  590.       //This make sure that if the XServer was blocked, and
  591.       //we ran dry, that we re-sync up.
  592.       if( (ulTick - m_ulLastTimeStamp) > 200 )
  593.         {
  594.           ((CAudioOutLinuxAlsa*)this)->_SyncUpTimeStamps();
  595.           ulTick = GetTickCount();
  596.         }
  597.       ulTheAnswer = (UINT64)(m_ulLastBytesPlayed+
  598.                              ((float)(ulTick-m_ulLastTimeStamp)*
  599.                               (float)m_unNumChannels/1000.0*
  600.                               m_unSampleRate*m_uSampFrameSize) +0.5 );
  601.     }
  602.   //    printf("BytesActualyPlayed %ldn", ulTheAnswer);
  603.   return  ulTheAnswer;
  604. }
  605. //this must return the number of bytes that can be written without blocking.
  606. HX_RESULT CAudioOutLinuxAlsa::_GetRoomOnDevice(ULONG32& ulBytes) const
  607. {
  608.   HX_RESULT  retCode = RA_AOE_NOERR;
  609.   // FIXME
  610.   ulBytes = m_ulDeviceBufferSize - ( m_ulTotalWritten - _GetBytesActualyPlayed() );
  611.   //    printf("RoomOnDevice %ldn", ulBytes);
  612.   m_wLastError = retCode;
  613.   return m_wLastError;
  614. }
  615. //Device specific method to get/set the devices current volume.
  616. UINT16 CAudioOutLinuxAlsa::_GetVolume() const
  617. {
  618.   UINT16 nRetVolume   = 0;
  619.   long pmin = 0, pmax = 0;
  620.   long percentage = 0;
  621.   
  622.   long frontLeftVolume = 0;
  623.   long frontRightVolume = 0;
  624.   
  625.   /*  long frontCenterVolume = 0;
  626.   long rearLeftVolume = 0;
  627.   long rearRightVolume = 0;
  628.   
  629.   long wooferVolume = 0;
  630.   snd_mixer_selem_id_t *sid;
  631.   snd_mixer_selem_id_alloca(&sid);
  632.   */
  633.   snd_mixer_elem_t *mixer_elements2;
  634.   mixer_elements2 = snd_mixer_first_elem( mixer_handle);
  635.   // grab first elem of mixer
  636.   if (snd_mixer_selem_has_playback_volume( mixer_elements2)) {
  637.             
  638.     if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  639.                                              SND_MIXER_SCHN_FRONT_LEFT, &frontLeftVolume) < 0)
  640.       frontLeftVolume = 0;
  641.            
  642.     if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  643.                                              SND_MIXER_SCHN_FRONT_RIGHT, &frontRightVolume) < 0)
  644.       frontRightVolume = 0;
  645.   }
  646.   /*    snd_mixer_selem_get_playback_volume_range( mixer_elements2, &pmin, &pmax);
  647.         int range = pmax - pmin;
  648.         int val = frontLeftVolume;
  649.         frontRightVolume =  frontLeftVolume;
  650.         val -= pmin;
  651.         //    printf("min %d, max %d, range %d, val %dn", pmin, pmax, range, val );
  652.         percentage = (long)rint((double)val / (double)range * 100);
  653.   */      
  654.   //    printf("fleft %ld, fright %ld, percent %dn",
  655.   //           frontLeftVolume, frontRightVolume, percentage);
  656.   // Save for future use, demonstrates multichannel mixer access.
  657.   
  658.   //   for ( mixer_elements2 = snd_mixer_first_elem( mixer_handle);
  659.   //         mixer_elements2;
  660.   //         mixer_elements2 = snd_mixer_elem_next( mixer_elements2) )  {
  661.     
  662.   //     snd_mixer_selem_get_id( mixer_elements2, sid);
  663.     
  664.   // //     if ( !snd_mixer_selem_is_active( mixer_elements2))
  665.   // //       continue;
  666.     
  667.   //     printf("mixer control '%s',%in", 
  668.   //     snd_mixer_selem_id_get_name( sid), 
  669.   //     snd_mixer_selem_id_get_index( sid));
  670.   
  671.   //        snd_mixer_selem_get_playback_volume_range( mixer_elements2, &pmin, &pmax);
  672.   //         // if its got mono mixer
  673.   //        if ( snd_mixer_selem_has_common_volume( mixer_elements2)) //mono
  674.   //      {
  675.   //            // check for playback vol mixer         
  676.   //          if (snd_mixer_selem_has_playback_volume( mixer_elements2)) {
  677.   //                //score!        
  678.   // //           if( snd_mixer_find_selem( mixer_handle,mixer_elements2) == SND_MIXER_SCHN_MONO)
  679.   // //             {
  680.   //              snd_mixer_selem_get_playback_volume( mixer_elements2,
  681.   //                                                   SND_MIXER_SCHN_MONO, &frontLeftVolume);
  682.   //                //for finding percentage
  683.                 
  684.   //              int range = pmax - pmin;
  685.   //              int val = frontLeftVolume;
  686.   //              frontRightVolume =  frontLeftVolume;
  687.   //              val -= pmin;
  688.   //              percentage = (long)rint((double)val / (double)range * 100);
  689.   //              printf("common min %d, max %d, range %dn", pmin, pmax, range);
  690.    
  691.   //          }
  692.   //      }
  693.   //        else //stereo
  694.   //       {
  695.   // //          printf("get seperate r and l volumesn");       
  696.   //           if (snd_mixer_selem_has_playback_volume( mixer_elements2)) {
  697.             
  698.   //               if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  699.   //                                                        SND_MIXER_SCHN_FRONT_LEFT, &frontLeftVolume) < 0)
  700.   //                   frontLeftVolume = 0;
  701.            
  702.   //               if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  703.   //                                                        SND_MIXER_SCHN_FRONT_RIGHT, &frontRightVolume) < 0)
  704.   //                   frontRightVolume = 0;
  705.               
  706.   // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  707.   // //                                                        SND_MIXER_SCHN_FRONT_CENTER, &frontCenterVolume) < 0)
  708.   // //                   frontCenterVolume = 0;
  709.            
  710.   // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  711.   // //                                                        SND_MIXER_SCHN_REAR_RIGHT, &rearRightVolume) < 0)
  712.   // //                   rearRightVolume = 0;
  713.               
  714.   // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  715.   // //                                                        SND_MIXER_SCHN_REAR_LEFT, &rearLeftVolume) < 0)
  716.   // //                   rearLeftVolume = 0;
  717.            
  718.   // //               if( snd_mixer_selem_get_playback_volume( mixer_elements2,
  719.   // //                                                        SND_MIXER_SCHN_WOOFER, &wooferVolume) < 0)
  720.   // //                   wooferVolume = 0;
  721.                 
  722.   //               long range = pmax - pmin;
  723.   //               long val = frontLeftVolume;
  724.   //               frontRightVolume =  frontLeftVolume;
  725.   //               val -= pmin;
  726.   //               percentage = (long)rint(val / range * 100);
  727.         
  728.   //                 //            printf("min %d, max %d, range %dn", pmin, pmax, range);
  729.   //               printf("fleft %ld, fright %ld, center %ld, rleft %ld, rright %ld, woofer %ld, percent %dn",
  730.   //                      frontLeftVolume, frontRightVolume, frontCenterVolume, rearLeftVolume,
  731.   //                      rearRightVolume, percentage);
  732.   //           }
  733.   // //         snd_mixer_selem_get_playback_volume_range( mixer_elements2, &pmin, &pmax);
  734.   //       }
  735.   //     }
  736.   
  737.   //    nLeftVolume  = (nVolume & 0x000000ff); 
  738.   //    nRightVolume = (nVolume & 0x0000ff00) >> 8;
  739.   nRetVolume  = (UINT16)frontLeftVolume;
  740.   //    printf("nRetVolume is %ldn",nRetVolume);
  741.   return nRetVolume;
  742. }
  743. HX_RESULT CAudioOutLinuxAlsa::_SetVolume(UINT16 unVolume)
  744. {
  745.   HX_RESULT retCode = RA_AOE_NOERR;
  746.   ///////////////////////// FIXME: 
  747.   long pmin = 0, pmax = 0;
  748.   long percentage = 0;
  749.   
  750.   long frontLeftVolume = 0;
  751.   long frontRightVolume = 0;
  752.   
  753.   /*  long frontCenterVolume = 0;
  754.   long rearLeftVolume = 0;
  755.   long rearRightVolume = 0;
  756.   
  757.   long wooferVolume = 0;
  758.   snd_mixer_selem_id_t *sid;
  759.   snd_mixer_selem_id_alloca(&sid);
  760.   */
  761.   snd_mixer_elem_t *mixer_elements;
  762.   frontLeftVolume = frontRightVolume = unVolume;
  763.   //    nNewVolume = (unVolume & 0xff) | ((unVolume &0xff) << 8);
  764.   mixer_elements = snd_mixer_first_elem( mixer_handle); //just grab first elem for now
  765.   if (snd_mixer_selem_has_playback_volume( mixer_elements)) {
  766.     // do this as the volume range in alsa might be more or less than 100
  767.     // act like its a percentage, the vol range for rmedigi96 is 16383, and my pci128 is 16
  768.     snd_mixer_selem_get_playback_volume_range( mixer_elements, &pmin, &pmax);
  769.     int range = pmax - pmin;
  770.     int val = frontLeftVolume;
  771.     //percent to volume
  772.     frontLeftVolume = rint((double)range * ((double)val*.01)) + pmin; 
  773.   
  774.     frontRightVolume = frontLeftVolume;
  775.   
  776.     if( snd_mixer_selem_set_playback_volume( mixer_elements,
  777.                                              SND_MIXER_SCHN_FRONT_LEFT,
  778.                                              frontLeftVolume) < 0)
  779.       frontLeftVolume = 0;
  780.     if( snd_mixer_selem_set_playback_volume( mixer_elements,
  781.                                              SND_MIXER_SCHN_FRONT_RIGHT,
  782.                                              frontRightVolume) < 0)
  783.       frontRightVolume = 0;
  784.   }              
  785. #ifdef _DEBUG
  786.   printf("setting volume left %ld, right %ld %ld%n", frontLeftVolume, frontRightVolume, unVolume );
  787. #endif
  788.   // Save for future use, demonstrates multichannel mixer access.
  789.  
  790.   //     for ( mixer_elements = snd_mixer_first_elem( mixer_handle); //ramble through elems
  791.   //           mixer_elements; 
  792.   //           mixer_elements = snd_mixer_elem_next( mixer_elements) )
  793.   //       {
  794.     
  795.   //           snd_mixer_selem_get_id( mixer_elements, sid);
  796.     
  797.   //           printf("mixer control '%s',%in", 
  798.   //                  snd_mixer_selem_id_get_name( sid), 
  799.   //                  snd_mixer_selem_id_get_index( sid));
  800.   //           if ( snd_mixer_selem_has_common_volume( mixer_elements)) //is mono
  801.   //             {
  802.   // //          // check for playback vol mixer         
  803.   //                 if (snd_mixer_selem_has_playback_volume( mixer_elements)) {
  804.   //                     snd_mixer_selem_get_playback_volume_range( mixer_elements, &pmin, &pmax);
  805.   //                     int range = pmax - pmin;
  806.   //                     int val = frontLeftVolume;
  807.   //                     val -= pmin;
  808.   //                     frontLeftVolume  =  rint((double)range * ((double)val*.01)) + pmin; //percent to volume
  809.   //                       //frontLeftVolume  = rint((double)val/(double)range * 100);
  810.   //                       // frontLeftVolume = (long) ( unVolume & 0x000000ff);
  811.   //                     frontRightVolume = frontLeftVolume;
  812.   //                       //  frontRightVolume = (long) ( unVolume & 0x0000ff00);
  813.   //                       //score!       
  814.   //                     if( snd_mixer_selem_set_playback_volume( mixer_elements,
  815.   //                                                              SND_MIXER_SCHN_MONO,
  816.   //                                                              frontLeftVolume) < 0)
  817.   //                         frontLeftVolume = 0;
  818.   //                     val -= pmin;
  819.   //                     percentage = (long)rint((double)val/(double)range * 100);
  820.   //                 }
  821.   //             }
  822.   //           else //is stereo
  823.   //             {
  824.   // //        if (snd_mixer_selem_has_playback_volume( mixer_elements)) {
  825.   //                 snd_mixer_selem_get_playback_volume_range( mixer_elements, &pmin, &pmax);
  826.   //                 printf("vol min %d, max %dn", pmin, pmax);
  827.   //                 int range = pmax - pmin;
  828.   //                 int tmp;
  829.   //                 int val = frontLeftVolume;
  830.   //                 frontLeftVolume  =  rint((double)range * ((double)val*.01)) + pmin; //percent to volume
  831.   //                   //rint((double)val/(double)range * 100);
  832.   
  833.   // //  frontLeftVolume = (long) ( unVolume & 0x000000ff);
  834.   //                 frontRightVolume = frontLeftVolume;
  835.   // //  frontRightVolume = (long) ( unVolume & 0x0000ff00);
  836.   
  837.   //                 if( snd_mixer_selem_set_playback_volume( mixer_elements,
  838.   //                                                          SND_MIXER_SCHN_FRONT_LEFT,
  839.   //                                                          frontLeftVolume) < 0)
  840.   //                     frontLeftVolume = 0;
  841.   //                 if( snd_mixer_selem_set_playback_volume( mixer_elements,
  842.   //                                                          SND_MIXER_SCHN_FRONT_RIGHT,
  843.   //                                                          frontRightVolume) < 0)
  844.   //                     frontRightVolume = 0;
  845.               
  846.   // //              if( snd_mixer_selem_set_playback_volume( mixer_elements,
  847.   // //                SND_MIXER_SCHN_FRONT_CENTER, &frontCenterVolume) < 0)
  848.   // //               frontCenterVolume = 0;
  849.            
  850.   // //               if( snd_mixer_selem_set_playback_volume( mixer_elements,
  851.   // //                  SND_MIXER_SCHN_REAR_RIGHT, &rearRightVolume) < 0)
  852.   // //                  rearRightVolume = 0;
  853.               
  854.   // //              if( snd_mixer_selem_set_playback_volume( mixer_elements,
  855.   // //                SND_MIXER_SCHN_REAR_LEFT, &rearLeftVolume) < 0)
  856.   // //               rearLeftVolume = 0;
  857.            
  858.   // //               if( snd_mixer_selem_set_playback_volume( mixer_elements,
  859.   // //                    SND_MIXER_SCHN_WOOFER, &wooferVolume) < 0)
  860.   // //                   wooferVolume = 0;
  861.   // //        }
  862.   //                 int range2 = pmax - pmin;
  863.   //                 int val2 = frontLeftVolume;
  864.   //                 val2 -= pmin;
  865.   //                 percentage = (long)rint((double)val2/(double)range2 * 100);
  866.         
  867.   //             }
  868.   //       }
  869.   //     printf("fleft %d, fright %d, center %d, rleft %d, rright %d, woofer %d, percent %dn",
  870.   //            frontLeftVolume, frontRightVolume, frontCenterVolume, rearLeftVolume, 
  871.   //            rearRightVolume, percentage);
  872.   m_wLastError = retCode;
  873.   return m_wLastError;
  874. }
  875. //Device specific method to drain a device. This should play the remaining
  876. //bytes in the devices buffer and then return.
  877. HX_RESULT CAudioOutLinuxAlsa::_Drain()
  878. {
  879.   HX_RESULT retCode = RA_AOE_NOERR;
  880.   if ( pcm_handle < 0 )
  881.     {
  882.       retCode = RA_AOE_DEVNOTOPEN;
  883.     }
  884.   // Stop PCM device after pending frames have been played
  885.   if( snd_pcm_drain( pcm_handle) < 0)
  886.     retCode = RA_AOE_GENERAL;
  887.     
  888.   m_wLastError = retCode;
  889.   return m_wLastError;
  890. }
  891. //Device specific method to reset device and return it to a state that it
  892. //can accept new sample rates, num channels, etc.
  893. HX_RESULT CAudioOutLinuxAlsa::_Reset()
  894. {
  895.   HX_RESULT retCode = RA_AOE_NOERR;
  896.   m_ulPausePosition = 0;
  897.   if( pcm_handle > 0 )
  898.     //    if (snd_pcm_state( pcm_handle) == SND_PCM_STATE_OPEN)
  899.     {
  900.       if( snd_pcm_reset( pcm_handle) < 0)
  901.         {
  902.           retCode = RA_AOE_GENERAL;
  903.         }
  904.     }
  905.   else
  906.     {
  907.       retCode = RA_AOE_DEVNOTOPEN;
  908.     }
  909.   m_wLastError = retCode;
  910.   return m_wLastError;
  911. }
  912. HX_RESULT CAudioOutLinuxAlsa::_CheckFormat( const HXAudioFormat* pFormat )
  913. {
  914.   int   nBitsPerSample = pFormat->uBitsPerSample;
  915.   int   ulTmp          = pFormat->ulSamplesPerSec;
  916.   int   nNumChannels   = pFormat->uChannels;
  917.   float fTmp           = 0.0;
  918.   int err=0;
  919.   snd_pcm_format_t m_format;
  920.   HX_RESULT retCode = RA_AOE_NOERR;
  921.   if( nBitsPerSample = 16)
  922.     {
  923.       m_format = SND_PCM_FORMAT_S16;
  924.     }
  925.   else
  926.     {
  927.       m_format =  SND_PCM_FORMAT_U8;
  928.     }
  929.   //Is the device already open?
  930.   if( pcm_handle > 0 ||  RA_AOE_NOERR != _OpenAudio() )
  931.     {
  932.       retCode = RA_AOE_DEVBUSY;
  933.       return retCode;
  934.     }
  935.   //See if the sample rate is supported.
  936.   if( snd_pcm_hw_params_test_rate ( pcm_handle, hwparams, ulTmp  ,0) < 0)
  937.     {
  938. #ifdef _DEBUG
  939.       fprintf (stderr, "cannot set sample rate (%s)n",
  940.                snd_strerror (err));
  941. #endif
  942.       //Not quite the real error, but it is what we need to return.
  943.       retCode = RA_AOE_BADFORMAT;
  944.       goto donechecking;
  945.     }
  946.   //Check num channels.
  947.   if(snd_pcm_hw_params_test_channels ( pcm_handle, hwparams, nNumChannels) < 0)
  948.     {
  949.       retCode = RA_AOE_BADFORMAT;
  950.       goto donechecking;
  951.     }
  952.   if ( snd_pcm_hw_params_test_format( pcm_handle, hwparams, m_format  /*SND_PCM_FORMAT_S16*/ ) < 0)
  953.     {
  954.       retCode = RA_AOE_BADFORMAT;
  955.       goto donechecking;
  956.     }
  957.   //Close the audio device.
  958.  donechecking:
  959.   _CloseAudio();
  960.   m_wLastError = retCode;
  961.   return retCode;
  962. }
  963. HX_RESULT CAudioOutLinuxAlsa::_CheckSampleRate( ULONG32 ulSampleRate )
  964. {
  965.   ULONG32 ulTmp = ulSampleRate;
  966.   m_wLastError = RA_AOE_NOERR;
  967.   //Is the device already open?
  968.   if( pcm_handle > 0 || RA_AOE_NOERR != _OpenAudio() )
  969.     {
  970.       m_wLastError = RA_AOE_DEVBUSY;
  971.     }
  972.   else
  973.     {
  974.       //See if the sample rate is supported.
  975.       if( snd_pcm_hw_params_test_rate ( pcm_handle, hwparams, (uint)ulTmp, 0) != 1)
  976.         {
  977.           //Not quite the real error, but it is what we need to return.
  978.           m_wLastError = RA_AOE_DEVBUSY;
  979.         }
  980.       else if( ulSampleRate != ulTmp )
  981.         {
  982.           //It is NOT supported
  983.           m_wLastError = RA_AOE_BADFORMAT;
  984.         }
  985.       _CloseAudio();
  986.     }
  987.   return m_wLastError;
  988. }
  989. HX_RESULT CAudioOutLinuxAlsa::_Pause()
  990. {
  991.   m_wLastError = HXR_OK;
  992.   m_ulPausePosition = m_ulTotalWritten;
  993.   m_ulTickCount = 0;
  994.   m_ulLastTimeStamp   = 0;
  995.   int err=0;
  996.   if( m_bHasHardwarePause)
  997.     {
  998.       if((err = snd_pcm_pause( pcm_handle, 1)) < 0)
  999.         {
  1000. #ifdef _DEBUG
  1001.           fprintf(stderr, "Error : cannot pause (%s)n", snd_strerror (err));
  1002. #endif
  1003.           m_wLastError = RA_AOE_NOTSUPPORTED;
  1004.         }
  1005.     }
  1006.   else
  1007.     {
  1008.       m_wLastError = RA_AOE_NOTSUPPORTED;
  1009.     }
  1010.   return m_wLastError;
  1011. }
  1012. HX_RESULT CAudioOutLinuxAlsa::_Resume()
  1013. {
  1014.   m_wLastError = HXR_OK;
  1015.   if( m_ulTotalWritten > 0 )
  1016.     {
  1017.       m_ulTickCount = GetTickCount();
  1018.       m_ulLastTimeStamp = m_ulTickCount;
  1019.     }
  1020.   if( m_bHasHardwarePause &&  m_bHasHardwareResume)
  1021.     {
  1022.       int err=0;
  1023.       if((err = snd_pcm_resume( pcm_handle)) < 0)
  1024.         {
  1025. #ifdef _DEBUG
  1026.           fprintf (stderr, "Error: cannot resume (%s)n",
  1027.                    snd_strerror (err));
  1028. #endif
  1029.           m_wLastError = RA_AOE_NOTSUPPORTED;
  1030.         }
  1031.     }
  1032.   else
  1033.     {
  1034.       m_wLastError = RA_AOE_NOTSUPPORTED;
  1035.     }
  1036.     
  1037.   return m_wLastError;
  1038. }
  1039. BOOL CAudioOutLinuxAlsa::_HardwarePauseSupported() const
  1040. {
  1041.     
  1042.   if( snd_pcm_hw_params_can_resume( hwparams) == 1)
  1043.     {
  1044.       m_bHasHardwareResume = true;
  1045.     }
  1046.   else
  1047.     {
  1048.       m_bHasHardwareResume = false;
  1049.     }
  1050.   if( snd_pcm_hw_params_can_pause( hwparams) == 1)
  1051.     {
  1052.       m_bHasHardwarePause = true;
  1053.       return TRUE;
  1054.     }
  1055.   else
  1056.     {
  1057.       m_bHasHardwarePause = false;
  1058.     }
  1059.   return FALSE;
  1060. }