📄 audlinux_alsa.cpp
字号:
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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -