working on variable channel count support (to allow preloading mono samples
in half memory, and to avoid redundant resampling in mono files)
This commit is contained in:
@@ -21,6 +21,7 @@ public:
|
||||
virtual ~SoundReader() { }
|
||||
virtual SoundReader *Copy() const = 0;
|
||||
virtual int GetSampleRate() const = 0;
|
||||
virtual unsigned GetNumChannels() const { return 2; } /* 1 or 2 */
|
||||
|
||||
bool Error() const { return !error.empty(); }
|
||||
string GetError() const { return error; }
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "RageSoundReader_Preload.h"
|
||||
#include "PrefsManager.h"
|
||||
|
||||
const int channels = 2;
|
||||
const int samplesize = 2 * channels; /* 16-bit */
|
||||
#define samplesize (2 * channels) /* 16-bit */
|
||||
|
||||
/* If a sound is smaller than this, we'll load it entirely into memory. */
|
||||
const unsigned max_prebuf_size = 1024*256;
|
||||
@@ -20,7 +19,8 @@ bool SoundReader_Preload::Open(SoundReader *source)
|
||||
{
|
||||
ASSERT(source);
|
||||
samplerate = source->GetSampleRate();
|
||||
|
||||
channels = source->GetNumChannels();
|
||||
|
||||
/* Check the length, and see if we think it'll fit in the buffer. */
|
||||
int len = source->GetLength_Fast();
|
||||
if(len != -1)
|
||||
|
||||
@@ -22,7 +22,8 @@ public:
|
||||
const string &get() const;
|
||||
};
|
||||
|
||||
class SoundReader_Preload: public SoundReader {
|
||||
class SoundReader_Preload: public SoundReader
|
||||
{
|
||||
rc_string buf;
|
||||
|
||||
/* Bytes: */
|
||||
@@ -31,6 +32,7 @@ class SoundReader_Preload: public SoundReader {
|
||||
int total_samples() const;
|
||||
|
||||
int samplerate;
|
||||
unsigned channels;
|
||||
float OffsetFix;
|
||||
|
||||
public:
|
||||
@@ -43,6 +45,7 @@ public:
|
||||
int SetPosition_Fast(int ms);
|
||||
int Read(char *buf, unsigned len);
|
||||
int GetSampleRate() const { return samplerate; }
|
||||
unsigned GetNumChannels() const { return channels; }
|
||||
|
||||
SoundReader *Copy() const;
|
||||
~SoundReader_Preload() { }
|
||||
|
||||
@@ -14,6 +14,7 @@ void RageSoundReader_Resample_Fast::Open(SoundReader *source_)
|
||||
|
||||
samplerate = source->GetSampleRate();
|
||||
resamp.SetInputSampleRate(samplerate);
|
||||
resamp.SetChannels( source->GetNumChannels() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
void SetSampleRate(int hz);
|
||||
|
||||
int GetSampleRate() const { return samplerate; }
|
||||
unsigned GetNumChannels() const { return source->GetNumChannels(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
|
||||
#include "RageTimer.h"
|
||||
|
||||
#define channels source->GetNumChannels()
|
||||
|
||||
RageSoundReader_Resample_Good::RageSoundReader_Resample_Good()
|
||||
{
|
||||
resamp[0] = resamp[1] = NULL;
|
||||
source = NULL;
|
||||
empty_resamp = NULL;
|
||||
samplerate = -1;
|
||||
BufSamples = 0;
|
||||
eof = false;
|
||||
@@ -37,12 +39,13 @@ void RageSoundReader_Resample_Good::Reset()
|
||||
eof = false;
|
||||
|
||||
/* Flush the resampler. */
|
||||
for( int i = 0; i < 2; ++i )
|
||||
for( unsigned i = 0; i < resamplers.size(); ++i )
|
||||
{
|
||||
if( resamp[i] )
|
||||
resample_close( resamp[i] );
|
||||
resample_channel &r = resamplers[i];
|
||||
if( r.resamp )
|
||||
resample_close( r.resamp );
|
||||
|
||||
resamp[i] = resample_dup( empty_resamp[i] );
|
||||
r.resamp = resample_dup( empty_resamp );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +53,16 @@ void RageSoundReader_Resample_Good::Reset()
|
||||
/* Call this if the sample factor changes. */
|
||||
void RageSoundReader_Resample_Good::ReopenResampler()
|
||||
{
|
||||
for( int i = 0; i < 2; ++i )
|
||||
{
|
||||
if( resamp[i] )
|
||||
resample_close( resamp[i] );
|
||||
if( empty_resamp )
|
||||
resample_close( empty_resamp );
|
||||
empty_resamp = resample_open( HighQuality, GetFactor()-0.1f, GetFactor()+0.1f );
|
||||
|
||||
resamp[i] = resample_open( HighQuality, GetFactor()-0.1f, GetFactor()+0.1f );
|
||||
empty_resamp[i] = resample_dup( resamp[i] );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,19 +72,23 @@ void RageSoundReader_Resample_Good::Open(SoundReader *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( int i = 0; i < 2; ++i )
|
||||
for( unsigned i = 0; i < resamplers.size(); ++i )
|
||||
{
|
||||
if( resamp[i] )
|
||||
resample_close( resamp[i] );
|
||||
if( empty_resamp[i] )
|
||||
resample_close( empty_resamp[i] );
|
||||
if( resamplers[i].resamp )
|
||||
resample_close( resamplers[i].resamp );
|
||||
}
|
||||
|
||||
if( empty_resamp )
|
||||
resample_close( empty_resamp );
|
||||
|
||||
delete source;
|
||||
}
|
||||
|
||||
@@ -123,8 +133,9 @@ bool RageSoundReader_Resample_Good::FillBuf()
|
||||
if( !samples_free )
|
||||
return true;
|
||||
|
||||
int16_t tmpbuf[BUFSIZE*2];
|
||||
int cnt = source->Read((char *) tmpbuf, samples_free * sizeof(int16_t) * 2);
|
||||
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 )
|
||||
{
|
||||
@@ -132,16 +143,16 @@ bool RageSoundReader_Resample_Good::FillBuf()
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (unsigned) cnt < samples_free * sizeof(int16_t) * 2 )
|
||||
if( cnt < samples_free * bytes_per_frame )
|
||||
eof = true;
|
||||
|
||||
cnt /= sizeof(int16_t);
|
||||
cnt /= 2;
|
||||
cnt /= bytes_per_frame;
|
||||
|
||||
for( int c = 0; c < 2; ++c )
|
||||
for( unsigned i = 0; i < channels; ++i )
|
||||
{
|
||||
resample_channel &r = resamplers[i];
|
||||
for( int s = 0; s < cnt; ++s )
|
||||
inbuf[c][s+BufSamples] = tmpbuf[s*2+c];
|
||||
r.inbuf[s+BufSamples] = tmpbuf[s*resamplers.size()+i];
|
||||
}
|
||||
BufSamples += cnt;
|
||||
return true;
|
||||
@@ -150,7 +161,7 @@ bool RageSoundReader_Resample_Good::FillBuf()
|
||||
int RageSoundReader_Resample_Good::Read(char *bufp, unsigned len)
|
||||
{
|
||||
int16_t *buf = (int16_t *) bufp;
|
||||
len /= 2;
|
||||
len /= sizeof(int16_t); /* bytes -> samples */
|
||||
const float factor = GetFactor();
|
||||
|
||||
int bytes_read = 0;
|
||||
@@ -159,24 +170,25 @@ int RageSoundReader_Resample_Good::Read(char *bufp, unsigned len)
|
||||
int samples_used = 0, samples_output = 0;
|
||||
if( BufSamples )
|
||||
{
|
||||
for( int c = 0; c < 2; ++c )
|
||||
for( unsigned i = 0; i < channels; ++i )
|
||||
{
|
||||
ASSERT( resamp[c] );
|
||||
resample_channel &r = resamplers[i];
|
||||
ASSERT( r.resamp );
|
||||
float outbuf[BUFSIZE];
|
||||
samples_output = resample_process(resamp[c],
|
||||
samples_output = resample_process( r.resamp,
|
||||
factor,
|
||||
inbuf[c], BufSamples,
|
||||
r.inbuf, BufSamples,
|
||||
eof,
|
||||
&samples_used,
|
||||
outbuf, len/2);
|
||||
outbuf, len/channels);
|
||||
if( samples_output == -1 )
|
||||
RageException::Throw( "Unexpected resample_process return value: -1" );
|
||||
|
||||
memmove( inbuf[c], &inbuf[c][samples_used], sizeof(float) * (BufSamples-samples_used) );
|
||||
memmove( r.inbuf, &r.inbuf[samples_used], sizeof(float) * (BufSamples-samples_used) );
|
||||
|
||||
for( int s = 0; s < samples_output; ++s )
|
||||
{
|
||||
buf[s*2+c] = int16_t(clamp(outbuf[s], -32768, 32767));
|
||||
buf[s*channels+i] = int16_t(clamp(outbuf[s], -32768, 32767));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,9 +205,9 @@ int RageSoundReader_Resample_Good::Read(char *bufp, unsigned len)
|
||||
return -1; /* source error */
|
||||
}
|
||||
|
||||
len -= samples_output*2;
|
||||
buf += samples_output*2;
|
||||
bytes_read += samples_output*2*sizeof(int16_t);
|
||||
len -= samples_output*channels;
|
||||
buf += samples_output*channels;
|
||||
bytes_read += samples_output*channels*sizeof(int16_t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,16 +216,18 @@ SoundReader *RageSoundReader_Resample_Good::Copy() const
|
||||
SoundReader *new_source = source->Copy();
|
||||
RageSoundReader_Resample_Good *ret = new RageSoundReader_Resample_Good;
|
||||
|
||||
for( int c = 0; c < 2; ++c )
|
||||
for( unsigned i = 0; i < channels; ++i )
|
||||
{
|
||||
ASSERT( resamp[c] );
|
||||
ret->resamp[c] = resample_dup( resamp[c] );
|
||||
ret->empty_resamp[c] = resample_dup( empty_resamp[c] );
|
||||
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;
|
||||
memcpy( ret->inbuf, inbuf, sizeof(inbuf));
|
||||
ret->BufSamples = BufSamples;
|
||||
ret->eof = eof;
|
||||
|
||||
|
||||
@@ -13,9 +13,14 @@ class RageSoundReader_Resample_Good: public RageSoundReader_Resample
|
||||
bool HighQuality;
|
||||
int samplerate;
|
||||
|
||||
void *resamp[2];
|
||||
void *empty_resamp[2];
|
||||
float inbuf[2][BUFSIZE];
|
||||
void *empty_resamp;
|
||||
struct resample_channel
|
||||
{
|
||||
resample_channel(): resamp(NULL) { }
|
||||
void *resamp;
|
||||
float inbuf[BUFSIZE];
|
||||
};
|
||||
vector<resample_channel> resamplers; /* one per channel */
|
||||
int BufSamples;
|
||||
bool eof;
|
||||
|
||||
@@ -41,6 +46,7 @@ public:
|
||||
void SetHighQuality( bool hq ) { HighQuality = hq; }
|
||||
|
||||
int GetSampleRate() const { return samplerate; }
|
||||
unsigned GetNumChannels() const { return source->GetNumChannels(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
* one. I'll optimize it if it becomes an issue.
|
||||
*/
|
||||
|
||||
const int channels = 2;
|
||||
|
||||
RageSoundResampler::RageSoundResampler()
|
||||
{
|
||||
reset();
|
||||
@@ -24,6 +22,7 @@ void RageSoundResampler::reset()
|
||||
memset(t, 0, sizeof(t));
|
||||
outbuf.clear();
|
||||
ipos = 0;
|
||||
Channels = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +34,7 @@ void RageSoundResampler::write(const void *data_, int bytes)
|
||||
const int16_t *data = (const int16_t *) data_;
|
||||
|
||||
const unsigned samples = bytes / sizeof(int16_t);
|
||||
const unsigned frames = samples / channels;
|
||||
const unsigned frames = samples / Channels;
|
||||
|
||||
if(InputRate == OutputRate)
|
||||
{
|
||||
@@ -55,7 +54,7 @@ void RageSoundResampler::write(const void *data_, int bytes)
|
||||
int ipos_begin = (int) roundf(ipos / div);
|
||||
int ipos_end = (int) roundf((ipos+1) / div);
|
||||
|
||||
for(int c = 0; c < channels; ++c)
|
||||
for(int c = 0; c < Channels; ++c)
|
||||
{
|
||||
const float f = 0.5f;
|
||||
prev[c] = int16_t(prev[c] * (f) + data[c] * (1-f));
|
||||
@@ -64,14 +63,14 @@ void RageSoundResampler::write(const void *data_, int bytes)
|
||||
}
|
||||
ipos++;
|
||||
}
|
||||
data += channels;
|
||||
data += Channels;
|
||||
}
|
||||
#else
|
||||
/* Lerp. */
|
||||
const float div = float(InputRate) / OutputRate;
|
||||
for(unsigned f = 0; f < frames; ++f)
|
||||
{
|
||||
for(int c = 0; c < channels; ++c)
|
||||
for(int c = 0; c < Channels; ++c)
|
||||
{
|
||||
while(t[c] < 1.0f)
|
||||
{
|
||||
@@ -85,7 +84,7 @@ void RageSoundResampler::write(const void *data_, int bytes)
|
||||
}
|
||||
|
||||
ipos++;
|
||||
data += channels;
|
||||
data += Channels;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -98,13 +97,13 @@ void RageSoundResampler::eof()
|
||||
/* Write some silence to flush out the real data. If we don't have any sound,
|
||||
* don't do this, so seeking past end of file doesn't write silence. */
|
||||
bool bNeedsFlush = false;
|
||||
for( int c = 0; c < channels; ++c )
|
||||
for( int c = 0; c < Channels; ++c )
|
||||
if( prev[c] != 0 )
|
||||
bNeedsFlush = true;
|
||||
|
||||
if( bNeedsFlush )
|
||||
{
|
||||
const int size = channels*16;
|
||||
const int size = Channels*16;
|
||||
int16_t *data = new int16_t[size];
|
||||
memset(data, 0, size * sizeof(int16_t));
|
||||
write(data, size * sizeof(int16_t));
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#ifndef RAGE_SOUND_RESAMPLER_H
|
||||
#define RAGE_SOUND_RESAMPLER_H
|
||||
|
||||
enum { MAX_CHANNELS = 15 };
|
||||
|
||||
class RageSoundResampler
|
||||
{
|
||||
int InputRate, OutputRate;
|
||||
int InputRate, OutputRate, Channels;
|
||||
|
||||
enum { MAX_CHANNELS = 15 };
|
||||
int16_t prev[MAX_CHANNELS];
|
||||
|
||||
vector<int16_t> outbuf;
|
||||
@@ -19,6 +18,7 @@ public:
|
||||
RageSoundResampler();
|
||||
|
||||
/* Configuration: */
|
||||
void SetChannels( int c ) { ASSERT( c < MAX_CHANNELS ); Channels = c; }
|
||||
void SetInputSampleRate(int hz) { InputRate = hz; }
|
||||
void SetOutputSampleRate(int hz) { OutputRate = hz; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user