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; }