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() { }
|
||||||
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; }
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user