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