Alsa9Buf::Alsa9Buf( hw hardware, int channels_ )
{
	GetSoundCardDebugInfo();
		
	InitializeErrorHandler();
	
	channels = channels_;
	samplerate = 44100;
	samplebits = 16;
	last_cursor_pos = 0;
	samplerate_set_explicitly = false;
	preferred_writeahead = 8192;
	preferred_chunksize = 1024;

	/* Open the device. */
	int err;
	err = dsnd_pcm_open( &pcm, DeviceName(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
	if (err < 0)
		RageException::ThrowNonfatal("dsnd_pcm_open(%s): %s", DeviceName().c_str(), dsnd_strerror(err));

	if( !SetHWParams() )
	{
		CHECKPOINT;
		dsnd_pcm_close(pcm);
		CHECKPOINT;
		RageException::ThrowNonfatal( "SetHWParams failed" );
	}

	SetSWParams();
}
Exemple #2
0
/* Don't fill the buffer any more than than "writeahead" frames.  Prefer to
 * write "chunksize" frames at a time.  (These numbers are hints; if the
 * hardware parameters require it, they can be ignored.) */
int Alsa9Buf::GetNumFramesToFill()
{
	/* Make sure we can write ahead at least two chunks.  Otherwise, we'll only
	 * fill one chunk ahead, and underrun. */
	int ActualWriteahead = max( writeahead, chunksize*2 );

	snd_pcm_sframes_t avail_frames = dsnd_pcm_avail_update(pcm);
	
	int total_frames = writeahead;
	if( avail_frames > total_frames )
	{
		/* underrun */
		const int size = avail_frames-total_frames;
		LOG->Trace("underrun (%i frames)", size);
		int large_skip_threshold = 2 * samplerate;

		/* For small underruns, ignore them.  We'll return the maximum writeahead and ALSA will
		 * just discard the data.  GetPosition will return consistent values during this time,
		 * so arrows will continue to scroll smoothly until the music catches up. */
		if( size >= large_skip_threshold )
		{
			/* It's a large skip.  Catch up.  If we fall too far behind, the sound thread will
			 * be decoding as fast as it can, which will steal too many cycles from the rendering
			 * thread. */
			dsnd_pcm_forward( pcm, size );
		}
	}
	
	if( avail_frames < 0 )
		avail_frames = dsnd_pcm_avail_update(pcm);

	if( avail_frames < 0 )
	{
		LOG->Trace( "RageSoundDriver_ALSA9::GetData: dsnd_pcm_avail_update: %s", dsnd_strerror(avail_frames) );
		return 0;
	}

	/* Number of frames that have data: */
	const snd_pcm_sframes_t filled_frames = max( 0l, total_frames - avail_frames );

	/* Number of frames that don't have data, that are within the writeahead: */
	snd_pcm_sframes_t unfilled_frames = clamp( ActualWriteahead - filled_frames, 0l, (snd_pcm_sframes_t)ActualWriteahead );

//	LOG->Trace( "total_fr: %i; avail_fr: %i; filled_fr: %i; ActualWr %i; chunksize %i; unfilled_frames %i ",
//			total_frames, avail_frames, filled_frames, ActualWriteahead, chunksize, unfilled_frames );

	/* If we have less than a chunk empty, don't fill at all.  Otherwise, we'll
	 * spend a lot of CPU filling in partial chunks, instead of waiting for some
	 * sound to play and then filling a whole chunk at once. */
	if( unfilled_frames < (int) chunksize )
		return 0;

	return chunksize;
}
void Alsa9Buf::Write( const int16_t *buffer, int frames )
{
	/* We should be able to write it all.  If we don't, treat it as an error. */
	int wrote = dsnd_pcm_mmap_writei( pcm, (const char *) buffer, frames );
	if( wrote < 0 )
	{
		LOG->Trace( "RageSoundDriver_ALSA9::GetData: dsnd_pcm_mmap_writei: %s (%i)", dsnd_strerror(wrote), wrote );
		return;
	}

	last_cursor_pos += wrote;
	if( wrote < frames )
		LOG->Trace("Couldn't write whole buffer? (%i < %i)", wrote, frames );
}
Exemple #4
0
void Alsa9Buf::ErrorHandler(const char *file, int line, const char *function, int err, const char *fmt, ...)
{
	va_list va;
	va_start( va, fmt );
	RString str = vssprintf(fmt, va);
	va_end( va );

	if( err )
		str += ssprintf( " (%s)", dsnd_strerror(err) );

	/* Annoying: these happen both normally (eg. "out of memory" when allocating too many PCM
	 * slots) and abnormally, and there's no way to tell which is which.  I don't want to
	 * pollute the warning output. */
	LOG->Trace( "ALSA error: %s:%i %s: %s", file, line, function, str.c_str() );
}
Exemple #5
0
RString Alsa9Buf::Init( int channels_,
		int iWriteahead,
		int iChunkSize,
		int iSampleRate )
{
	channels = channels_;
	preferred_writeahead = iWriteahead;
	preferred_chunksize = iChunkSize;
	if( iSampleRate == 0 )
		samplerate = 44100;
	else
		samplerate = iSampleRate;
	
	GetSoundCardDebugInfo();
		
	InitializeErrorHandler();
	
	/* Open the device. */
	int err;
	err = dsnd_pcm_open( &pcm, DeviceName(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
	if( err < 0 )
		return ssprintf( "dsnd_pcm_open(%s): %s", DeviceName().c_str(), dsnd_strerror(err) );

	if( !SetHWParams() )
	{
		CHECKPOINT;
		return "SetHWParams failed";
	}

	SetSWParams();

	LOG->Info( "ALSA: Mixing at %ihz", samplerate );

	if( preferred_writeahead != writeahead )
		LOG->Info( "ALSA: writeahead adjusted from %u to %u", (unsigned) preferred_writeahead, (unsigned) writeahead );
	if( preferred_chunksize != chunksize )
		LOG->Info( "ALSA: chunksize adjusted from %u to %u", (unsigned) preferred_chunksize, (unsigned) chunksize );

	return "";
}
Exemple #6
0
RString Alsa9Buf::GetHardwareID( RString name )
{
	InitializeErrorHandler();

	if( name.empty() )
		name = DeviceName();
	
	snd_ctl_t *handle;
	int err;
	err = dsnd_ctl_open( &handle, name, 0 );
	if ( err < 0 )
	{
		LOG->Info( "Couldn't open card \"%s\" to get ID: %s", name.c_str(), dsnd_strerror(err) );
		return "???";
	}

	snd_ctl_card_info_t *info;
	dsnd_ctl_card_info_alloca(&info);
	err = dsnd_ctl_card_info( handle, info );
	RString ret = dsnd_ctl_card_info_get_id( info );
	dsnd_ctl_close(handle);

	return ret;
}
Exemple #7
0
void Alsa9Buf::GetSoundCardDebugInfo()
{
	static bool done = false;	
	if( done )
		return;
	done = true;

	if( DoesFileExist("/rootfs/proc/asound/version") )
	{
		RString sVersion;
		GetFileContents( "/rootfs/proc/asound/version", sVersion, true );
		LOG->Info( "ALSA: %s", sVersion.c_str() );
	}

	InitializeErrorHandler();

	int card = -1;
	while( dsnd_card_next( &card ) >= 0 && card >= 0 )
	{
		const RString id = ssprintf( "hw:%d", card );
		snd_ctl_t *handle;
		int err;
		err = dsnd_ctl_open( &handle, id, 0 );
		if ( err < 0 )
		{
			LOG->Info( "Couldn't open card #%i (\"%s\") to probe: %s", card, id.c_str(), dsnd_strerror(err) );
			continue;
		}

		snd_ctl_card_info_t *info;
		dsnd_ctl_card_info_alloca(&info);
		err = dsnd_ctl_card_info( handle, info );
		if ( err < 0 )
		{
			LOG->Info( "Couldn't get card info for card #%i (\"%s\"): %s", card, id.c_str(), dsnd_strerror(err) );
			dsnd_ctl_close( handle );
			continue;
		}

		int dev = -1;
		while ( dsnd_ctl_pcm_next_device( handle, &dev ) >= 0 && dev >= 0 )
		{
			snd_pcm_info_t *pcminfo;
			dsnd_pcm_info_alloca(&pcminfo);
			dsnd_pcm_info_set_device(pcminfo, dev);
			dsnd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);

			err = dsnd_ctl_pcm_info(handle, pcminfo);
			if ( err < 0 )
			{
				if (err != -ENOENT)
					LOG->Info("dsnd_ctl_pcm_info(%i) (%s) failed: %s", card, id.c_str(), dsnd_strerror(err));
				continue;
			}

			LOG->Info( "ALSA Driver: %i: %s [%s], device %i: %s [%s], %i/%i subdevices avail",
					card, dsnd_ctl_card_info_get_name(info), dsnd_ctl_card_info_get_id(info), dev,
					dsnd_pcm_info_get_id(pcminfo), dsnd_pcm_info_get_name(pcminfo),
					dsnd_pcm_info_get_subdevices_avail(pcminfo),
					dsnd_pcm_info_get_subdevices_count(pcminfo) );

		}
		dsnd_ctl_close(handle);
	}

	if( card == 0 )
		LOG->Info( "No ALSA sound cards were found.");
	
	if( !PREFSMAN->m_iSoundDevice.Get().empty() )
		LOG->Info( "ALSA device overridden to \"%s\"", PREFSMAN->m_iSoundDevice.Get().c_str() );
}