Files
itgmania212121/stepmania/src/RageSoundReader_Resample_Good.cpp
T
Glenn Maynard 27243e074c working on variable channel count support (to allow preloading mono samples
in half memory, and to avoid redundant resampling in mono files)
2004-07-24 01:22:10 +00:00

264 lines
6.5 KiB
C++

#include "global.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageSoundReader_Resample_Good.h"
#include "libresample/include/libresample.h"
#ifdef _WINDOWS
#pragma comment(lib, "libresample/resample.lib")
#endif
#ifdef _XBOX
#ifdef _DEBUG
#pragma comment(lib, "libresample/xboxresample/debug/xboxresample.lib")
#else
#pragma comment(lib, "libresample/xboxresample/release/xboxresample.lib")
#endif
#endif
#include "RageTimer.h"
#define channels source->GetNumChannels()
RageSoundReader_Resample_Good::RageSoundReader_Resample_Good()
{
source = NULL;
empty_resamp = NULL;
samplerate = -1;
BufSamples = 0;
eof = false;
HighQuality = false;
}
/* Call this if the input position is changed or reset. */
void RageSoundReader_Resample_Good::Reset()
{
BufSamples = 0;
eof = false;
/* Flush the resampler. */
for( unsigned i = 0; i < resamplers.size(); ++i )
{
resample_channel &r = resamplers[i];
if( r.resamp )
resample_close( r.resamp );
r.resamp = resample_dup( empty_resamp );
}
}
/* Call this if the sample factor changes. */
void RageSoundReader_Resample_Good::ReopenResampler()
{
if( empty_resamp )
resample_close( empty_resamp );
empty_resamp = resample_open( HighQuality, GetFactor()-0.1f, GetFactor()+0.1f );
for( unsigned i = 0; i < resamplers.size(); ++i )
{
resample_channel &r = resamplers[i];
if( r.resamp )
resample_close( r.resamp );
r.resamp = resample_dup( empty_resamp );
}
}
void RageSoundReader_Resample_Good::Open(SoundReader *source_)
{
source = source_;
ASSERT(source);
samplerate = source->GetSampleRate();
for( unsigned i = 0; i < source->GetNumChannels(); ++i )
resamplers.push_back( resample_channel() );
}
RageSoundReader_Resample_Good::~RageSoundReader_Resample_Good()
{
for( unsigned i = 0; i < resamplers.size(); ++i )
{
if( resamplers[i].resamp )
resample_close( resamplers[i].resamp );
}
if( empty_resamp )
resample_close( empty_resamp );
delete source;
}
float RageSoundReader_Resample_Good::GetFactor() const
{
return float(samplerate) / source->GetSampleRate();
}
void RageSoundReader_Resample_Good::SetSampleRate(int hz)
{
samplerate = hz;
ReopenResampler();
}
int RageSoundReader_Resample_Good::GetLength() const
{
return source->GetLength();
}
int RageSoundReader_Resample_Good::GetLength_Fast() const
{
return source->GetLength_Fast();
}
int RageSoundReader_Resample_Good::SetPosition_Accurate(int ms)
{
Reset();
return source->SetPosition_Accurate(ms);
}
int RageSoundReader_Resample_Good::SetPosition_Fast(int ms)
{
Reset();
return source->SetPosition_Fast(ms);
}
bool RageSoundReader_Resample_Good::FillBuf()
{
int samples_free = BUFSIZE-BufSamples;
if( eof )
return true;
if( !samples_free )
return true;
const int bytes_per_frame = sizeof(int16_t)*channels;
int16_t *tmpbuf = (int16_t *) alloca( BUFSIZE*bytes_per_frame );
int cnt = source->Read( (char *) tmpbuf, samples_free * bytes_per_frame );
if( cnt == -1 )
{
SetError(source->GetError());
return false;
}
if( cnt < samples_free * bytes_per_frame )
eof = true;
cnt /= bytes_per_frame;
for( unsigned i = 0; i < channels; ++i )
{
resample_channel &r = resamplers[i];
for( int s = 0; s < cnt; ++s )
r.inbuf[s+BufSamples] = tmpbuf[s*resamplers.size()+i];
}
BufSamples += cnt;
return true;
}
int RageSoundReader_Resample_Good::Read(char *bufp, unsigned len)
{
int16_t *buf = (int16_t *) bufp;
len /= sizeof(int16_t); /* bytes -> samples */
const float factor = GetFactor();
int bytes_read = 0;
while( 1 )
{
int samples_used = 0, samples_output = 0;
if( BufSamples )
{
for( unsigned i = 0; i < channels; ++i )
{
resample_channel &r = resamplers[i];
ASSERT( r.resamp );
float outbuf[BUFSIZE];
samples_output = resample_process( r.resamp,
factor,
r.inbuf, BufSamples,
eof,
&samples_used,
outbuf, len/channels);
if( samples_output == -1 )
RageException::Throw( "Unexpected resample_process return value: -1" );
memmove( r.inbuf, &r.inbuf[samples_used], sizeof(float) * (BufSamples-samples_used) );
for( int s = 0; s < samples_output; ++s )
{
buf[s*channels+i] = int16_t(clamp(outbuf[s], -32768, 32767));
}
}
}
BufSamples -= samples_used;
if( !samples_output )
{
if( !len )
return bytes_read; /* buffer full */
if( eof )
return bytes_read; /* EOF and we're completely flushed */
if( !FillBuf() )
return -1; /* source error */
}
len -= samples_output*channels;
buf += samples_output*channels;
bytes_read += samples_output*channels*sizeof(int16_t);
}
}
SoundReader *RageSoundReader_Resample_Good::Copy() const
{
SoundReader *new_source = source->Copy();
RageSoundReader_Resample_Good *ret = new RageSoundReader_Resample_Good;
for( unsigned i = 0; i < channels; ++i )
{
const resample_channel &r = resamplers[i];
ASSERT( r.resamp );
ret->resamplers.push_back( resample_channel() );
ret->resamplers[i].resamp = resample_dup( r.resamp );
memcpy( ret->resamplers[i].inbuf, r.inbuf, sizeof(r.inbuf));
}
ret->empty_resamp = resample_dup( empty_resamp );
ret->source = new_source;
ret->HighQuality = HighQuality;
ret->samplerate = samplerate;
ret->BufSamples = BufSamples;
ret->eof = eof;
// ret->Open(new_source);
// ret->SetSampleRate(samplerate);
return ret;
}
/*
* Copyright (c) 2003 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.
*/