📄 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 <ljp@llornkcor.com>
*
* ***** END LICENSE BLOCK ***** */
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ioctl.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <math.h>
#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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -