audlinux_alsa.cpp
Upload User: dangjiwu
Upload Date: 2013-07-19
Package Size: 42019k
Code Size: 38k
Category:

Symbian

Development Platform:

Visual C++

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