From e6dfa1f081bfe3095d41b6c2dcd01ad46d0537b1 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Wed, 23 Apr 2003 07:17:31 +0000 Subject: [PATCH] add resampler --- stepmania/src/Makefile.am | 3 +- stepmania/src/RageSoundResampler.cpp | 136 +++++++++++++++++++++++++++ stepmania/src/RageSoundResampler.h | 47 +++++++++ stepmania/src/StepMania.dsp | 16 +++- stepmania/src/StepMania.vcproj | 6 ++ 5 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 stepmania/src/RageSoundResampler.cpp create mode 100644 stepmania/src/RageSoundResampler.h diff --git a/stepmania/src/Makefile.am b/stepmania/src/Makefile.am index 72124f8e3d..7bb01fb26d 100644 --- a/stepmania/src/Makefile.am +++ b/stepmania/src/Makefile.am @@ -37,7 +37,8 @@ RageException.cpp RageException.h RageInput.cpp RageInputDevice.cpp RageInputDev RageLog.cpp RageLog.h RageMath.cpp RageMath.h RageSound.cpp RageSound.h \ RageSoundManager.cpp RageSoundManager.h RageSoundReader.h RageSoundReader_SDL_Sound.cpp RageSoundReader_SDL_Sound.h \ RageTexture.cpp RageTexture.h RageTextureManager.cpp RageTextureManager.h RageThreads.cpp RageThreads.h \ -RageTimer.cpp RageTimer.h RageTypes.h RageUtil.cpp RageUtil.h RageSoundReader_Preload.cpp RageSoundReader_Preload.h +RageTimer.cpp RageTimer.h RageTypes.h RageUtil.cpp RageUtil.h RageSoundReader_Preload.cpp RageSoundReader_Preload.h \ +RageSoundResampler.cpp RageSoundResampler.h DataStructures = \ CodeDetector.cpp CodeDetector.h Course.cpp Course.h Font.cpp Font.h FontCharAliases.cpp FontCharAliases.h \ diff --git a/stepmania/src/RageSoundResampler.cpp b/stepmania/src/RageSoundResampler.cpp new file mode 100644 index 0000000000..4bba045166 --- /dev/null +++ b/stepmania/src/RageSoundResampler.cpp @@ -0,0 +1,136 @@ +#include "global.h" +#include "RageSoundResampler.h" +#include "RageUtil.h" +#include "RageLog.h" +#include + +/* + * This class handles sound conversion. + * + * Rules: + * + * We only handle 16-bit, signed data in the local endianness. We don't need + * to handle 8-bit data right now, because SDL_sound will let us tell it to + * do the conversion, but if we need to handle other types ourself, the conversions + * are trivial. + * + * The primary function here is to resample between arbitrary sample rates. + * + * We also handle channel conversion. If we have a mono signal, and we're outputting + * a stereo signal, it'd be silly to do the channel doubling before the resample. + * + * + * This isn't very efficient; we write to a static buffer instead of a circular + * one. I'll optimize it if it becomes an issue. + */ + + + +RageSoundResampler::RageSoundResampler() +{ + reset(); +} + +void RageSoundResampler::reset() +{ + at_eof = false; + memset(prev, 0, sizeof(prev)); + memset(t, 0, sizeof(t)); + ipos = 0; +} + + +/* Write data to be converted. */ +void RageSoundResampler::write(const void *data_, int bytes) +{ + ASSERT(!at_eof); + + const Sint16 *data = (const Sint16 *) data_; + + const unsigned samples = bytes / sizeof(Sint16); + const unsigned frames = samples / InputChannels; + + if(InputRate == OutputRate) + { + /* Optimization. */ + outbuf.insert(outbuf.end(), data, data+samples); + return; + } + +#if 0 + /* Possibly slightly higher quality, but way too slow. */ + const int upsamp = 8; + const float div = float(InputRate*upsamp) / OutputRate; + for(unsigned f = 0; f < frames; ++f) + { + for(int u = 0; u < upsamp; ++u) + { + int ipos_begin = (int) roundf(ipos / div); + int ipos_end = (int) roundf((ipos+1) / div); + + for(int c = 0; c < InputChannels; ++c) + { + const float f = 0.5f; + prev[c] = Sint16(prev[c] * (f) + data[c] * (1-f)); + for(int i = ipos_begin; i < ipos_end; ++i) + outbuf.push_back(prev[c]); + } + ipos++; + } + data += InputChannels; + } +#else + /* Lerp. */ + const float div = float(InputRate) / OutputRate; + for(unsigned f = 0; f < frames; ++f) + { + for(int c = 0; c < InputChannels; ++c) + { + while(t[c] < 1.0f) + { + Sint16 s = Sint16(prev[c]*(1-t[c]) + data[c]*t[c]); + outbuf.push_back(s); + t[c] += div; + } + + t[c] -= 1; + prev[c] = data[c]; + } + + ipos++; + data += InputChannels; + } +#endif +} + + +void RageSoundResampler::eof() +{ + ASSERT(!at_eof); + + /* Write some silence to flush out the real data. */ + const int size = InputChannels*16; + Sint16 *data = new Sint16[size]; + memset(data, 0, size * sizeof(Sint16)); + write(data, size * sizeof(Sint16)); + delete [] data; + + at_eof = true; +} + + +int RageSoundResampler::read(void *data, unsigned bytes) +{ + /* Don't be silly. */ + ASSERT( (bytes % sizeof(Sint16)) == 0); + + /* If no data is available, and we're at_eof, return -1. */ + if(outbuf.size() == 0 && at_eof) + return -1; + + /* Fill as much as we have. */ + int Avail = min(outbuf.size()*sizeof(Sint16), bytes); + memcpy(data, &outbuf[0], Avail); + outbuf.erase(outbuf.begin(), outbuf.begin() + Avail/sizeof(Sint16)); + return Avail; +} diff --git a/stepmania/src/RageSoundResampler.h b/stepmania/src/RageSoundResampler.h new file mode 100644 index 0000000000..158e30818a --- /dev/null +++ b/stepmania/src/RageSoundResampler.h @@ -0,0 +1,47 @@ +#ifndef RAGE_SOUND_RESAMPLER_H +#define RAGE_SOUND_RESAMPLER_H + +#include "SDL_utils.h" + +enum { MAX_CHANNELS = 15 }; + +class RageSoundResampler +{ + int InputRate, OutputRate; + int InputChannels, OutputChannels; + + Sint16 prev[MAX_CHANNELS]; + + vector outbuf; + bool at_eof; + int ipos; + + float t[MAX_CHANNELS]; + +public: + RageSoundResampler(); + + /* Configuration: */ + void SetInputSampleRate(int hz) { InputRate = hz; } + void SetOutputSampleRate(int hz) { OutputRate = hz; } + void SetInputChannels(int ch) { InputChannels = ch; } + void SetOutputChannels(int ch) { OutputChannels = ch; } + + /* Write data to be converted. */ + void write(const void *data, int bytes); + + /* Indicate that there is no more data. */ + void eof(); + + void reset(); + + /* Return the number of bytes currently readable. */ + unsigned avail() const { return outbuf.size() / 2; } + + /* Read converted data. Returns the number of bytes filled. + * If eof() has been called and the output is completely + * flushed, returns -1. */ + int read(void *data, unsigned bytes); +}; + +#endif diff --git a/stepmania/src/StepMania.dsp b/stepmania/src/StepMania.dsp index 37d936268c..bd706cc821 100644 --- a/stepmania/src/StepMania.dsp +++ b/stepmania/src/StepMania.dsp @@ -57,10 +57,10 @@ LINK32=link.exe # SUBTRACT LINK32 /verbose /pdb:none # Begin Special Build Tool IntDir=.\../Release6 -TargetDir=\stepmania\stepmania +TargetDir=\temp\stepmania TargetName=StepMania SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi # End Special Build Tool @@ -92,10 +92,10 @@ LINK32=link.exe # SUBTRACT LINK32 /verbose /profile /pdb:none /incremental:no /nodefaultlib # Begin Special Build Tool IntDir=.\../Debug6 -TargetDir=\stepmania\stepmania +TargetDir=\temp\stepmania TargetName=StepMania-debug SOURCE="$(InputPath)" -PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ +PreLink_Cmds=disasm\verinc cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ PostBuild_Cmds=disasm\mapconv $(IntDir)\$(TargetName).map $(TargetDir)\StepMania.vdi ia32.vdi # End Special Build Tool @@ -247,6 +247,14 @@ SOURCE=.\RageSoundReader_SDL_Sound.h # End Source File # Begin Source File +SOURCE=.\RageSoundResampler.cpp +# End Source File +# Begin Source File + +SOURCE=.\RageSoundResampler.h +# End Source File +# Begin Source File + SOURCE=.\RageTexture.cpp # End Source File # Begin Source File diff --git a/stepmania/src/StepMania.vcproj b/stepmania/src/StepMania.vcproj index f72219d634..1e9d428d52 100644 --- a/stepmania/src/StepMania.vcproj +++ b/stepmania/src/StepMania.vcproj @@ -1486,6 +1486,12 @@ cl /Zl /nologo /c verstub.cpp /Fo$(IntDir)\ + + + +