Integrate C++11 branch into 5_1-new

This commit is contained in:
teejusb
2019-06-22 12:35:38 -07:00
444 changed files with 19503 additions and 21007 deletions
+7 -7
View File
@@ -6,13 +6,13 @@
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
static void *Handle = NULL;
static void *Handle = nullptr;
#include "RageUtil.h"
#include "ALSA9Dynamic.h"
/* foo_f dfoo = NULL */
#define FUNC(ret, name, proto) name##_f d##name = NULL
/* foo_f dfoo = nullptr */
#define FUNC(ret, name, proto) name##_f d##name = nullptr
#include "ALSA9Functions.h"
#undef FUNC
@@ -31,10 +31,10 @@ RString LoadALSA()
if( !IsADirectory("/rootfs/proc/asound/") )
return "/proc/asound/ does not exist";
ASSERT( Handle == NULL );
ASSERT( Handle == nullptr );
Handle = dlopen( lib, RTLD_NOW );
if( Handle == NULL )
if( Handle == nullptr )
return ssprintf("dlopen(%s): %s", lib.c_str(), dlerror());
RString error;
@@ -62,8 +62,8 @@ void UnloadALSA()
{
if( Handle )
dlclose( Handle );
Handle = NULL;
#define FUNC(ret, name, proto) d##name = NULL;
Handle = nullptr;
#define FUNC(ret, name, proto) d##name = nullptr;
#include "ALSA9Functions.h"
#undef FUNC
}
+462 -462
View File
@@ -1,462 +1,462 @@
#include "global.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "ALSA9Helpers.h"
#include "ALSA9Dynamic.h"
#include "PrefsManager.h"
/* int err; must be defined before using this macro */
#define ALSA_CHECK(x) \
if ( err < 0 ) { LOG->Info("ALSA: %s: %s", x, dsnd_strerror(err)); return false; }
#define ALSA_ASSERT(x) \
if (err < 0) { LOG->Warn("ALSA: %s: %s", x, dsnd_strerror(err)); }
bool Alsa9Buf::SetHWParams()
{
int err;
if( dsnd_pcm_state(pcm) == SND_PCM_STATE_PREPARED )
dsnd_pcm_drop( pcm );
if( dsnd_pcm_state(pcm) != SND_PCM_STATE_OPEN )
{
/* Reset the stream to SND_PCM_STATE_OPEN. */
err = dsnd_pcm_hw_free( pcm );
ALSA_ASSERT("dsnd_pcm_hw_free");
}
// ASSERT_M( dsnd_pcm_state(pcm) == SND_PCM_STATE_OPEN, ssprintf("(%s)", dsnd_pcm_state_name(dsnd_pcm_state(pcm))) );
/* allocate the hardware parameters structure */
snd_pcm_hw_params_t *hwparams;
dsnd_pcm_hw_params_alloca( &hwparams );
err = dsnd_pcm_hw_params_any(pcm, hwparams);
ALSA_CHECK("dsnd_pcm_hw_params_any");
/* Set to interleaved mmap mode. */
err = dsnd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED);
ALSA_CHECK("dsnd_pcm_hw_params_set_access");
/* Set the PCM format: signed 16bit, native endian. */
err = dsnd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16);
ALSA_CHECK("dsnd_pcm_hw_params_set_format");
/* Set the number of channels. */
err = dsnd_pcm_hw_params_set_channels(pcm, hwparams, 2);
ALSA_CHECK("dsnd_pcm_hw_params_set_channels");
/* Set the sample rate. */
err = dsnd_pcm_hw_params_set_rate_near(pcm, hwparams, &samplerate, 0);
ALSA_CHECK("dsnd_pcm_hw_params_set_rate_near");
/* Set the buffersize to the writeahead, and then copy back the actual value
* we got. */
writeahead = preferred_writeahead;
err = dsnd_pcm_hw_params_set_buffer_size_near( pcm, hwparams, &writeahead );
ALSA_CHECK("dsnd_pcm_hw_params_set_buffer_size_near");
/* The period size is roughly equivalent to what we call the chunksize. */
int dir = 0;
chunksize = preferred_chunksize;
err = dsnd_pcm_hw_params_set_period_size_near( pcm, hwparams, &chunksize, &dir );
ALSA_CHECK("dsnd_pcm_hw_params_set_period_size_near");
// LOG->Info("asked for %i period, got %i", chunksize, period_size);
/* write the hardware parameters to the device */
err = dsnd_pcm_hw_params( pcm, hwparams );
ALSA_CHECK("dsnd_pcm_hw_params");
return true;
}
bool Alsa9Buf::SetSWParams()
{
snd_pcm_sw_params_t *swparams;
dsnd_pcm_sw_params_alloca( &swparams );
dsnd_pcm_sw_params_current( pcm, swparams );
int err = dsnd_pcm_sw_params_set_xfer_align( pcm, swparams, 1 );
ALSA_ASSERT("dsnd_pcm_sw_params_set_xfer_align");
/* chunksize has been set to the period size. Set avail_min to the period
* size, too, so poll() wakes up once per chunk. */
err = dsnd_pcm_sw_params_set_avail_min( pcm, swparams, chunksize );
ALSA_ASSERT("dsnd_pcm_sw_params_set_avail_min");
/* If this fails, we might have bound dsnd_pcm_sw_params_set_avail_min to
* the old SW API. */
// ASSERT( err <= 0 );
/* Disable SND_PCM_STATE_XRUN. */
snd_pcm_uframes_t boundary = 0;
err = dsnd_pcm_sw_params_get_boundary( swparams, &boundary );
ALSA_ASSERT("dsnd_pcm_sw_params_get_boundary");
err = dsnd_pcm_sw_params_set_stop_threshold( pcm, swparams, boundary );
ALSA_ASSERT("dsnd_pcm_sw_params_set_stop_threshold");
err = dsnd_pcm_sw_params(pcm, swparams);
ALSA_ASSERT("dsnd_pcm_sw_params");
err = dsnd_pcm_prepare(pcm);
ALSA_ASSERT("dsnd_pcm_prepare");
return true;
}
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() );
}
void Alsa9Buf::InitializeErrorHandler()
{
dsnd_lib_error_set_handler( ErrorHandler );
}
static RString DeviceName()
{
if( !PREFSMAN->m_iSoundDevice.Get().empty() )
return PREFSMAN->m_iSoundDevice;
return "default";
}
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() );
}
Alsa9Buf::Alsa9Buf()
{
samplerate = 44100;
samplebits = 16;
last_cursor_pos = 0;
preferred_writeahead = 8192;
preferred_chunksize = 1024;
pcm = NULL;
}
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 "";
}
Alsa9Buf::~Alsa9Buf()
{
if( pcm != NULL )
dsnd_pcm_close( pcm );
}
/* 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;
}
bool Alsa9Buf::WaitUntilFramesCanBeFilled( int timeout_ms )
{
int err = dsnd_pcm_wait( pcm, timeout_ms );
/* EINTR is normal; don't warn. */
if( err == -EINTR )
return false;
ALSA_ASSERT("snd_pcm_wait");
return err == 1;
}
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;
do
{
wrote = dsnd_pcm_mmap_writei( pcm, (const char *) buffer, frames );
}
while( wrote == -EAGAIN );
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 );
}
/*
* When the play buffer underruns, subsequent writes to the buffer
* return -EPIPE. When this happens, call Recover() to restart playback.
*/
bool Alsa9Buf::Recover( int r )
{
if( r == -EPIPE )
{
LOG->Trace("RageSound_ALSA9::Recover (prepare)");
int err = dsnd_pcm_prepare(pcm);
ALSA_ASSERT("dsnd_pcm_prepare (Recover)");
return true;
}
if( r == -ESTRPIPE )
{
LOG->Trace("RageSound_ALSA9::Recover (resume)");
int err;
while ((err = dsnd_pcm_resume(pcm)) == -EAGAIN)
usleep(10000); // 10ms
ALSA_ASSERT("dsnd_pcm_resume (Recover)");
return true;
}
return false;
}
int64_t Alsa9Buf::GetPosition() const
{
if( dsnd_pcm_state(pcm) == SND_PCM_STATE_PREPARED )
return last_cursor_pos;
dsnd_pcm_hwsync( pcm );
/* delay is returned in frames */
snd_pcm_sframes_t delay;
int err = dsnd_pcm_delay( pcm, &delay );
ALSA_ASSERT("dsnd_pcm_delay");
return last_cursor_pos - delay;
}
void Alsa9Buf::Play()
{
/* NOP. It'll start playing when it gets some data. */
}
void Alsa9Buf::Stop()
{
dsnd_pcm_drop( pcm );
dsnd_pcm_prepare( pcm );
last_cursor_pos = 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;
}
/*
* (c) 2002-2004 Glenn Maynard, Aaron VonderHaar
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "ALSA9Helpers.h"
#include "ALSA9Dynamic.h"
#include "PrefsManager.h"
/* int err; must be defined before using this macro */
#define ALSA_CHECK(x) \
if ( err < 0 ) { LOG->Info("ALSA: %s: %s", x, dsnd_strerror(err)); return false; }
#define ALSA_ASSERT(x) \
if (err < 0) { LOG->Warn("ALSA: %s: %s", x, dsnd_strerror(err)); }
bool Alsa9Buf::SetHWParams()
{
int err;
if( dsnd_pcm_state(pcm) == SND_PCM_STATE_PREPARED )
dsnd_pcm_drop( pcm );
if( dsnd_pcm_state(pcm) != SND_PCM_STATE_OPEN )
{
/* Reset the stream to SND_PCM_STATE_OPEN. */
err = dsnd_pcm_hw_free( pcm );
ALSA_ASSERT("dsnd_pcm_hw_free");
}
// ASSERT_M( dsnd_pcm_state(pcm) == SND_PCM_STATE_OPEN, ssprintf("(%s)", dsnd_pcm_state_name(dsnd_pcm_state(pcm))) );
/* allocate the hardware parameters structure */
snd_pcm_hw_params_t *hwparams;
dsnd_pcm_hw_params_alloca( &hwparams );
err = dsnd_pcm_hw_params_any(pcm, hwparams);
ALSA_CHECK("dsnd_pcm_hw_params_any");
/* Set to interleaved mmap mode. */
err = dsnd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED);
ALSA_CHECK("dsnd_pcm_hw_params_set_access");
/* Set the PCM format: signed 16bit, native endian. */
err = dsnd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16);
ALSA_CHECK("dsnd_pcm_hw_params_set_format");
/* Set the number of channels. */
err = dsnd_pcm_hw_params_set_channels(pcm, hwparams, 2);
ALSA_CHECK("dsnd_pcm_hw_params_set_channels");
/* Set the sample rate. */
err = dsnd_pcm_hw_params_set_rate_near(pcm, hwparams, &samplerate, 0);
ALSA_CHECK("dsnd_pcm_hw_params_set_rate_near");
/* Set the buffersize to the writeahead, and then copy back the actual value
* we got. */
writeahead = preferred_writeahead;
err = dsnd_pcm_hw_params_set_buffer_size_near( pcm, hwparams, &writeahead );
ALSA_CHECK("dsnd_pcm_hw_params_set_buffer_size_near");
/* The period size is roughly equivalent to what we call the chunksize. */
int dir = 0;
chunksize = preferred_chunksize;
err = dsnd_pcm_hw_params_set_period_size_near( pcm, hwparams, &chunksize, &dir );
ALSA_CHECK("dsnd_pcm_hw_params_set_period_size_near");
// LOG->Info("asked for %i period, got %i", chunksize, period_size);
/* write the hardware parameters to the device */
err = dsnd_pcm_hw_params( pcm, hwparams );
ALSA_CHECK("dsnd_pcm_hw_params");
return true;
}
bool Alsa9Buf::SetSWParams()
{
snd_pcm_sw_params_t *swparams;
dsnd_pcm_sw_params_alloca( &swparams );
dsnd_pcm_sw_params_current( pcm, swparams );
int err = dsnd_pcm_sw_params_set_xfer_align( pcm, swparams, 1 );
ALSA_ASSERT("dsnd_pcm_sw_params_set_xfer_align");
/* chunksize has been set to the period size. Set avail_min to the period
* size, too, so poll() wakes up once per chunk. */
err = dsnd_pcm_sw_params_set_avail_min( pcm, swparams, chunksize );
ALSA_ASSERT("dsnd_pcm_sw_params_set_avail_min");
/* If this fails, we might have bound dsnd_pcm_sw_params_set_avail_min to
* the old SW API. */
// ASSERT( err <= 0 );
/* Disable SND_PCM_STATE_XRUN. */
snd_pcm_uframes_t boundary = 0;
err = dsnd_pcm_sw_params_get_boundary( swparams, &boundary );
ALSA_ASSERT("dsnd_pcm_sw_params_get_boundary");
err = dsnd_pcm_sw_params_set_stop_threshold( pcm, swparams, boundary );
ALSA_ASSERT("dsnd_pcm_sw_params_set_stop_threshold");
err = dsnd_pcm_sw_params(pcm, swparams);
ALSA_ASSERT("dsnd_pcm_sw_params");
err = dsnd_pcm_prepare(pcm);
ALSA_ASSERT("dsnd_pcm_prepare");
return true;
}
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() );
}
void Alsa9Buf::InitializeErrorHandler()
{
dsnd_lib_error_set_handler( ErrorHandler );
}
static RString DeviceName()
{
if( !PREFSMAN->m_iSoundDevice.Get().empty() )
return PREFSMAN->m_iSoundDevice;
return "default";
}
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() );
}
Alsa9Buf::Alsa9Buf()
{
samplerate = 44100;
samplebits = 16;
last_cursor_pos = 0;
preferred_writeahead = 8192;
preferred_chunksize = 1024;
pcm = nullptr;
}
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 "";
}
Alsa9Buf::~Alsa9Buf()
{
if( pcm != nullptr )
dsnd_pcm_close( pcm );
}
/* 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;
}
bool Alsa9Buf::WaitUntilFramesCanBeFilled( int timeout_ms )
{
int err = dsnd_pcm_wait( pcm, timeout_ms );
/* EINTR is normal; don't warn. */
if( err == -EINTR )
return false;
ALSA_ASSERT("snd_pcm_wait");
return err == 1;
}
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;
do
{
wrote = dsnd_pcm_mmap_writei( pcm, (const char *) buffer, frames );
}
while( wrote == -EAGAIN );
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 );
}
/*
* When the play buffer underruns, subsequent writes to the buffer
* return -EPIPE. When this happens, call Recover() to restart playback.
*/
bool Alsa9Buf::Recover( int r )
{
if( r == -EPIPE )
{
LOG->Trace("RageSound_ALSA9::Recover (prepare)");
int err = dsnd_pcm_prepare(pcm);
ALSA_ASSERT("dsnd_pcm_prepare (Recover)");
return true;
}
if( r == -ESTRPIPE )
{
LOG->Trace("RageSound_ALSA9::Recover (resume)");
int err;
while ((err = dsnd_pcm_resume(pcm)) == -EAGAIN)
usleep(10000); // 10ms
ALSA_ASSERT("dsnd_pcm_resume (Recover)");
return true;
}
return false;
}
int64_t Alsa9Buf::GetPosition() const
{
if( dsnd_pcm_state(pcm) == SND_PCM_STATE_PREPARED )
return last_cursor_pos;
dsnd_pcm_hwsync( pcm );
/* delay is returned in frames */
snd_pcm_sframes_t delay;
int err = dsnd_pcm_delay( pcm, &delay );
ALSA_ASSERT("dsnd_pcm_delay");
return last_cursor_pos - delay;
}
void Alsa9Buf::Play()
{
/* NOP. It'll start playing when it gets some data. */
}
void Alsa9Buf::Stop()
{
dsnd_pcm_drop( pcm );
dsnd_pcm_prepare( pcm );
last_cursor_pos = 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;
}
/*
* (c) 2002-2004 Glenn Maynard, Aaron VonderHaar
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+10 -10
View File
@@ -43,10 +43,10 @@ void DSound::SetPrimaryBufferMode()
format.dwSize = sizeof(format);
format.dwFlags = DSBCAPS_PRIMARYBUFFER;
format.dwBufferBytes = 0;
format.lpwfxFormat = NULL;
format.lpwfxFormat = nullptr;
IDirectSoundBuffer *pBuffer;
HRESULT hr = this->GetDS()->CreateSoundBuffer( &format, &pBuffer, NULL );
HRESULT hr = this->GetDS()->CreateSoundBuffer( &format, &pBuffer, nullptr );
if( FAILED(hr) )
{
LOG->Warn(hr_ssprintf(hr, "Couldn't create primary buffer"));
@@ -98,15 +98,15 @@ void DSound::SetPrimaryBufferMode()
DSound::DSound()
{
HRESULT hr;
if( FAILED( hr = CoInitialize(NULL) ) )
if( FAILED( hr = CoInitialize(nullptr) ) )
RageException::Throw( hr_ssprintf(hr, "CoInitialize") );
m_pDS = NULL;
m_pDS = nullptr;
}
RString DSound::Init()
{
HRESULT hr;
if( FAILED( hr = DirectSoundCreate(NULL, &m_pDS, NULL) ) )
if( FAILED( hr = DirectSoundCreate(nullptr, &m_pDS, nullptr) ) )
return hr_ssprintf( hr, "DirectSoundCreate" );
static bool bShownInfo = false;
@@ -139,7 +139,7 @@ RString DSound::Init()
DSound::~DSound()
{
if( m_pDS != NULL )
if( m_pDS != nullptr )
m_pDS->Release();
CoUninitialize();
}
@@ -163,8 +163,8 @@ bool DSound::IsEmulated() const
DSoundBuf::DSoundBuf()
{
m_pBuffer = NULL;
m_pTempBuffer = NULL;
m_pBuffer = nullptr;
m_pTempBuffer = nullptr;
}
RString DSoundBuf::Init( DSound &ds, DSoundBuf::hw hardware,
@@ -236,7 +236,7 @@ RString DSoundBuf::Init( DSound &ds, DSoundBuf::hw hardware,
format.lpwfxFormat = &waveformat;
HRESULT hr = ds.GetDS()->CreateSoundBuffer( &format, &m_pBuffer, NULL );
HRESULT hr = ds.GetDS()->CreateSoundBuffer( &format, &m_pBuffer, nullptr );
if( FAILED(hr) )
return hr_ssprintf( hr, "CreateSoundBuffer failed (%i hz)", m_iSampleBits );
@@ -318,7 +318,7 @@ static bool contained( int iStart, int iEnd, int iPos )
DSoundBuf::~DSoundBuf()
{
if( m_pBuffer != NULL )
if( m_pBuffer != nullptr )
m_pBuffer->Release();
delete [] m_pTempBuffer;
}
+10 -9
View File
@@ -3,7 +3,7 @@
#include "RageSoundManager.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "Foreach.h"
#include "arch/arch_default.h"
DriverList RageSoundDriver::m_pDriverList;
@@ -44,28 +44,29 @@ RageSoundDriver *RageSoundDriver::Create( const RString& drivers )
}
}
FOREACH_CONST( RString, drivers_to_try, Driver )
for (RString const &Driver : drivers_to_try)
{
RageDriver *pDriver = m_pDriverList.Create( *Driver );
if( pDriver == NULL )
RageDriver *pDriver = m_pDriverList.Create( Driver );
char const *driverString = Driver.c_str();
if( pDriver == nullptr )
{
LOG->Trace( "Unknown sound driver: %s", Driver->c_str() );
LOG->Trace( "Unknown sound driver: %s", driverString );
continue;
}
RageSoundDriver *pRet = dynamic_cast<RageSoundDriver *>( pDriver );
ASSERT( pRet != NULL );
ASSERT( pRet != nullptr );
const RString sError = pRet->Init();
if( sError.empty() )
{
LOG->Info( "Sound driver: %s", Driver->c_str() );
LOG->Info( "Sound driver: %s", driverString );
return pRet;
}
LOG->Info( "Couldn't load driver %s: %s", Driver->c_str(), sError.c_str() );
LOG->Info( "Couldn't load driver %s: %s", driverString, sError.c_str() );
SAFE_DELETE( pRet );
}
return NULL;
return nullptr;
}
RString RageSoundDriver::GetDefaultSoundDriverList()
@@ -53,12 +53,12 @@ bool RageSoundDriver_ALSA9_Software::GetData()
if( frames_to_fill <= 0 )
return false;
static int16_t *buf = NULL;
static int16_t *buf = nullptr;
static int bufsize = 0;
if( buf && bufsize < frames_to_fill )
{
delete[] buf;
buf = NULL;
buf = nullptr;
}
if( !buf )
{
@@ -89,7 +89,7 @@ void RageSoundDriver_ALSA9_Software::SetupDecodingThread()
RageSoundDriver_ALSA9_Software::RageSoundDriver_ALSA9_Software()
{
m_pPCM = NULL;
m_pPCM = nullptr;
m_bShutdown = false;
}
+14 -14
View File
@@ -40,8 +40,8 @@ static inline RString FourCCToString( uint32_t num )
return s;
}
RageSoundDriver_AU::RageSoundDriver_AU() : m_OutputUnit(NULL), m_iSampleRate(0), m_bDone(false), m_bStarted(false),
m_pIOThread(NULL), m_pNotificationThread(NULL), m_Semaphore("Sound")
RageSoundDriver_AU::RageSoundDriver_AU() : m_OutputUnit(nullptr), m_iSampleRate(0), m_bDone(false), m_bStarted(false),
m_pIOThread(nullptr), m_pNotificationThread(nullptr), m_Semaphore("Sound")
{
}
@@ -80,7 +80,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
kAudioObjectPropertyElementWildcard
};
if( (error = AudioObjectGetPropertyData(OutputDevice, &AvailableRatesAddr, 0, NULL, &size, NULL)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &AvailableRatesAddr, 0, nullptr, &size, nullptr)) )
{
LOG->Warn( WERROR("Couldn't get available nominal sample rates info", error) );
return;
@@ -113,7 +113,7 @@ static void SetSampleRate( AudioUnit au, Float64 desiredRate )
if( bestRate == 0.0 )
return;
if( (error = AudioObjectSetPropertyData(OutputDevice, &RateAddr, 0, NULL, sizeof(Float64), &bestRate)) )
if( (error = AudioObjectSetPropertyData(OutputDevice, &RateAddr, 0, nullptr, sizeof(Float64), &bestRate)) )
{
LOG->Warn( WERROR("Couldn't set the device's sample rate", error) );
}
@@ -131,12 +131,12 @@ RString RageSoundDriver_AU::Init()
Component comp = FindNextComponent( NULL, &desc );
if( comp == NULL )
if( comp == nullptr )
return "Failed to find the default output unit.";
OSStatus error = OpenAComponent( comp, &m_OutputUnit );
if( error != noErr || m_OutputUnit == NULL )
if( error != noErr || m_OutputUnit == nullptr )
return ERROR( "Could not open the default output unit", error );
// Set up a callback function to generate output to the output unit
@@ -255,7 +255,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
};
size = sizeof( Float64 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &RateAddr, 0, NULL, &size, &sampleRate)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &RateAddr, 0, nullptr, &size, &sampleRate)) )
{
LOG->Warn( WERROR("Couldn't get the device sample rate", error) );
return 0.0f;
@@ -268,7 +268,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
};
size = sizeof( UInt32 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &BufferAddr, 0, NULL, &size, &bufferSize)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &BufferAddr, 0, nullptr, &size, &bufferSize)) )
{
LOG->Warn( WERROR("Couldn't determine buffer size", error) );
bufferSize = 0;
@@ -283,7 +283,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
};
size = sizeof( UInt32 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &LatencyAddr, 0, NULL, &size, &frames)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &LatencyAddr, 0, nullptr, &size, &frames)) )
{
LOG->Warn( WERROR( "Couldn't get device latency", error) );
frames = 0;
@@ -297,7 +297,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
bufferSize += frames;
size = sizeof( UInt32 );
if( (error = AudioObjectGetPropertyData(OutputDevice, &SafetyAddr, 0, NULL, &size, &frames)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &SafetyAddr, 0, nullptr, &size, &frames)) )
{
LOG->Warn( WERROR("Couldn't get device safety offset", error) );
frames = 0;
@@ -312,7 +312,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
kAudioObjectPropertyElementWildcard
};
if( (error = AudioObjectGetPropertyData(OutputDevice, &StreamsAddr, 0, NULL, &size, NULL)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &StreamsAddr, 0, nullptr, &size, nullptr)) )
{
LOG->Warn( WERROR("Device has no streams", error) );
break;
@@ -325,7 +325,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
}
AudioStreamID *streams = new AudioStreamID[num];
if( (error = AudioObjectGetPropertyData(OutputDevice, &StreamsAddr, 0, NULL, &size, streams)) )
if( (error = AudioObjectGetPropertyData(OutputDevice, &StreamsAddr, 0, nullptr, &size, streams)) )
{
LOG->Warn( WERROR("Cannot get device's streams", error) );
delete[] streams;
@@ -338,7 +338,7 @@ float RageSoundDriver_AU::GetPlayLatency() const
kAudioObjectPropertyElementWildcard
};
if( (error = AudioObjectGetPropertyData(streams[0], &LatencyAddr, 0, NULL, &size, &frames)) )
if( (error = AudioObjectGetPropertyData(streams[0], &LatencyAddr, 0, nullptr, &size, &frames)) )
{
LOG->Warn( WERROR("Stream does not report latency", error) );
frames = 0;
@@ -360,7 +360,7 @@ OSStatus RageSoundDriver_AU::Render( void *inRefCon,
{
RageSoundDriver_AU *This = (RageSoundDriver_AU *)inRefCon;
if( unlikely(This->m_pIOThread == NULL) )
if( unlikely(This->m_pIOThread == nullptr) )
This->m_pIOThread = new RageThreadRegister( "HAL I/O thread" );
AudioBuffer &buf = ioData->mBuffers[0];
+169 -169
View File
@@ -1,169 +1,169 @@
#include "global.h"
#include "RageSoundDriver_DSound_Software.h"
#include "DSoundHelpers.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageSoundManager.h"
#include "PrefsManager.h"
#include "archutils/Win32/ErrorStrings.h"
REGISTER_SOUND_DRIVER_CLASS2( DirectSound-sw, DSound_Software );
static const int channels = 2;
static const int bytes_per_frame = channels*2; /* 16-bit */
static const int safe_writeahead = 1024*4; /* in frames */
static int g_iMaxWriteahead;
/* We'll fill the buffer in chunks this big. */
static const int num_chunks = 8;
static int chunksize() { return g_iMaxWriteahead / num_chunks; }
void RageSoundDriver_DSound_Software::MixerThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) )
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn(werr_ssprintf(GetLastError(), "Failed to set sound thread priority"));
/* Fill a buffer before we start playing, so we don't play whatever junk is
* in the buffer. */
char *locked_buf;
unsigned len;
while( m_pPCM->get_output_buf(&locked_buf, &len, chunksize()) )
{
memset( locked_buf, 0, len );
m_pPCM->release_output_buf(locked_buf, len);
}
/* Start playing. */
m_pPCM->Play();
while( !m_bShutdownMixerThread )
{
char *pLockedBuf;
unsigned iLen;
const int64_t iPlayPos = m_pPCM->GetOutputPosition(); /* must be called before get_output_buf */
if( !m_pPCM->get_output_buf(&pLockedBuf, &iLen, chunksize()) )
{
Sleep( chunksize()*1000 / m_iSampleRate );
continue;
}
this->Mix( (int16_t *) pLockedBuf, iLen/bytes_per_frame, iPlayPos, m_pPCM->GetPosition() );
m_pPCM->release_output_buf( pLockedBuf, iLen );
}
/* I'm not sure why, but if we don't stop the stream now, then the thread will take
* 90ms (our buffer size) longer to close. */
m_pPCM->Stop();
}
int64_t RageSoundDriver_DSound_Software::GetPosition() const
{
return m_pPCM->GetPosition();
}
int RageSoundDriver_DSound_Software::MixerThread_start(void *p)
{
((RageSoundDriver_DSound_Software *) p)->MixerThread();
return 0;
}
RageSoundDriver_DSound_Software::RageSoundDriver_DSound_Software()
{
m_bShutdownMixerThread = false;
m_pPCM = NULL;
}
RString RageSoundDriver_DSound_Software::Init()
{
RString sError = ds.Init();
if( sError != "" )
return sError;
/* If we're emulated, we're better off with the WaveOut driver; DS
* emulation tends to be desynced. */
if( ds.IsEmulated() )
return "Driver unusable (emulated device)";
g_iMaxWriteahead = safe_writeahead;
if( PREFSMAN->m_iSoundWriteAhead )
g_iMaxWriteahead = PREFSMAN->m_iSoundWriteAhead;
/* Create a DirectSound stream, but don't force it into hardware. */
m_pPCM = new DSoundBuf;
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
if( m_iSampleRate == 0 )
m_iSampleRate = 44100;
sError = m_pPCM->Init( ds, DSoundBuf::HW_DONT_CARE, channels, m_iSampleRate, 16, g_iMaxWriteahead );
if( sError != "" )
return sError;
LOG->Info( "Software mixing at %i hz", m_iSampleRate );
StartDecodeThread();
m_MixingThread.SetName("Mixer thread");
m_MixingThread.Create( MixerThread_start, this );
return RString();
}
RageSoundDriver_DSound_Software::~RageSoundDriver_DSound_Software()
{
/* Signal the mixing thread to quit. */
if( m_MixingThread.IsCreated() )
{
m_bShutdownMixerThread = true;
LOG->Trace("Shutting down mixer thread ...");
LOG->Flush();
m_MixingThread.Wait();
LOG->Trace("Mixer thread shut down.");
LOG->Flush();
}
delete m_pPCM;
}
void RageSoundDriver_DSound_Software::SetupDecodingThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set decoding thread priority") );
}
float RageSoundDriver_DSound_Software::GetPlayLatency() const
{
return (1.0f / m_iSampleRate) * g_iMaxWriteahead;
}
int RageSoundDriver_DSound_Software::GetSampleRate() const
{
return m_iSampleRate;
}
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "RageSoundDriver_DSound_Software.h"
#include "DSoundHelpers.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageSoundManager.h"
#include "PrefsManager.h"
#include "archutils/Win32/ErrorStrings.h"
REGISTER_SOUND_DRIVER_CLASS2( DirectSound-sw, DSound_Software );
static const int channels = 2;
static const int bytes_per_frame = channels*2; /* 16-bit */
static const int safe_writeahead = 1024*4; /* in frames */
static int g_iMaxWriteahead;
/* We'll fill the buffer in chunks this big. */
static const int num_chunks = 8;
static int chunksize() { return g_iMaxWriteahead / num_chunks; }
void RageSoundDriver_DSound_Software::MixerThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) )
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn(werr_ssprintf(GetLastError(), "Failed to set sound thread priority"));
/* Fill a buffer before we start playing, so we don't play whatever junk is
* in the buffer. */
char *locked_buf;
unsigned len;
while( m_pPCM->get_output_buf(&locked_buf, &len, chunksize()) )
{
memset( locked_buf, 0, len );
m_pPCM->release_output_buf(locked_buf, len);
}
/* Start playing. */
m_pPCM->Play();
while( !m_bShutdownMixerThread )
{
char *pLockedBuf;
unsigned iLen;
const int64_t iPlayPos = m_pPCM->GetOutputPosition(); /* must be called before get_output_buf */
if( !m_pPCM->get_output_buf(&pLockedBuf, &iLen, chunksize()) )
{
Sleep( chunksize()*1000 / m_iSampleRate );
continue;
}
this->Mix( (int16_t *) pLockedBuf, iLen/bytes_per_frame, iPlayPos, m_pPCM->GetPosition() );
m_pPCM->release_output_buf( pLockedBuf, iLen );
}
/* I'm not sure why, but if we don't stop the stream now, then the thread will take
* 90ms (our buffer size) longer to close. */
m_pPCM->Stop();
}
int64_t RageSoundDriver_DSound_Software::GetPosition() const
{
return m_pPCM->GetPosition();
}
int RageSoundDriver_DSound_Software::MixerThread_start(void *p)
{
((RageSoundDriver_DSound_Software *) p)->MixerThread();
return 0;
}
RageSoundDriver_DSound_Software::RageSoundDriver_DSound_Software()
{
m_bShutdownMixerThread = false;
m_pPCM = nullptr;
}
RString RageSoundDriver_DSound_Software::Init()
{
RString sError = ds.Init();
if( sError != "" )
return sError;
/* If we're emulated, we're better off with the WaveOut driver; DS
* emulation tends to be desynced. */
if( ds.IsEmulated() )
return "Driver unusable (emulated device)";
g_iMaxWriteahead = safe_writeahead;
if( PREFSMAN->m_iSoundWriteAhead )
g_iMaxWriteahead = PREFSMAN->m_iSoundWriteAhead;
/* Create a DirectSound stream, but don't force it into hardware. */
m_pPCM = new DSoundBuf;
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
if( m_iSampleRate == 0 )
m_iSampleRate = 44100;
sError = m_pPCM->Init( ds, DSoundBuf::HW_DONT_CARE, channels, m_iSampleRate, 16, g_iMaxWriteahead );
if( sError != "" )
return sError;
LOG->Info( "Software mixing at %i hz", m_iSampleRate );
StartDecodeThread();
m_MixingThread.SetName("Mixer thread");
m_MixingThread.Create( MixerThread_start, this );
return RString();
}
RageSoundDriver_DSound_Software::~RageSoundDriver_DSound_Software()
{
/* Signal the mixing thread to quit. */
if( m_MixingThread.IsCreated() )
{
m_bShutdownMixerThread = true;
LOG->Trace("Shutting down mixer thread ...");
LOG->Flush();
m_MixingThread.Wait();
LOG->Trace("Mixer thread shut down.");
LOG->Flush();
}
delete m_pPCM;
}
void RageSoundDriver_DSound_Software::SetupDecodingThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set decoding thread priority") );
}
float RageSoundDriver_DSound_Software::GetPlayLatency() const
{
return (1.0f / m_iSampleRate) * g_iMaxWriteahead;
}
int RageSoundDriver_DSound_Software::GetSampleRate() const
{
return m_iSampleRate;
}
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
@@ -18,7 +18,7 @@ static int underruns = 0, logged_underruns = 0;
RageSoundDriver::Sound::Sound()
{
m_pSound = NULL;
m_pSound = nullptr;
m_State = AVAILABLE;
m_bPaused = false;
}
@@ -257,7 +257,7 @@ void RageSoundDriver::Update()
while( m_Sounds[i].m_PosMapQueue.read( &p, 1 ) )
{
RageSoundBase *pSound = m_Sounds[i].m_pSound;
if( pSound != NULL )
if( pSound != nullptr )
pSound->CommitPlayingPosition( p.iStreamFrame, p.iHardwareFrame, p.iFrames );
}
}
@@ -280,7 +280,7 @@ void RageSoundDriver::Update()
// LOG->Trace("finishing sound %i", i);
m_Sounds[i].m_pSound->SoundIsFinishedPlaying();
m_Sounds[i].m_pSound = NULL;
m_Sounds[i].m_pSound = nullptr;
/* This sound is done. Set it to HALTING, since the mixer thread might
* be accessing it; it'll change it back to STOPPED once it's ready to
@@ -389,7 +389,7 @@ void RageSoundDriver::StopMixing( RageSoundBase *pSound )
/* Invalidate the m_pSound pointer to guarantee we don't make any further references to
* it. Once this call returns, the sound may no longer exist. */
m_Sounds[i].m_pSound = NULL;
m_Sounds[i].m_pSound = nullptr;
// LOG->Trace("end StopMixing");
m_Mutex.Unlock();
@@ -439,7 +439,7 @@ void RageSoundDriver::SetDecodeBufferSize( int iFrames )
void RageSoundDriver::low_sample_count_workaround()
{
if (soundDriverMaxSamples != 0) GetHardwareFrame(NULL);
if (soundDriverMaxSamples != 0) GetHardwareFrame(nullptr);
}
RageSoundDriver::RageSoundDriver():
@@ -531,9 +531,9 @@ int64_t RageSoundDriver::ClampHardwareFrame( int64_t iHardwareFrame ) const
return m_iVMaxHardwareFrame;
}
int64_t RageSoundDriver::GetHardwareFrame( RageTimer *pTimestamp=NULL ) const
int64_t RageSoundDriver::GetHardwareFrame( RageTimer *pTimestamp=nullptr ) const
{
if( pTimestamp == NULL )
if( pTimestamp == nullptr )
return ClampHardwareFrame( GetPosition() );
/*
+20 -20
View File
@@ -11,15 +11,15 @@ REGISTER_SOUND_DRIVER_CLASS( JACK );
RageSoundDriver_JACK::RageSoundDriver_JACK() :
RageSoundDriver()
{
client = NULL;
port_l = NULL;
port_r = NULL;
client = nullptr;
port_l = nullptr;
port_r = nullptr;
}
RageSoundDriver_JACK::~RageSoundDriver_JACK()
{
// If Init failed, it cleaned up already and set client to NULL
if (client == NULL)
// If Init failed, it cleaned up already and set client to nullptr
if (client == nullptr)
return;
// Clean up and shut down client
@@ -36,7 +36,7 @@ RString RageSoundDriver_JACK::Init()
// Open JACK client and call it "StepMania" or whatever
client = jack_client_open(PRODUCT_FAMILY, JackNoStartServer, &status);
if (client == NULL)
if (client == nullptr)
return "Couldn't connect to JACK server";
sample_rate = jack_get_sample_rate(client);
@@ -64,7 +64,7 @@ RString RageSoundDriver_JACK::Init()
// Create output ports
port_l = jack_port_register(client, "out_l", JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
if (port_l == NULL)
if (port_l == nullptr)
{
error = "Couldn't create JACK port out_l";
goto out_close;
@@ -72,7 +72,7 @@ RString RageSoundDriver_JACK::Init()
port_r = jack_port_register(client, "out_r", JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
if (port_r == NULL)
if (port_r == nullptr)
{
error = "Couldn't create JACK port out_r";
goto out_unreg_l;
@@ -104,7 +104,7 @@ out_unreg_l:
jack_port_unregister(client, port_l);
out_close:
jack_client_close(client);
client = NULL;
client = nullptr;
return error;
}
@@ -113,23 +113,23 @@ RString RageSoundDriver_JACK::ConnectPorts()
vector<RString> portNames;
split(PREFSMAN->m_iSoundDevice.Get(), ",", portNames, true);
const char *port_out_l = NULL, *port_out_r = NULL;
const char **ports = NULL;
const char *port_out_l = nullptr, *port_out_r = nullptr;
const char **ports = nullptr;
if( portNames.size() == 0 )
{
// The user has NOT specified any ports to connect to. Search
// for all physical sinks and use the first two.
ports = jack_get_ports( client, NULL, NULL, JackPortIsInput | JackPortIsPhysical );
if( ports == NULL )
ports = jack_get_ports( client, nullptr, nullptr, JackPortIsInput | JackPortIsPhysical );
if( ports == nullptr )
return "Couldn't get JACK ports";
if( ports[0] == NULL )
if( ports[0] == nullptr )
{
jack_free( ports );
return "No physical sinks!";
}
port_out_l = ports[0];
if( ports[1] == NULL )
if( ports[1] == nullptr )
// Only one physical sink. We're going mono!
port_out_r = ports[0];
else
@@ -151,9 +151,9 @@ RString RageSoundDriver_JACK::ConnectPorts()
if( ! ( jack_port_flags( out ) & JackPortIsInput ) )
continue;
if( out != NULL )
if( out != nullptr )
{
if( port_out_l == NULL )
if( port_out_l == nullptr )
port_out_l = jack_port_name( out );
else
{
@@ -162,10 +162,10 @@ RString RageSoundDriver_JACK::ConnectPorts()
}
}
}
if( port_out_l == NULL )
if( port_out_l == nullptr )
return "All specified sinks are invalid.";
if( port_out_r == NULL )
if( port_out_r == nullptr )
// Only found one valid sink. Going mono!
port_out_r = port_out_l;
}
@@ -177,7 +177,7 @@ RString RageSoundDriver_JACK::ConnectPorts()
else if( jack_connect( client, jack_port_name(port_r), port_out_r ) != 0 )
ret = "Couldn't connect right JACK port";
if( ports != NULL )
if( ports != nullptr )
jack_free( ports );
return ret;
+2 -2
View File
@@ -58,7 +58,7 @@ void RageSoundDriver_OSS::MixerThread()
usleep( 10000 );
struct timeval tv = { 0, 10000 };
select(fd+1, NULL, &f, NULL, &tv);
select(fd+1, nullptr, &f, nullptr, &tv);
}
}
@@ -81,7 +81,7 @@ bool RageSoundDriver_OSS::GetData()
const int chunksize = ab.fragsize;
static int16_t *buf = NULL;
static int16_t *buf = nullptr;
if(!buf)
buf = new int16_t[chunksize / sizeof(int16_t)];
+17 -17
View File
@@ -16,9 +16,9 @@ REGISTER_SOUND_DRIVER_CLASS2( Pulse, PulseAudio );
/* Constructor */
RageSoundDriver_PulseAudio::RageSoundDriver_PulseAudio()
: RageSoundDriver(),
m_LastPosition(0), m_SampleRate(0), m_Error(NULL),
m_LastPosition(0), m_SampleRate(0), m_Error(nullptr),
m_Sem("Pulseaudio Synchronization Semaphore"),
m_PulseMainLoop(NULL), m_PulseCtx(NULL), m_PulseStream(NULL)
m_PulseMainLoop(nullptr), m_PulseCtx(nullptr), m_PulseStream(nullptr)
{
m_SampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
if( m_SampleRate == 0 )
@@ -32,7 +32,7 @@ RageSoundDriver_PulseAudio::~RageSoundDriver_PulseAudio()
pa_threaded_mainloop_stop(m_PulseMainLoop);
pa_threaded_mainloop_free(m_PulseMainLoop);
if(m_Error != NULL)
if(m_Error != nullptr)
{
free(m_Error);
}
@@ -45,7 +45,7 @@ RString RageSoundDriver_PulseAudio::Init()
LOG->Trace("Pulse: pa_threaded_mainloop_new()...");
m_PulseMainLoop = pa_threaded_mainloop_new();
if(m_PulseMainLoop == NULL)
if(m_PulseMainLoop == nullptr)
{
return "pa_threaded_mainloop_new() failed!";
}
@@ -63,7 +63,7 @@ RString RageSoundDriver_PulseAudio::Init()
"StepMania", plist);
pa_proplist_free(plist);
if(m_PulseCtx == NULL)
if(m_PulseCtx == nullptr)
{
return "pa_context_new_with_proplist() failed!";
}
@@ -72,7 +72,7 @@ RString RageSoundDriver_PulseAudio::Init()
m_PulseCtx = pa_context_new(
pa_threaded_mainloop_get_api(m_PulseMainLoop),
"Stepmania");
if(m_PulseCtx == NULL)
if(m_PulseCtx == nullptr)
{
return "pa_context_new() failed!";
}
@@ -81,7 +81,7 @@ RString RageSoundDriver_PulseAudio::Init()
pa_context_set_state_callback(m_PulseCtx, StaticCtxStateCb, this);
LOG->Trace("Pulse: pa_context_connect()...");
error = pa_context_connect(m_PulseCtx, NULL, (pa_context_flags_t)0, NULL);
error = pa_context_connect(m_PulseCtx, nullptr, (pa_context_flags_t)0, nullptr);
if(error < 0)
{
@@ -101,10 +101,10 @@ RString RageSoundDriver_PulseAudio::Init()
StartDecodeThread();
/* Wait for the pulseaudio stream to be ready before returning.
* An error may occur, if it appends, m_Error becomes non-NULL. */
* An error may occur, if it appends, m_Error becomes non-nullptr. */
m_Sem.Wait();
if(m_Error == NULL)
if(m_Error == nullptr)
{
return "";
}
@@ -137,7 +137,7 @@ void RageSoundDriver_PulseAudio::m_InitStream(void)
{
if(asprintf(&m_Error, "invalid sample spec!") == -1)
{
m_Error = NULL;
m_Error = nullptr;
}
m_Sem.Post();
return;
@@ -151,11 +151,11 @@ void RageSoundDriver_PulseAudio::m_InitStream(void)
/* create the stream */
LOG->Trace("Pulse: pa_stream_new()...");
m_PulseStream = pa_stream_new(m_PulseCtx, "Stepmania Audio", &ss, &map);
if(m_PulseStream == NULL)
if(m_PulseStream == nullptr)
{
if(asprintf(&m_Error, "pa_stream_new(): %s", pa_strerror(pa_context_errno(m_PulseCtx))) == -1)
{
m_Error = NULL;
m_Error = nullptr;
}
m_Sem.Post();
return;
@@ -224,14 +224,14 @@ void RageSoundDriver_PulseAudio::m_InitStream(void)
/* connect the stream for playback */
LOG->Trace("Pulse: pa_stream_connect_playback()...");
error = pa_stream_connect_playback(m_PulseStream, NULL, &attr,
PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
error = pa_stream_connect_playback(m_PulseStream, nullptr, &attr,
PA_STREAM_AUTO_TIMING_UPDATE, nullptr, nullptr);
if(error < 0)
{
if(asprintf(&m_Error, "pa_stream_connect_playback(): %s",
pa_strerror(pa_context_errno(m_PulseCtx))) == -1)
{
m_Error = NULL;
m_Error = nullptr;
}
m_Sem.Post();
return;
@@ -261,7 +261,7 @@ void RageSoundDriver_PulseAudio::CtxStateCb(pa_context *c)
case PA_CONTEXT_FAILED:
if(asprintf(&m_Error, "context connection failed: %s", pa_strerror(pa_context_errno(m_PulseCtx))) == -1)
{
m_Error = NULL;
m_Error = nullptr;
}
m_Sem.Post();
return;
@@ -316,7 +316,7 @@ void RageSoundDriver_PulseAudio::StreamWriteCb(pa_stream *s, size_t length)
int64_t pos1 = m_LastPosition;
int64_t pos2 = pos1 + nbframes/2; /* Mix() position in stereo frames */
this->Mix( buf, pos2-pos1, pos1, pos2);
if(pa_stream_write(m_PulseStream, buf, length, NULL, 0, PA_SEEK_RELATIVE) < 0)
if(pa_stream_write(m_PulseStream, buf, length, nullptr, 0, PA_SEEK_RELATIVE) < 0)
{
RageException::Throw("Pulse: pa_stream_write()");
}
File diff suppressed because it is too large Load Diff
+211 -211
View File
@@ -1,211 +1,211 @@
#include "global.h"
#include "RageSoundDriver_WaveOut.h"
#if defined(_MSC_VER)
#pragma comment(lib, "winmm.lib")
#endif
#include "RageTimer.h"
#include "RageLog.h"
#include "RageSound.h"
#include "RageUtil.h"
#include "RageSoundManager.h"
#include "PrefsManager.h"
#include "archutils/Win32/ErrorStrings.h"
REGISTER_SOUND_DRIVER_CLASS( WaveOut );
const int channels = 2;
const int bytes_per_frame = channels*2; /* 16-bit */
const int buffersize_frames = 1024*8; /* in frames */
const int buffersize = buffersize_frames * bytes_per_frame; /* in bytes */
const int num_chunks = 8;
const int chunksize_frames = buffersize_frames / num_chunks;
const int chunksize = buffersize / num_chunks; /* in bytes */
static RString wo_ssprintf( MMRESULT err, const char *szFmt, ...)
{
char szBuf[MAXERRORLENGTH];
waveOutGetErrorText( err, szBuf, MAXERRORLENGTH );
va_list va;
va_start( va, szFmt );
RString s = vssprintf( szFmt, va );
va_end( va );
return s += ssprintf( "(%s)", szBuf );
}
int RageSoundDriver_WaveOut::MixerThread_start( void *p )
{
((RageSoundDriver_WaveOut *) p)->MixerThread();
return 0;
}
void RageSoundDriver_WaveOut::MixerThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set sound thread priority") );
while( !m_bShutdown )
{
while( GetData() )
;
WaitForSingleObject( m_hSoundEvent, 10 );
}
waveOutReset( m_hWaveOut );
}
bool RageSoundDriver_WaveOut::GetData()
{
/* Look for a free buffer. */
int b;
for( b = 0; b < num_chunks; ++b )
if( m_aBuffers[b].dwFlags & WHDR_DONE )
break;
if( b == num_chunks )
return false;
/* Call the callback. */
this->Mix( (int16_t *) m_aBuffers[b].lpData, chunksize_frames, m_iLastCursorPos, GetPosition() );
MMRESULT ret = waveOutWrite( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
if( ret != MMSYSERR_NOERROR )
FAIL_M( wo_ssprintf(ret, "waveOutWrite failed") );
/* Increment m_iLastCursorPos. */
m_iLastCursorPos += chunksize_frames;
return true;
}
void RageSoundDriver_WaveOut::SetupDecodingThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set sound thread priority") );
}
int64_t RageSoundDriver_WaveOut::GetPosition() const
{
MMTIME tm;
tm.wType = TIME_SAMPLES;
MMRESULT ret = waveOutGetPosition( m_hWaveOut, &tm, sizeof(tm) );
if( ret != MMSYSERR_NOERROR )
FAIL_M( wo_ssprintf(ret, "waveOutGetPosition failed") );
return tm.u.sample;
}
RageSoundDriver_WaveOut::RageSoundDriver_WaveOut()
{
m_bShutdown = false;
m_iLastCursorPos = 0;
m_hSoundEvent = CreateEvent( NULL, false, true, NULL );
m_hWaveOut = NULL;
}
RString RageSoundDriver_WaveOut::Init()
{
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
if( m_iSampleRate == 0 )
m_iSampleRate = 44100;
WAVEFORMATEX fmt;
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.nChannels = channels;
fmt.cbSize = 0;
fmt.nSamplesPerSec = m_iSampleRate;
fmt.wBitsPerSample = 16;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
MMRESULT ret = waveOutOpen( &m_hWaveOut, WAVE_MAPPER, &fmt, (DWORD_PTR) m_hSoundEvent, NULL, CALLBACK_EVENT );
if( ret != MMSYSERR_NOERROR )
return wo_ssprintf( ret, "waveOutOpen failed" );
ZERO( m_aBuffers );
for(int b = 0; b < num_chunks; ++b)
{
m_aBuffers[b].dwBufferLength = chunksize;
m_aBuffers[b].lpData = new char[chunksize];
ret = waveOutPrepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
if( ret != MMSYSERR_NOERROR )
return wo_ssprintf( ret, "waveOutPrepareHeader failed" );
m_aBuffers[b].dwFlags |= WHDR_DONE;
}
LOG->Info( "WaveOut software mixing at %i hz", m_iSampleRate );
/* We have a very large writeahead; make sure we have a large enough decode
* buffer to recover cleanly from underruns. */
SetDecodeBufferSize( buffersize_frames * 3/2 );
StartDecodeThread();
MixingThread.SetName( "Mixer thread" );
MixingThread.Create( MixerThread_start, this );
return RString();
}
RageSoundDriver_WaveOut::~RageSoundDriver_WaveOut()
{
/* Signal the mixing thread to quit. */
if( MixingThread.IsCreated() )
{
m_bShutdown = true;
SetEvent( m_hSoundEvent );
LOG->Trace( "Shutting down mixer thread ..." );
MixingThread.Wait();
LOG->Trace( "Mixer thread shut down." );
}
if( m_hWaveOut != NULL )
{
for( int b = 0; b < num_chunks && m_aBuffers[b].lpData != NULL; ++b )
{
waveOutUnprepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
delete [] m_aBuffers[b].lpData;
}
waveOutClose( m_hWaveOut );
}
CloseHandle( m_hSoundEvent );
}
float RageSoundDriver_WaveOut::GetPlayLatency() const
{
/* If we have a 1000-byte buffer, and we fill 100 bytes at a time, we
* almost always have between 900 and 1000 bytes filled; on average, 950. */
return (buffersize_frames - chunksize_frames/2) * (1.0f / m_iSampleRate);
}
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "RageSoundDriver_WaveOut.h"
#if defined(_MSC_VER)
#pragma comment(lib, "winmm.lib")
#endif
#include "RageTimer.h"
#include "RageLog.h"
#include "RageSound.h"
#include "RageUtil.h"
#include "RageSoundManager.h"
#include "PrefsManager.h"
#include "archutils/Win32/ErrorStrings.h"
REGISTER_SOUND_DRIVER_CLASS( WaveOut );
const int channels = 2;
const int bytes_per_frame = channels*2; /* 16-bit */
const int buffersize_frames = 1024*8; /* in frames */
const int buffersize = buffersize_frames * bytes_per_frame; /* in bytes */
const int num_chunks = 8;
const int chunksize_frames = buffersize_frames / num_chunks;
const int chunksize = buffersize / num_chunks; /* in bytes */
static RString wo_ssprintf( MMRESULT err, const char *szFmt, ...)
{
char szBuf[MAXERRORLENGTH];
waveOutGetErrorText( err, szBuf, MAXERRORLENGTH );
va_list va;
va_start( va, szFmt );
RString s = vssprintf( szFmt, va );
va_end( va );
return s += ssprintf( "(%s)", szBuf );
}
int RageSoundDriver_WaveOut::MixerThread_start( void *p )
{
((RageSoundDriver_WaveOut *) p)->MixerThread();
return 0;
}
void RageSoundDriver_WaveOut::MixerThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set sound thread priority") );
while( !m_bShutdown )
{
while( GetData() )
;
WaitForSingleObject( m_hSoundEvent, 10 );
}
waveOutReset( m_hWaveOut );
}
bool RageSoundDriver_WaveOut::GetData()
{
/* Look for a free buffer. */
int b;
for( b = 0; b < num_chunks; ++b )
if( m_aBuffers[b].dwFlags & WHDR_DONE )
break;
if( b == num_chunks )
return false;
/* Call the callback. */
this->Mix( (int16_t *) m_aBuffers[b].lpData, chunksize_frames, m_iLastCursorPos, GetPosition() );
MMRESULT ret = waveOutWrite( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
if( ret != MMSYSERR_NOERROR )
FAIL_M( wo_ssprintf(ret, "waveOutWrite failed") );
/* Increment m_iLastCursorPos. */
m_iLastCursorPos += chunksize_frames;
return true;
}
void RageSoundDriver_WaveOut::SetupDecodingThread()
{
if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set sound thread priority") );
}
int64_t RageSoundDriver_WaveOut::GetPosition() const
{
MMTIME tm;
tm.wType = TIME_SAMPLES;
MMRESULT ret = waveOutGetPosition( m_hWaveOut, &tm, sizeof(tm) );
if( ret != MMSYSERR_NOERROR )
FAIL_M( wo_ssprintf(ret, "waveOutGetPosition failed") );
return tm.u.sample;
}
RageSoundDriver_WaveOut::RageSoundDriver_WaveOut()
{
m_bShutdown = false;
m_iLastCursorPos = 0;
m_hSoundEvent = CreateEvent( nullptr, false, true, nullptr );
m_hWaveOut = nullptr;
}
RString RageSoundDriver_WaveOut::Init()
{
m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
if( m_iSampleRate == 0 )
m_iSampleRate = 44100;
WAVEFORMATEX fmt;
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.nChannels = channels;
fmt.cbSize = 0;
fmt.nSamplesPerSec = m_iSampleRate;
fmt.wBitsPerSample = 16;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
MMRESULT ret = waveOutOpen( &m_hWaveOut, WAVE_MAPPER, &fmt, (DWORD_PTR) m_hSoundEvent, NULL, CALLBACK_EVENT );
if( ret != MMSYSERR_NOERROR )
return wo_ssprintf( ret, "waveOutOpen failed" );
ZERO( m_aBuffers );
for(int b = 0; b < num_chunks; ++b)
{
m_aBuffers[b].dwBufferLength = chunksize;
m_aBuffers[b].lpData = new char[chunksize];
ret = waveOutPrepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
if( ret != MMSYSERR_NOERROR )
return wo_ssprintf( ret, "waveOutPrepareHeader failed" );
m_aBuffers[b].dwFlags |= WHDR_DONE;
}
LOG->Info( "WaveOut software mixing at %i hz", m_iSampleRate );
/* We have a very large writeahead; make sure we have a large enough decode
* buffer to recover cleanly from underruns. */
SetDecodeBufferSize( buffersize_frames * 3/2 );
StartDecodeThread();
MixingThread.SetName( "Mixer thread" );
MixingThread.Create( MixerThread_start, this );
return RString();
}
RageSoundDriver_WaveOut::~RageSoundDriver_WaveOut()
{
/* Signal the mixing thread to quit. */
if( MixingThread.IsCreated() )
{
m_bShutdown = true;
SetEvent( m_hSoundEvent );
LOG->Trace( "Shutting down mixer thread ..." );
MixingThread.Wait();
LOG->Trace( "Mixer thread shut down." );
}
if( m_hWaveOut != nullptr )
{
for( int b = 0; b < num_chunks && m_aBuffers[b].lpData != nullptr; ++b )
{
waveOutUnprepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
delete [] m_aBuffers[b].lpData;
}
waveOutClose( m_hWaveOut );
}
CloseHandle( m_hSoundEvent );
}
float RageSoundDriver_WaveOut::GetPlayLatency() const
{
/* If we have a 1000-byte buffer, and we fill 100 bytes at a time, we
* almost always have between 900 and 1000 bytes filled; on average, 950. */
return (buffersize_frames - chunksize_frames/2) * (1.0f / m_iSampleRate);
}
/*
* (c) 2002-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/