- Real-time, sophisticated resampling framework with several performance/quality profiles for dynamically generated windowed sinc and CIC chains based on analysis of

fourier transforms and optimal cost equations. Fast 2-tap linear as a low quality alternative.
- libgambatte: Use deltas and a running sum to decrease buffer writes in sound emulation sample generation.
- libgambatte: Rearrange sound emulation event loop to optimize for high-frequency event units.
- libgambatte: Initialize palette arrays to avoid valgrind noise.
- Don't do resampling in libgambatte. Update API to reflect this.
- Move non-emulation common code to a common directory to avoid duplication.
- Update front-ends to new libgambatte API.
- Utilize resampling framework in front-ends. Selectable resamplers.
- Improved adaptive sleep class that estimates oversleep.
- Gambatte SDL: Estimate actual output sample rate in terms of OS timers and derive frame rate from it.
- Gambatte SDL: Move AudioData and RingBuffer classes to separate files.
- Gambatte SDL: Make underruns slightly less painful, by resetting buffer positions.
- Gambatte Qt: Update AudioEngine to support sample rate estimation in terms of OS timers.
- Gambatte Qt: Implement sample rate estimation in ALSA and OSS audio engines.
- Gambatte Qt: AlsaEngine: Revert to using snd_pcm_avail_update for buffer status since snd_pcm_delay may consider external latencies.
- Gambatte Qt: AlsaEngine: Use snd_pcm_hw_params_set_buffer_time_near. Don't request a particular number of periods per buffer.
- Gambatte Qt: AlsaEngine: Use hw as default custom device string, rather than hw:0,0.
- Gambatte Qt: OssEngine: Don't trust GETOSPACE fragment info.
- Gambatte Qt: Estimate optimal frame rate based on sample rate estimations.
- Gambatte Qt: Extend BlitterWidget to support estimation of vsynced frame rate in terms of OS timers.
- Gambatte Qt: Implement vsync frame rate estimation in QGlBlitter, Direct3DBlitter and DirectDrawBlitter.
- Gambatte Qt: Use a combination of OS timer sample rate estimation and vsync frame rate estimation to derive resampling ratio for no-frame-duplication vsync.
- Gambatte Qt: Change API to reflect MediaSources not being responsible for resampling.
- Gambatte Qt: Make sure to parent PaletteDialog list model, so it gets deleted properly.
- Various refactoring, small changes and stuff I forgot.


git-svn-id: https://gambatte.svn.sourceforge.net/svnroot/gambatte@165 9dfb2916-2d38-0410-aef4-c5fe6c9ffc24
This commit is contained in:
sinamas 2008-10-13 21:08:08 +00:00
parent 6aff6b2cbd
commit 5bbabdcfdd
86 changed files with 3440 additions and 822 deletions

56
common/adaptivesleep.cpp Normal file
View File

@ -0,0 +1,56 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "adaptivesleep.h"
usec_t AdaptiveSleep::sleepUntil(usec_t base, usec_t inc) {
usec_t now = getusecs();
usec_t diff = now - base;
if (diff >= inc)
return diff - inc;
diff = inc - diff;
if (diff > oversleep + oversleepVar) {
diff -= oversleep + oversleepVar;
usecsleep(diff);
const usec_t ideal = now + diff;
now = getusecs();
{
usec_t curOversleep = now - ideal;
if (negate(curOversleep) < curOversleep)
curOversleep = 0;
oversleepVar = (oversleepVar * 15 + (curOversleep < oversleep ? oversleep - curOversleep : curOversleep - oversleep)) >> 4;
oversleep = (oversleep * 15 + curOversleep) >> 4;
}
noSleep = 60;
} else if (--noSleep == 0) {
noSleep = 60;
oversleep = oversleepVar = 0;
}
while (now - base < inc)
now = getusecs();
return 0;
}

34
common/adaptivesleep.h Normal file
View File

@ -0,0 +1,34 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ADAPTIVE_SLEEP_H
#define ADAPTIVE_SLEEP_H
#include "usec.h"
class AdaptiveSleep {
usec_t oversleep;
usec_t oversleepVar;
unsigned noSleep;
public:
AdaptiveSleep() : oversleep(0), oversleepVar(0), noSleep(60) {}
usec_t sleepUntil(usec_t base, usec_t inc);
};
#endif

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -24,13 +24,15 @@
template<typename T>
class Array {
T *a;
std::size_t sz;
Array(const Array &ar);
public:
Array(const std::size_t size = 0) : a(size ? new T[size] : 0) {}
Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {}
~Array() { delete []a; }
void reset(const std::size_t size) { delete []a; a = size ? new T[size] : 0; }
void reset(const std::size_t size) { delete []a; a = size ? new T[size] : 0; sz = size; }
std::size_t size() const { return sz; }
operator T*() { return a; }
operator const T*() const { return a; }
};

48
common/rateest.cpp Normal file
View File

@ -0,0 +1,48 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "rateest.h"
#include <cstdlib>
void RateEst::init(const long srate) {
this->srate.est = srate;
this->srate.var = srate >> 13;
last = 0;
samples = 0;
count = 16;
}
void RateEst::feed(const long samplesIn) {
samples += samplesIn;
if (--count == 0) {
count = 16;
const usec_t now = getusecs();
if (last) {
long est = samples * 1000000.0f / (now - last) + 0.5f;
est = (srate.est * 15 + est + 8) >> 4;
srate.var = (srate.var * 15 + std::abs(est - srate.est) + 8) >> 4;
srate.est = est;
}
last = now;
samples = 0;
}
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -16,29 +16,29 @@
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SAMPLESCALCULATOR_H
#define SAMPLESCALCULATOR_H
#ifndef RATEEST_H
#define RATEEST_H
class SamplesCalculator {
unsigned samples;
unsigned baseSamples;
unsigned updates;
unsigned lastFromUnderrun;
unsigned lastUnderrunTime;
unsigned lastOverflowTime;
unsigned samplesOverflowed;
#include "usec.h"
class RateEst {
public:
struct Result {
long est;
long var;
};
const unsigned maxDiff;
private:
Result srate;
usec_t last;
long samples;
unsigned count;
public:
SamplesCalculator(unsigned baseSamples = 804, unsigned maxDiff = 4);
void setBaseSamples(unsigned samples);
void update(unsigned fromUnderrun, unsigned fromOverflow);
unsigned getSamples() const {
return samples;
}
RateEst(const long srate=0) { init(srate); }
void init(long srate);
void feed(long samples);
const Result& result() const { return srate; }
};
#endif

View File

@ -0,0 +1,100 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef BLACKMANSINC_H
#define BLACKMANSINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic4.h"
#include <cmath>
#include <cstdlib>
template<unsigned channels, unsigned phases>
class BlackmanSinc : public SubResampler {
PolyPhaseConvoluter<channels, phases> convoluters[channels];
short *kernel;
static double blackmanWin(const long i, const long M) {
static const double PI = 3.14159265358979323846;
return 0.42 - 0.5 * std::cos(2 * PI * i / M) + 0.08 * std::cos(4 * PI * i / M);
}
void init(unsigned div, unsigned phaseLen, double fc);
public:
enum { MUL = phases };
typedef Cic4<channels> Cic;
static float cicLimit() { return 4.7f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
static const float widthTimesTaps = 4.5f;
return std::ceil(widthTimesTaps / rollOffWidth);
}
static float toFc(const float rollOffStart, const int taps) {
static const float startToFcDeltaTimesTaps = 1.69f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
BlackmanSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); }
BlackmanSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); }
~BlackmanSinc() { delete[] kernel; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
void adjustDiv(unsigned div);
unsigned mul() const { return MUL; }
unsigned div() const { return convoluters[0].div(); }
};
template<const unsigned channels, const unsigned phases>
void BlackmanSinc<channels, phases>::init(const unsigned div, const unsigned phaseLen, const double fc) {
kernel = new short[phaseLen * phases];
makeSincKernel(kernel, phases, phaseLen, fc, blackmanWin);
for (unsigned i = 0; i < channels; ++i)
convoluters[i].reset(kernel, phaseLen, div);
}
template<const unsigned channels, const unsigned phases>
std::size_t BlackmanSinc<channels, phases>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i)
samplesOut = convoluters[i].filter(out + i, in + i, inlen);
return samplesOut;
}
template<const unsigned channels, const unsigned phases>
void BlackmanSinc<channels, phases>::adjustDiv(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
convoluters[i].adjustDiv(div);
}
#endif

View File

@ -0,0 +1,118 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "chainresampler.h"
float ChainResampler::get2ChainMidRatio(const float ratio, const float rollOff) {
return std::sqrt(0.5f * rollOff * ratio) + 1;
}
float ChainResampler::get2ChainCost(const float ratio, const float rollOff, const float midRatio) {
return midRatio * ratio / ((midRatio - 1) * 2) + midRatio / rollOff;
}
float ChainResampler::get3ChainRatio1(float ratio1, const float rollOff, const float ratio) {
for (unsigned n = 8; n--;) {
ratio1 = std::sqrt(ratio - ratio / get3ChainRatio2(ratio1, rollOff)) + 1;
}
return ratio1;
}
float ChainResampler::get3ChainCost(const float ratio, const float rollOff, const float ratio1, const float ratio2) {
return ratio1 * ratio / ((ratio1 - 1) * 2) + ratio2 * ratio1 / ((ratio2 - 1) * 2) + ratio2 / rollOff;
}
std::size_t ChainResampler::reallocateBuffer() {
std::size_t bufSz[2] = { 0, 0 };
std::size_t inSz = periodSize;
int i = -1;
for (list_t::iterator it = list.begin(); it != list.end(); ++it) {
inSz = (inSz * (*it)->mul() - 1) / (*it)->div() + 1;
++i;
if (inSz > bufSz[i&1])
bufSz[i&1] = inSz;
}
if (inSz >= bufSz[i&1])
bufSz[i&1] = 0;
if (bufferSize < bufSz[0] + bufSz[1]) {
delete[] buffer;
buffer = (bufferSize = bufSz[0] + bufSz[1]) ? new short[bufferSize * channels] : NULL;
}
buffer2 = bufSz[1] ? buffer + bufSz[0] * channels : NULL;
return (maxOut_ = inSz);
}
void ChainResampler::adjustRate(const long inRate, const long outRate) {
unsigned long mul, div;
exactRatio(mul, div);
bigSinc->adjustDiv(static_cast<double>(inRate) * mul / (static_cast<double>(div / bigSinc->div()) * outRate) + 0.5);
reallocateBuffer();
setRate(inRate, outRate);
}
void ChainResampler::exactRatio(unsigned long &mul, unsigned long &div) const {
mul = 1;
div = 1;
for (list_t::const_iterator it = list.begin(); it != list.end(); ++it) {
mul *= (*it)->mul();
div *= (*it)->div();
}
}
std::size_t ChainResampler::resample(short *const out, const short *const in, std::size_t inlen) {
assert(inlen <= periodSize);
short *const buf = buffer != buffer2 ? buffer : out;
short *const buf2 = buffer2 ? buffer2 : out;
const short *inbuf = in;
short *outbuf = NULL;
for (list_t::iterator it = list.begin(); it != list.end(); ++it) {
outbuf = ++list_t::iterator(it) == list.end() ? out : (inbuf == buf ? buf2 : buf);
inlen = (*it)->resample(outbuf, inbuf, inlen);
inbuf = outbuf;
}
return inlen;
}
void ChainResampler::uninit() {
delete[] buffer;
buffer2 = buffer = NULL;
bufferSize = 0;
periodSize = 0;
bigSinc = NULL;
for (list_t::iterator it = list.begin(); it != list.end(); ++it)
delete *it;
list.clear();
}

View File

@ -0,0 +1,189 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CHAINRESAMPLER_H
#define CHAINRESAMPLER_H
#include <cmath>
#include <cstdlib>
#include <cassert>
#include <cstddef>
#include <list>
#include "subresampler.h"
#include "resampler.h"
#include "upsampler.h"
class ChainResampler : public Resampler {
enum { channels = 2 };
typedef std::list<SubResampler*> list_t;
list_t list;
SubResampler *bigSinc;
short *buffer;
short *buffer2;
std::size_t bufferSize;
std::size_t periodSize;
std::size_t maxOut_;
static float get1ChainCost(const float ratio, const float rollOff) {
return ratio / rollOff;
}
static float get2ChainMidRatio(float ratio, float rollOff);
static float get2ChainCost(float ratio, float rollOff, float midRatio);
static float get3ChainRatio2(const float ratio1, const float rollOff) {
return get2ChainMidRatio(ratio1, rollOff);
}
static float get3ChainRatio1(float ratio1, float rollOff, float ratio);
static float get3ChainCost(float ratio, float rollOff, float ratio1, float ratio2);
template<template<unsigned,unsigned> class Sinc>
std::size_t downinit(long inRate, long outRate, std::size_t periodSize);
std::size_t reallocateBuffer();
template<template<unsigned,unsigned> class Sinc>
std::size_t upinit(long inRate, long outRate, std::size_t periodSize);
public:
ChainResampler() : bigSinc(NULL), buffer(NULL), buffer2(NULL), bufferSize(0), periodSize(0) {}
~ChainResampler() { uninit(); }
void adjustRate(long inRate, long outRate);
void exactRatio(unsigned long &mul, unsigned long &div) const;
template<template<unsigned,unsigned> class Sinc>
std::size_t init(long inRate, long outRate, std::size_t periodSize);
std::size_t maxOut(std::size_t /*inlen*/) const { return maxOut_; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
void uninit();
};
template<template<unsigned,unsigned> class Sinc>
std::size_t ChainResampler::init(const long inRate, const long outRate, const std::size_t periodSize) {
setRate(inRate, outRate);
if (outRate > inRate)
return upinit<Sinc>(inRate, outRate, periodSize);
else
return downinit<Sinc>(inRate, outRate, periodSize);
}
template<template<unsigned,unsigned> class Sinc>
std::size_t ChainResampler::downinit(const long inRate, const long outRate, const std::size_t periodSize) {
typedef Sinc<channels,2048> BigSinc;
typedef Sinc<channels,32> SmallSinc;
uninit();
this->periodSize = periodSize;
const float rollOff = std::max((outRate - 36000.0f + outRate - 40000.0f) / outRate, 0.2f);
double ratio = static_cast<double>(inRate) / outRate;
while (ratio >= BigSinc::cicLimit() * 2) {
const int div = std::min<int>(static_cast<int>(ratio / BigSinc::cicLimit()), BigSinc::Cic::MAX_DIV);
list.push_back(new typename BigSinc::Cic(div));
ratio /= div;
}
{
int div_2c = ratio * SmallSinc::MUL / get2ChainMidRatio(ratio, rollOff) + 0.5f;
double ratio_2c = ratio * SmallSinc::MUL / div_2c;
float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c);
if (cost_2c < get1ChainCost(ratio, rollOff)) {
const int div1_3c = ratio * SmallSinc::MUL / get3ChainRatio1(ratio_2c, rollOff, ratio) + 0.5f;
const double ratio1_3c = ratio * SmallSinc::MUL / div1_3c;
const int div2_3c = ratio1_3c * SmallSinc::MUL / get3ChainRatio2(ratio1_3c, rollOff) + 0.5f;
const double ratio2_3c = ratio1_3c * SmallSinc::MUL / div2_3c;
if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) {
list.push_back(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio, (ratio1_3c - 1) / ratio)));
ratio = ratio1_3c;
div_2c = div2_3c;
ratio_2c = ratio2_3c;
}
list.push_back(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio, (ratio_2c - 1) / ratio)));
ratio = ratio_2c;
}
}
list.push_back(bigSinc = new BigSinc(BigSinc::MUL * ratio + 0.5,
typename BigSinc::RollOff(0.5f * (1 + std::max((outRate - 40000.0f) / outRate, 0.0f) - rollOff) / ratio, 0.5f * rollOff / ratio)));
return reallocateBuffer();
}
template<template<unsigned,unsigned> class Sinc>
std::size_t ChainResampler::upinit(const long inRate, const long outRate, const std::size_t periodSize) {
typedef Sinc<channels,2048> BigSinc;
typedef Sinc<channels,32> SmallSinc;
uninit();
this->periodSize = periodSize;
const float rollOff = std::max((inRate - 36000.0f) / inRate, 0.2f);
double ratio = static_cast<double>(outRate) / inRate;
// Spectral images above 20 kHz assumed inaudible
{
const int div = outRate / std::max(inRate, 40000l);
if (div >= 2) {
list.push_front(new Upsampler<channels>(div));
ratio /= div;
}
}
{
int div_2c = get2ChainMidRatio(ratio, rollOff) * SmallSinc::MUL / ratio + 0.5f;
double ratio_2c = ratio * div_2c / SmallSinc::MUL;
float cost_2c = get2ChainCost(ratio, rollOff, ratio_2c);
if (cost_2c < get1ChainCost(ratio, rollOff)) {
const int div1_3c = get3ChainRatio1(ratio_2c, rollOff, ratio) * SmallSinc::MUL / ratio + 0.5f;
const double ratio1_3c = ratio * div1_3c / SmallSinc::MUL;
const int div2_3c = get3ChainRatio2(ratio1_3c, rollOff) * SmallSinc::MUL / ratio1_3c + 0.5f;
const double ratio2_3c = ratio1_3c * div2_3c / SmallSinc::MUL;
if (get3ChainCost(ratio, rollOff, ratio1_3c, ratio2_3c) < cost_2c) {
list.push_front(new SmallSinc(div1_3c, typename SmallSinc::RollOff(0.5f / ratio1_3c, (ratio1_3c - 1) / ratio1_3c)));
ratio = ratio1_3c;
div_2c = div2_3c;
ratio_2c = ratio2_3c;
}
list.push_front(new SmallSinc(div_2c, typename SmallSinc::RollOff(0.5f / ratio_2c, (ratio_2c - 1) / ratio_2c)));
ratio = ratio_2c;
}
}
list.push_front(bigSinc = new BigSinc(BigSinc::MUL / ratio + 0.5, typename BigSinc::RollOff(0.5f * (1 - rollOff), 0.5f * rollOff)));
return reallocateBuffer();
}
#endif

198
common/resample/cic2.h Normal file
View File

@ -0,0 +1,198 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CIC2_H
#define CIC2_H
#include "subresampler.h"
template<unsigned channels>
class Cic2Core {
// enum { BUFLEN = 64 };
// unsigned long buf[BUFLEN];
unsigned long sum1;
unsigned long sum2;
unsigned long prev1;
unsigned long prev2;
unsigned div_;
unsigned nextdivn;
// unsigned bufpos;
public:
Cic2Core(const unsigned div = 2) {
reset(div);
}
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
};
template<const unsigned channels>
void Cic2Core<channels>::reset(const unsigned div) {
sum2 = sum1 = 0;
prev2 = prev1 = 0;
this->div_ = div;
nextdivn = div;
// bufpos = div - 1;
}
template<const unsigned channels>
std::size_t Cic2Core<channels>::filter(short *out, const short *const in, std::size_t inlen) {
// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_;
const std::size_t produced = (inlen + div_ - nextdivn) / div_;
const long mul = 0x10000 / (div_ * div_); // trouble if div is too large, may be better to only support power of 2 div
const short *s = in;
/*unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
while (inlen >> 2) {
unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2);
const unsigned end = n * 4;
unsigned i = 0;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
buf[i++] = sm2 += s1;
buf[i++] = sm2 += sm1;
s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
buf[i++] = sm2 += s1;
buf[i++] = sm2 += sm1;
} while (--n);
while (bufpos < end) {
const unsigned long out2 = buf[bufpos] - prev2;
prev2 = buf[bufpos];
bufpos += div_;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
bufpos -= end;
inlen -= end;
}
if (inlen) {
unsigned n = inlen;
unsigned i = 0;
do {
sm1 += static_cast<long>(*s);
s += channels;
buf[i++] = sm2 += sm1;
} while (--n);
while (bufpos < inlen) {
const unsigned long out2 = buf[bufpos] - prev2;
prev2 = buf[bufpos];
bufpos += div_;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
bufpos -= inlen;
}
sum1 = sm1;
sum2 = sm2;*/
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
if (inlen >= nextdivn) {
unsigned divn = nextdivn;
std::size_t n = produced;
do {
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
} while (--divn);
const unsigned long out2 = sm2 - prev2;
prev2 = sm2;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
divn = div_;
} while (--n);
nextdivn = div_;
}
{
unsigned divn = (in + inlen * channels - s) / channels;
nextdivn -= divn;
while (divn--) {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
}
}
sum1 = sm1;
sum2 = sm2;
return produced;
}
template<unsigned channels>
class Cic2 : public SubResampler {
Cic2Core<channels> cics[channels];
public:
enum { MAX_DIV = 64 };
Cic2(unsigned div);
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return 1; }
unsigned div() const { return cics[0].div(); }
};
template<const unsigned channels>
Cic2<channels>::Cic2(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
cics[i].reset(div);
}
template<const unsigned channels>
std::size_t Cic2<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i) {
samplesOut = cics[i].filter(out + i, in + i, inlen);
}
return samplesOut;
}
#endif

382
common/resample/cic3.h Normal file
View File

@ -0,0 +1,382 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CIC3_H
#define CIC3_H
#include "subresampler.h"
template<unsigned channels>
class Cic3Core {
// enum { BUFLEN = 64 };
// unsigned long buf[BUFLEN];
unsigned long sum1;
unsigned long sum2;
unsigned long sum3;
unsigned long prev1;
unsigned long prev2;
unsigned long prev3;
unsigned div_;
unsigned nextdivn;
// unsigned bufpos;
public:
Cic3Core(const unsigned div = 1) {
reset(div);
}
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
};
template<const unsigned channels>
void Cic3Core<channels>::reset(const unsigned div) {
sum3 = sum2 = sum1 = 0;
prev3 = prev2 = prev1 = 0;
this->div_ = div;
nextdivn = div;
// bufpos = div - 1;
}
template<const unsigned channels>
std::size_t Cic3Core<channels>::filter(short *out, const short *const in, std::size_t inlen) {
// const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_;
const std::size_t produced = (inlen + div_ - nextdivn) / div_;
const long mul = 0x10000 / (div_ * div_ * div_); // trouble if div is too large, may be better to only support power of 2 div
const short *s = in;
/*unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
while (inlen >> 1) {
unsigned n = (inlen < BUFLEN ? inlen >> 1 : BUFLEN >> 1);
const unsigned end = n * 2;
unsigned i = 0;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
buf[i++] = sm3 += s2;
buf[i++] = sm3 += sm2;
} while (--n);
while (bufpos < end) {
const unsigned long out3 = buf[bufpos] - prev3;
prev3 = buf[bufpos];
bufpos += div_;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
bufpos -= end;
inlen -= end;
}
if (inlen) {
unsigned n = inlen;
unsigned i = 0;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
buf[i++] = sm3 += sm2;
} while (--n);
while (bufpos < inlen) {
const unsigned long out3 = buf[bufpos] - prev3;
prev3 = buf[bufpos];
bufpos += div_;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
bufpos -= inlen;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;*/
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
if (inlen >= nextdivn) {
unsigned divn = nextdivn;
std::size_t n = produced;
do {
do {
sm1 += static_cast<long>(*s);
sm2 += sm1;
sm3 += sm2;
s += channels;
} while (--divn);
const unsigned long out3 = sm3 - prev3;
prev3 = sm3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
divn = div_;
} while (--n);
nextdivn = div_;
}
{
unsigned divn = (in + inlen * channels - s) / channels;
nextdivn -= divn;
while (divn--) {
sm1 += static_cast<long>(*s);
sm2 += sm1;
sm3 += sm2;
s += channels;
}
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
return produced;
}
/*template<unsigned channels>
class Cic3EvenOddCore {
unsigned long sum1;
unsigned long sum2;
unsigned long sum3;
unsigned long prev1;
unsigned long prev2;
unsigned long prev3;
unsigned div_;
unsigned nextdivn;
static int getMul(unsigned div) {
return 0x10000 / (div * div * div); // trouble if div is too large, may be better to only support power of 2 div
}
void filterEven(short *out, const short *s, std::size_t n);
void filterOdd(short *out, const short *s, std::size_t n);
public:
Cic3EvenOddCore(const unsigned div = 2) {
reset(div);
}
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
};
template<const unsigned channels>
void Cic3EvenOddCore<channels>::reset(const unsigned div) {
sum3 = sum2 = sum1 = 0;
prev3 = prev2 = prev1 = 0;
this->div_ = div;
nextdivn = div;
}
template<const unsigned channels>
void Cic3EvenOddCore<channels>::filterEven(short *out, const short *s, std::size_t n) {
const int mul = getMul(div_);
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
while (n--) {
{
unsigned sn = div_ >> 1;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
sm3 += s2;
sm3 += sm2;
} while (--sn);
}
const unsigned long out3 = sm3 - prev3;
prev3 = sm3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
}
template<const unsigned channels>
void Cic3EvenOddCore<channels>::filterOdd(short *out, const short *s, std::size_t n) {
const int mul = getMul(div_);
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
while (n--) {
{
unsigned sn = div_ >> 1;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
sm3 += s2;
sm3 += sm2;
} while (--sn);
}
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
const unsigned long out3 = sm3 - prev3;
prev3 = sm3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
}
template<const unsigned channels>
std::size_t Cic3EvenOddCore<channels>::filter(short *out, const short *const in, std::size_t inlen) {
short *const outStart = out;
const short *s = in;
if (inlen >= nextdivn) {
{
{
unsigned divn = nextdivn;
do {
sum1 += static_cast<long>(*s);
s += channels;
sum2 += sum1;
sum3 += sum2;
} while (--divn);
}
const unsigned long out3 = sum3 - prev3;
prev3 = sum3;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * getMul(div_) / 0x10000;
prev1 = out2;
out += channels;
}
std::size_t n = (inlen - nextdivn) / div_;
if (div_ & 1)
filterOdd(out, s, n);
else
filterEven(out, s, n);
s += n * div_ * channels;
out += n * channels;
nextdivn = div_;
}
{
unsigned divn = inlen - (s - in) / channels;
nextdivn -= divn;
while (divn--) {
sum1 += static_cast<long>(*s);
s += channels;
sum2 += sum1;
sum3 += sum2;
}
}
return (out - outStart) / channels;
}*/
template<unsigned channels>
class Cic3 : public SubResampler {
Cic3Core<channels> cics[channels];
public:
enum { MAX_DIV = 23 };
Cic3(unsigned div);
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return 1; }
unsigned div() const { return cics[0].div(); }
};
template<const unsigned channels>
Cic3<channels>::Cic3(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
cics[i].reset(div);
}
template<const unsigned channels>
std::size_t Cic3<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i) {
samplesOut = cics[i].filter(out + i, in + i, inlen);
}
return samplesOut;
}
#endif

237
common/resample/cic4.h Normal file
View File

@ -0,0 +1,237 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CIC4_H
#define CIC4_H
#include "subresampler.h"
template<unsigned channels>
class Cic4Core {
enum { BUFLEN = 64 };
unsigned long buf[BUFLEN];
unsigned long sum1;
unsigned long sum2;
unsigned long sum3;
unsigned long sum4;
unsigned long prev1;
unsigned long prev2;
unsigned long prev3;
unsigned long prev4;
unsigned div_;
// unsigned nextdivn;
unsigned bufpos;
public:
Cic4Core(const unsigned div = 1) {
reset(div);
}
unsigned div() const { return div_; }
std::size_t filter(short *out, const short *in, std::size_t inlen);
void reset(unsigned div);
};
template<const unsigned channels>
void Cic4Core<channels>::reset(const unsigned div) {
sum4 = sum3 = sum2 = sum1 = 0;
prev4 = prev3 = prev2 = prev1 = 0;
this->div_ = div;
// nextdivn = div;
bufpos = div - 1;
}
template<const unsigned channels>
std::size_t Cic4Core<channels>::filter(short *out, const short *const in, std::size_t inlen) {
const std::size_t produced = (inlen + div_ - (bufpos + 1)) / div_;
// const std::size_t produced = (inlen + div_ - nextdivn) / div_;
const long mul = 0x10000 / (div_ * div_ * div_ * div_); // trouble if div is too large, may be better to only support power of 2 div
const short *s = in;
unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
unsigned long sm4 = sum4;
while (inlen >> 2) {
unsigned n = (inlen < BUFLEN ? inlen >> 2 : BUFLEN >> 2);
const unsigned end = n * 4;
unsigned i = 0;
do {
unsigned long s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
unsigned long s2 = sm2 += s1;
sm2 += sm1;
unsigned long s3 = sm3 += s2;
sm3 += sm2;
buf[i++] = sm4 += s3;
buf[i++] = sm4 += sm3;
s1 = sm1 += static_cast<long>(*s);
s += channels;
sm1 += static_cast<long>(*s);
s += channels;
s2 = sm2 += s1;
sm2 += sm1;
s3 = sm3 += s2;
sm3 += sm2;
buf[i++] = sm4 += s3;
buf[i++] = sm4 += sm3;
} while (--n);
while (bufpos < end) {
const unsigned long out4 = buf[bufpos] - prev4;
prev4 = buf[bufpos];
bufpos += div_;
const unsigned long out3 = out4 - prev3;
prev3 = out4;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
bufpos -= end;
inlen -= end;
}
if (inlen) {
unsigned n = inlen;
unsigned i = 0;
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
buf[i++] = sm4 += sm3;
} while (--n);
while (bufpos < inlen) {
const unsigned long out4 = buf[bufpos] - prev4;
prev4 = buf[bufpos];
bufpos += div_;
const unsigned long out3 = out4 - prev3;
prev3 = out4;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
}
bufpos -= inlen;
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
sum4 = sm4;
/*unsigned long sm1 = sum1;
unsigned long sm2 = sum2;
unsigned long sm3 = sum3;
unsigned long sm4 = sum4;
if (produced) {
unsigned divn = nextdivn;
std::size_t n = produced;
do {
do {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
sm4 += sm3;
} while (--divn);
const unsigned long out4 = sm4 - prev4;
prev4 = sm4;
const unsigned long out3 = out4 - prev3;
prev3 = out4;
const unsigned long out2 = out3 - prev2;
prev2 = out3;
*out = static_cast<long>(out2 - prev1) * mul / 0x10000;
prev1 = out2;
out += channels;
divn = div_;
} while (--n);
nextdivn = div_;
}
{
unsigned divn = (in + inlen * channels - s) / channels;
nextdivn -= divn;
while (divn--) {
sm1 += static_cast<long>(*s);
s += channels;
sm2 += sm1;
sm3 += sm2;
sm4 += sm3;
}
}
sum1 = sm1;
sum2 = sm2;
sum3 = sm3;
sum4 = sm4;*/
return produced;
}
template<unsigned channels>
class Cic4 : public SubResampler {
Cic4Core<channels> cics[channels];
public:
enum { MAX_DIV = 13 };
Cic4(unsigned div);
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return 1; }
unsigned div() const { return cics[0].div(); }
};
template<const unsigned channels>
Cic4<channels>::Cic4(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
cics[i].reset(div);
}
template<const unsigned channels>
std::size_t Cic4<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i) {
samplesOut = cics[i].filter(out + i, in + i, inlen);
}
return samplesOut;
}
#endif

View File

@ -0,0 +1,156 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CONVOLUTER_H
#define CONVOLUTER_H
#include <algorithm>
#include <cstring>
template<unsigned channels, unsigned phases>
class PolyPhaseConvoluter {
const short *kernel;
short *prevbuf;
unsigned phaseLen;
unsigned div_;
unsigned x_;
public:
PolyPhaseConvoluter() : kernel(NULL), prevbuf(NULL), phaseLen(0), div_(0), x_(0) {}
PolyPhaseConvoluter(const short *kernel, unsigned phaseLen, unsigned div) { reset(kernel, phaseLen, div); }
~PolyPhaseConvoluter() { delete[] prevbuf; }
void reset(const short *kernel, unsigned phaseLen, unsigned div);
std::size_t filter(short *out, const short *in, std::size_t inlen);
void adjustDiv(const unsigned div) { this->div_ = div; }
unsigned div() const { return div_; }
};
template<const unsigned channels, const unsigned phases>
void PolyPhaseConvoluter<channels, phases>::reset(const short *const kernel, const unsigned phaseLen, const unsigned div) {
this->kernel = kernel;
this->phaseLen = phaseLen;
this->div_ = div;
x_ = 0;
delete[] prevbuf;
prevbuf = new short[phaseLen];
std::fill(prevbuf, prevbuf + phaseLen, 0);
}
template<const unsigned channels, const unsigned phases>
std::size_t PolyPhaseConvoluter<channels, phases>::filter(short *out, const short *const in, std::size_t inlen) {
if (!kernel || !inlen)
return 0;
/*for (std::size_t x = 0; x < inlen + M; ++x) {
const int end = x < inlen ? M + 1 : inlen + M - x;
int j = x < M ? M - x : 0;
j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample
for (; j < end; j += phases) {
buffer[x] += kernel[j] * start[(x - M + j) / phases];
}
}*/
/*for (std::size_t x = 0; x < inlen + M; ++x) {
const int end = x < inlen ? M + 1 : inlen + M - x;
int j = x < M ? M - x : 0;
j += (phases - (x - M + j) % phases) % phases; // adjust j so we don't start on a virtual 0 sample
const short *k = kernel + (j % phases) * phaseLen + j / phases;
const short *s = start + (x - M + j) / phases;
int n = ((end - j) + phases - 1) / phases;
do {
buffer[x] += *k++ * *s++;
} while (--n);
}*/
const std::size_t M = phaseLen * phases - 1;
inlen *= phases;
std::size_t x = x_;
for (; x < (M < inlen ? M : inlen); x += div_) {
long acc = 0;
const unsigned phase = (phases - (x + 1) % phases) % phases; // adjust phase so we don't start on a virtual 0 sample
const short *s = prevbuf + (x + 1 + phase) / phases;
const short *k = kernel + phase * phaseLen;
unsigned n = prevbuf + phaseLen - s;
while (n--) {
acc += *k++ * *s++;
}
s = in;
n = x / phases + 1;
do {
acc += *k++ * *s;
s += channels;
} while (--n);
*out = acc / 0x10000;
out += channels;
}
for (; x < inlen; x += div_) {
long acc = 0;
const unsigned phase = (phases - (x - M) % phases) % phases; // adjust phase so we don't start on a virtual 0 sample
const short *s = in + ((x - M + phase) / phases) * channels;
const short *k = kernel + phase * phaseLen;
// unsigned n = (M + 1/* - phase + phases - 1*/) / phases;
unsigned n = phaseLen;
do {
acc += *k++ * *s;
s += channels;
} while (--n);
*out = acc / 0x10000;
out += channels;
}
const std::size_t produced = (x - x_) / div_;
x_ = x - inlen;
inlen /= phases;
{
short *p = prevbuf;
const short *s = in + (inlen - phaseLen) * channels;
unsigned n = phaseLen;
if (inlen < phaseLen) {
const unsigned i = phaseLen - inlen;
std::memmove(p, p + inlen, i * sizeof(short));
p += i;
n -= i;
s = in;
}
do {
*p++ = *s;
s += channels;
} while (--n);
}
return produced;
}
#endif

View File

@ -0,0 +1,100 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef HAMMINGSINC_H
#define HAMMINGSINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic3.h"
#include <cmath>
#include <cstdlib>
template<unsigned channels, unsigned phases>
class HammingSinc : public SubResampler {
PolyPhaseConvoluter<channels, phases> convoluters[channels];
short *kernel;
static double hammingWin(const long i, const long M) {
static const double PI = 3.14159265358979323846;
return 0.53836 - 0.46164 * std::cos(2 * PI * i / M);
}
void init(unsigned div, unsigned phaseLen, double fc);
public:
enum { MUL = phases };
typedef Cic3<channels> Cic;
static float cicLimit() { return 4.2f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
static const float widthTimesTaps = 3.0f;
return std::ceil(widthTimesTaps / rollOffWidth);
}
static float toFc(const float rollOffStart, const int taps) {
static const float startToFcDeltaTimesTaps = 1.27f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
HammingSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); }
HammingSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); }
~HammingSinc() { delete[] kernel; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
void adjustDiv(unsigned div);
unsigned mul() const { return MUL; }
unsigned div() const { return convoluters[0].div(); }
};
template<const unsigned channels, const unsigned phases>
void HammingSinc<channels, phases>::init(const unsigned div, const unsigned phaseLen, const double fc) {
kernel = new short[phaseLen * phases];
makeSincKernel(kernel, phases, phaseLen, fc, hammingWin);
for (unsigned i = 0; i < channels; ++i)
convoluters[i].reset(kernel, phaseLen, div);
}
template<const unsigned channels, const unsigned phases>
std::size_t HammingSinc<channels, phases>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i)
samplesOut = convoluters[i].filter(out + i, in + i, inlen);
return samplesOut;
}
template<const unsigned channels, const unsigned phases>
void HammingSinc<channels, phases>::adjustDiv(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
convoluters[i].adjustDiv(div);
}
#endif

129
common/resample/linint.h Normal file
View File

@ -0,0 +1,129 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef LININT_H
#define LININT_H
#include <cstddef>
#include "resampler.h"
#include "u48div.h"
template<unsigned channels>
class LinintCore {
unsigned long ratio;
std::size_t pos_;
unsigned fracPos_;
int prevSample_;
public:
LinintCore(long inRate = 1, long outRate = 1) { init(inRate, outRate); }
void adjustRate(long inRate, long outRate) { ratio = (static_cast<double>(inRate) / outRate) * 0x10000 + 0.5; }
void exactRatio(unsigned long &mul, unsigned long &div) const { mul = 0x10000; div = ratio; }
void init(long inRate, long outRate);
std::size_t maxOut(std::size_t inlen) const { return inlen ? u48div(inlen - 1, 0xFFFF, ratio) + 1 : 0; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
};
template<const unsigned channels>
void LinintCore<channels>::init(const long inRate, const long outRate) {
adjustRate(inRate, outRate);
pos_ = (ratio >> 16) + 1;
fracPos_ = ratio & 0xFFFF;
prevSample_ = 0;
}
template<const unsigned channels>
std::size_t LinintCore<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t opos = 0;
std::size_t pos = pos_;
unsigned fracPos = fracPos_;
int prevSample = prevSample_;
if (pos < inlen) {
if (pos != 0)
prevSample = in[(pos-1) * channels];
for (;;) {
out[opos] = prevSample + (in[pos * channels] - prevSample) * static_cast<long>(fracPos) / 0x10000;
opos += channels;
{
const unsigned long next = ratio + fracPos;
pos += next >> 16;
fracPos = next & 0xFFFF;
}
if (pos < inlen) {
prevSample = in[(pos-1) * channels];
} else
break;
}
if (pos == inlen)
prevSample = in[(pos-1) * channels];
}
// const std::size_t produced = ((pos - pos_) * 0x10000 + fracPos - fracPos_) / ratio;
pos_ = pos - inlen;
fracPos_ = fracPos;
prevSample_ = prevSample;
return opos / channels;
}
template<unsigned channels>
class Linint : public Resampler {
LinintCore<channels> cores[channels];
public:
Linint(long inRate, long outRate);
void adjustRate(long inRate, long outRate);
void exactRatio(unsigned long &mul, unsigned long &div) const { cores[0].exactRatio(mul, div); }
std::size_t maxOut(std::size_t inlen) const { return cores[0].maxOut(inlen); }
std::size_t resample(short *out, const short *in, std::size_t inlen);
};
template<const unsigned channels>
Linint<channels>::Linint(const long inRate, const long outRate) {
setRate(inRate, outRate);
for (unsigned i = 0; i < channels; ++i)
cores[i].init(inRate, outRate);
}
template<const unsigned channels>
void Linint<channels>::adjustRate(const long inRate, const long outRate) {
setRate(inRate, outRate);
for (unsigned i = 0; i < channels; ++i)
cores[i].adjustRate(inRate, outRate);
}
template<const unsigned channels>
std::size_t Linint<channels>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t outlen = 0;
for (unsigned i = 0; i < channels; ++i)
outlen = cores[i].resample(out + i, in + i, inlen);
return outlen;
}
#endif

View File

@ -0,0 +1,104 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef MAKE_SINC_KERNEL_H
#define MAKE_SINC_KERNEL_H
#include <cmath>
#include <cstdlib>
template<class Window>
void makeSincKernel(short *const kernel, const unsigned phases, const unsigned phaseLen, double fc, Window win) {
static const double PI = 3.14159265358979323846;
fc /= phases;
/*{
double *const dkernel = new double[phaseLen * phases];
const long M = static_cast<long>(phaseLen) * phases - 1;
for (long i = 0; i < M + 1; ++i) {
const double sinc = i * 2 == M ?
PI * fc :
std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M);
dkernel[(i % phases) * phaseLen + i / phases] = win(i, M) * sinc;
}
{
double gain = 0;
for (long i = 0; i < M + 1; ++i)
gain += std::abs(dkernel[i]);
gain = phases * 0x10000 / gain;
for (long i = 0; i < M + 1; ++i)
kernel[i] = std::floor(dkernel[i] * gain + 0.5);
}
delete[] dkernel;
}*/
// The following is equivalent to the more readable version above
const long M = static_cast<long>(phaseLen) * phases - 1;
{
double *const dkernel = new double[M / 2 + 1];
for (long i = 0; i < M / 2 + 1; ++i) {
const double sinc = i * 2 == M ?
PI * fc :
std::sin(PI * fc * (i * 2 - M)) / (i * 2 - M);
dkernel[i] = win(i, M) * sinc;
}
double gain = 0;
for (long i = 0; i < M / 2; ++i)
gain += std::abs(dkernel[i]);
gain *= 2;
gain += std::abs((M & 1) ? dkernel[M / 2] * 2 : dkernel[M / 2]);
gain = phases * 0x10000 / gain;
{
long kpos = 0;
for (long i = 0; i < M / 2 + 1; ++i) {
kernel[kpos] = std::floor(dkernel[i] * gain + 0.5);
if ((kpos += phaseLen) > M)
kpos -= M;
}
}
delete[] dkernel;
}
for (unsigned phase = 0; phase < phases; ++phase) {
short *k = kernel + phase * phaseLen;
short *km = kernel + M - phase * phaseLen;
for (long i = phase; i < M / 2 + 1; i += phases)
*km-- = *k++;
}
}
#endif

View File

@ -0,0 +1,99 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RECTSINC_H
#define RECTSINC_H
#include "convoluter.h"
#include "subresampler.h"
#include "makesinckernel.h"
#include "cic2.h"
#include <cmath>
#include <cstdlib>
template<unsigned channels, unsigned phases>
class RectSinc : public SubResampler {
PolyPhaseConvoluter<channels, phases> convoluters[channels];
short *kernel;
static double rectWin(const long /*i*/, const long /*M*/) {
return 1;
}
void init(unsigned div, unsigned phaseLen, double fc);
public:
enum { MUL = phases };
typedef Cic2<channels> Cic;
static float cicLimit() { return 2.0f; }
class RollOff {
static unsigned toTaps(const float rollOffWidth) {
static const float widthTimesTaps = 0.9f;
return std::ceil(widthTimesTaps / rollOffWidth);
}
static float toFc(const float rollOffStart, const int taps) {
static const float startToFcDeltaTimesTaps = 0.43f;
return startToFcDeltaTimesTaps / taps + rollOffStart;
}
public:
const unsigned taps;
const float fc;
RollOff(float rollOffStart, float rollOffWidth) : taps(toTaps(rollOffWidth)), fc(toFc(rollOffStart, taps)) {}
};
RectSinc(unsigned div, unsigned phaseLen, double fc) { init(div, phaseLen, fc); }
RectSinc(unsigned div, RollOff ro) { init(div, ro.taps, ro.fc); }
~RectSinc() { delete[] kernel; }
std::size_t resample(short *out, const short *in, std::size_t inlen);
void adjustDiv(unsigned div);
unsigned mul() const { return MUL; }
unsigned div() const { return convoluters[0].div(); }
};
template<const unsigned channels, const unsigned phases>
void RectSinc<channels, phases>::init(const unsigned div, const unsigned phaseLen, const double fc) {
kernel = new short[phaseLen * phases];
makeSincKernel(kernel, phases, phaseLen, fc, rectWin);
for (unsigned i = 0; i < channels; ++i)
convoluters[i].reset(kernel, phaseLen, div);
}
template<const unsigned channels, const unsigned phases>
std::size_t RectSinc<channels, phases>::resample(short *const out, const short *const in, const std::size_t inlen) {
std::size_t samplesOut;
for (unsigned i = 0; i < channels; ++i)
samplesOut = convoluters[i].filter(out + i, in + i, inlen);
return samplesOut;
}
template<const unsigned channels, const unsigned phases>
void RectSinc<channels, phases>::adjustDiv(const unsigned div) {
for (unsigned i = 0; i < channels; ++i)
convoluters[i].adjustDiv(div);
}
#endif

View File

@ -0,0 +1,43 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RESAMPLER_H
#define RESAMPLER_H
#include <cstddef>
class Resampler {
long inRate_;
long outRate_;
protected:
void setRate(const long inRate, const long outRate) { inRate_ = inRate; outRate_ = outRate; }
public:
Resampler() : inRate_(0), outRate_(0) {}
long inRate() const { return inRate_; }
long outRate() const { return outRate_; }
virtual void adjustRate(long inRate, long outRate) = 0;
virtual void exactRatio(unsigned long &mul, unsigned long &div) const = 0;
virtual std::size_t maxOut(std::size_t inlen) const = 0;
virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0;
virtual ~Resampler() {}
};
#endif

View File

@ -0,0 +1,61 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "resamplerinfo.h"
#include "chainresampler.h"
#include "hammingsinc.h"
#include "blackmansinc.h"
#include "rectsinc.h"
#include "linint.h"
struct LinintInfo {
static Resampler* create(long inRate, long outRate, std::size_t) { return new Linint<2>(inRate, outRate); }
};
struct RectsincInfo {
static Resampler* create(long inRate, long outRate, std::size_t periodSz) {
ChainResampler *r = new ChainResampler;
r->init<RectSinc>(inRate, outRate, periodSz);
return r;
}
};
struct HammingsincInfo {
static Resampler* create(long inRate, long outRate, std::size_t periodSz) {
ChainResampler *r = new ChainResampler;
r->init<HammingSinc>(inRate, outRate, periodSz);
return r;
}
};
struct BlackmansincInfo {
static Resampler* create(long inRate, long outRate, std::size_t periodSz) {
ChainResampler *r = new ChainResampler;
r->init<BlackmanSinc>(inRate, outRate, periodSz);
return r;
}
};
const ResamplerInfo ResamplerInfo::resamplers[] = {
{ "2-tap linear interpolation", LinintInfo::create },
{ "Rectangular windowed sinc (~20 dB SNR)", RectsincInfo::create },
{ "Hamming windowed sinc (~50 dB SNR)", HammingsincInfo::create },
{ "Blackman windowed sinc (~70 dB SNR)", BlackmansincInfo::create }
};
const unsigned ResamplerInfo::num_ = sizeof(ResamplerInfo::resamplers) / sizeof(ResamplerInfo);

View File

@ -0,0 +1,36 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RESAMPLER_INFO_H
#define RESAMPLER_INFO_H
#include "resampler.h"
struct ResamplerInfo {
const char *desc;
Resampler* (*create)(long inRate, long outRate, std::size_t periodSz);
static unsigned num() { return num_; }
static const ResamplerInfo& get(unsigned n) { return resamplers[n]; }
private:
static const ResamplerInfo resamplers[];
static const unsigned num_;
};
#endif

View File

@ -0,0 +1,33 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SUBRESAMPLER_H
#define SUBRESAMPLER_H
#include <cstddef>
class SubResampler {
public:
virtual std::size_t resample(short *out, const short *in, std::size_t inlen) = 0;
virtual unsigned mul() const = 0;
virtual unsigned div() const = 0;
virtual void adjustDiv(unsigned /*div*/) {}
virtual ~SubResampler() {}
};
#endif

View File

@ -0,0 +1,54 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "u48div.h"
unsigned long u48div(unsigned long num1, unsigned num2, const unsigned long den) {
unsigned long res = 0;
unsigned s = 16;
do {
if (num1 < 0x10000) {
num1 <<= s;
num1 |= num2 & ((1 << s) - 1);
s = 0;
} else {
if (num1 < 0x1000000) {
const unsigned maxs = s < 8 ? s : 8;
num1 <<= maxs;
num1 |= (num2 >> (s -= maxs)) & ((1 << maxs) - 1);
}
if (num1 < 0x10000000) {
const unsigned maxs = s < 4 ? s : 4;
num1 <<= maxs;
num1 |= (num2 >> (s -= maxs)) & ((1 << maxs) - 1);
}
while (num1 < den && s) {
num1 <<= 1; // if this overflows we're screwed
num1 |= num2 >> --s & 1;
}
}
res += (num1 / den) << s;
num1 = (num1 % den);
} while (s);
return res;
}

24
common/resample/u48div.h Normal file
View File

@ -0,0 +1,24 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef U48DIV_H
#define U48DIV_H
unsigned long u48div(unsigned long num1, unsigned num2, unsigned long den);
#endif

View File

@ -1,57 +1,51 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SCALEBUFFER_H_
#define SCALEBUFFER_H_
#include <cstring>
template<typename T>
static inline void do_scaleBuffer(const T *s, T *d, const unsigned srcW, const unsigned srcH, const unsigned dstPitch, const unsigned scale) {
const unsigned dstW = srcW * scale;
for (unsigned h = srcH; h--;) {
for (unsigned w = srcW; w--;) {
for (unsigned n = scale; n--;)
*d++ = *s;
++s;
}
s += dstPitch - dstW;
for (unsigned n = scale; --n; d += dstPitch)
std::memcpy(d, d - dstPitch, dstW * sizeof(T));
}
}
template<typename T>
void scaleBuffer(const T *s, T *d, const unsigned srcW, const unsigned srcH, const unsigned dstPitch, const unsigned scale) {
switch (scale) {
case 2: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 2); break;
case 3: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 3); break;
case 4: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 4); break;
case 5: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 5); break;
case 6: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 6); break;
case 7: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 7); break;
case 8: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 8); break;
default: do_scaleBuffer(s, d, srcW, srcH, dstPitch, scale); break;
}
}
#endif /*SCALEBUFFER_H_*/
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef UPSAMPLER_H
#define UPSAMPLER_H
#include "subresampler.h"
#include <cstring>
template<unsigned channels>
class Upsampler : public SubResampler {
unsigned mul_;
public:
Upsampler(const unsigned mul) : mul_(mul) {}
std::size_t resample(short *out, const short *in, std::size_t inlen);
unsigned mul() const { return mul_; }
unsigned div() const { return 1; }
};
template<const unsigned channels>
std::size_t Upsampler<channels>::resample(short *out, const short *in, std::size_t inlen) {
if (inlen) {
std::memset(out, 0, inlen * mul_ * sizeof(short) * channels);
do {
std::memcpy(out, in, sizeof(short) * channels);
in += channels;
out += mul_ * channels;
} while (--inlen);
}
return inlen * mul_;
}
#endif

31
common/usec.h Normal file
View File

@ -0,0 +1,31 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef USEC_H
#define USEC_H
typedef unsigned long usec_t;
static inline usec_t negate(usec_t t) {
return usec_t(0) - t;
}
usec_t getusecs();
void usecsleep(usec_t usecs);
#endif

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -22,8 +22,12 @@
class QWidget;
#include <QString>
#include <rateest.h>
class AudioEngine {
int rate_;
virtual int doInit(int rate, unsigned msLatency) = 0;
public:
struct BufferState {
enum { NOT_SUPPORTED = 0xFFFFFFFFu };
@ -32,12 +36,14 @@ public:
};
const QString nameString;
int rate() const { return rate_; }
AudioEngine(const QString &name) : nameString(name) {}
AudioEngine(const QString &name) : rate_(0), nameString(name) {}
virtual ~AudioEngine() {}
virtual int init(int rate, unsigned msLatency) = 0;
int init(int rate, unsigned msLatency) { return rate_ = doInit(rate, msLatency); }
virtual void uninit() {}
virtual int write(void *buffer, unsigned samples) = 0;
virtual const RateEst::Result rateEstimate() const { const RateEst::Result r = { rate_, 0 }; return r; }
virtual const BufferState bufferState() const { static const BufferState s = { BufferState::NOT_SUPPORTED, BufferState::NOT_SUPPORTED }; return s; }
virtual void pause() {}
virtual QWidget* settingsWidget() { return NULL; }

View File

@ -17,23 +17,26 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "alsaengine.h"
#include <algorithm>
#include <cstdio>
AlsaEngine::AlsaEngine() :
AudioEngine("ALSA"),
conf("Custom PCM device:", "default", "alsaengine", "hw:0,0"),
conf("Custom PCM device:", "default", "alsaengine", "hw"),
pcm_handle(NULL),
bufSize(0)
bufSize(0),
prevfur(0)
{}
AlsaEngine::~AlsaEngine() {
uninit();
}
int AlsaEngine::init(const int inrate, const unsigned latency) {
int AlsaEngine::doInit(const int inrate, const unsigned latency) {
unsigned rate = inrate;
if (snd_pcm_open(&pcm_handle, conf.device(), SND_PCM_STREAM_PLAYBACK, 0) < 0) {
fprintf(stderr, "Error opening PCM device %s\n", conf.device());
std::fprintf(stderr, "Error opening PCM device %s\n", conf.device());
pcm_handle = NULL;
goto fail;
}
@ -43,35 +46,35 @@ int AlsaEngine::init(const int inrate, const unsigned latency) {
snd_pcm_hw_params_alloca(&hwparams);
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
fprintf(stderr, "Can not configure this PCM device.\n");
std::fprintf(stderr, "Can not configure this PCM device.\n");
goto fail;
}
if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
fprintf(stderr, "Error setting access.\n");
std::fprintf(stderr, "Error setting access.\n");
goto fail;
}
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16) < 0) {
fprintf(stderr, "Error setting format.\n");
std::fprintf(stderr, "Error setting format.\n");
goto fail;
}
if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0) < 0) {
fprintf(stderr, "Error setting rate.\n");
std::fprintf(stderr, "Error setting rate.\n");
goto fail;
}
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0) {
fprintf(stderr, "Error setting channels.\n");
std::fprintf(stderr, "Error setting channels.\n");
goto fail;
}
{
/*{
unsigned periods = 2;
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, 0) < 0) {
fprintf(stderr, "Error setting periods %u.\n", periods);
std::fprintf(stderr, "Error setting periods %u.\n", periods);
goto fail;
}
}
@ -80,19 +83,42 @@ int AlsaEngine::init(const int inrate, const unsigned latency) {
snd_pcm_uframes_t bSize = (rate * latency + 500) / 1000;
if (snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &bSize) < 0) {
fprintf(stderr, "Error setting buffer size %u.\n", static_cast<unsigned>(bSize));
std::fprintf(stderr, "Error setting buffer size %u.\n", static_cast<unsigned>(bSize));
goto fail;
}
bufSize = bSize;
}*/
{
unsigned ulatency = latency * 1000;
if (snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &ulatency, 0) < 0) {
std::fprintf(stderr, "Error setting buffer latency %u.\n", ulatency);
goto fail;
}
}
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
std::fprintf(stderr, "Error setting HW params.\n");
goto fail;
}
{
snd_pcm_uframes_t bSize = 0;
if (snd_pcm_hw_params_get_buffer_size(hwparams, &bSize) < 0) {
std::fprintf(stderr, "Error getting buffer size\n");
goto fail;
}
bufSize = bSize;
}
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
fprintf(stderr, "Error setting HW params.\n");
goto fail;
}
}
prevfur = 0;
est.init(rate);
return rate;
fail:
@ -108,10 +134,24 @@ void AlsaEngine::uninit() {
}
int AlsaEngine::write(void *const buffer, const unsigned samples) {
bool underrun = false;
const BufferState &bstate = bufferState();
if (prevfur > bstate.fromUnderrun && bstate.fromUnderrun != BufferState::NOT_SUPPORTED) {
est.feed(prevfur - bstate.fromUnderrun);
underrun = bstate.fromUnderrun == 0;
}
prevfur = bstate.fromUnderrun + samples;
while (snd_pcm_writei(pcm_handle, buffer, samples) < 0) {
snd_pcm_prepare(pcm_handle);
underrun = true;
}
if (underrun)
est.init(std::min(est.result().est + (est.result().est >> 10), (long) rate() + (rate() >> 4)));
return 0;
}
@ -119,14 +159,20 @@ const AudioEngine::BufferState AlsaEngine::bufferState() const {
BufferState s;
snd_pcm_sframes_t avail;
if (snd_pcm_delay(pcm_handle, &avail)) {
snd_pcm_hwsync(pcm_handle);
avail = snd_pcm_avail_update(pcm_handle);
if (avail == -EPIPE)
avail = bufSize;
if (avail < 0) {
s.fromOverflow = s.fromUnderrun = BufferState::NOT_SUPPORTED;
} else {
if (avail < 0)
avail = 0;
if (static_cast<unsigned>(avail) > bufSize)
avail = bufSize;
s.fromUnderrun = avail;
s.fromOverflow = bufSize - avail;
s.fromUnderrun = bufSize - avail;
s.fromOverflow = avail;
}
return s;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -25,16 +25,21 @@
class AlsaEngine : public AudioEngine {
CustomDevConf conf;
RateEst est;
snd_pcm_t *pcm_handle;
unsigned bufSize;
unsigned prevfur;
int doInit(int rate, unsigned latency);
public:
AlsaEngine();
~AlsaEngine();
int init(int rate, unsigned latency);
void uninit();
int write(void *buffer, unsigned samples);
const RateEst::Result rateEstimate() const { return est.result(); }
const BufferState bufferState() const;
void pause() { prevfur = 0; est.init(est.result().est); }
QWidget* settingsWidget() { return conf.settingsWidget(); }
void acceptSettings() { conf.acceptSettings(); }
void rejectSettings() { conf.rejectSettings(); }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -24,7 +24,7 @@ AoEngine::~AoEngine() {
uninit();
}
int AoEngine::init(const int rate, unsigned /*latency*/) {
int AoEngine::doInit(const int rate, unsigned /*latency*/) {
ao_initialize();
aoDevice = NULL;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -26,10 +26,11 @@
class AoEngine : public AudioEngine {
ao_device *aoDevice;
int doInit(int rate, unsigned latency);
public:
AoEngine();
~AoEngine();
int init(int rate, unsigned latency);
void uninit();
int write(void *buffer, unsigned samples);
};

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -124,7 +124,7 @@ static unsigned nearestPowerOf2(const unsigned in) {
return out;
}
int DirectSoundEngine::init(const int rate, const unsigned latency) {
int DirectSoundEngine::doInit(const int rate, const unsigned latency) {
if (DirectSoundCreate(deviceSelector->itemData(deviceIndex).value<GUID*>(), &lpDS, NULL) != DS_OK) {
lpDS = NULL;
goto fail;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -42,10 +42,11 @@ class DirectSoundEngine : public AudioEngine {
static BOOL CALLBACK enumCallback(LPGUID, const char*, const char*, LPVOID);
int doInit(int rate, unsigned latency);
public:
DirectSoundEngine(HWND hwnd);
~DirectSoundEngine();
int init(int rate, unsigned latency);
void uninit();
int write(void *buffer, unsigned frames);
const BufferState bufferState() const;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -22,9 +22,10 @@
#include "audioengine.h"
class NullAudioEngine : public AudioEngine {
int doInit(int rate, unsigned /*latency*/) { return rate; }
public:
NullAudioEngine() : AudioEngine("Null") {}
int init(int /*rate*/, unsigned /*latency*/) { return 0; }
int write(void */*buffer*/, unsigned /*samples*/) { return 0; }
};

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -136,7 +136,7 @@ void OpenAlEngine::deleteProcessedBufs() const {
}
}
int OpenAlEngine::init(const int rate, unsigned latency) {
int OpenAlEngine::doInit(const int rate, unsigned latency) {
class FailureCleaner {
OpenAlEngine *const engine;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -44,11 +44,11 @@ class OpenAlEngine : public AudioEngine {
int rate;
void deleteProcessedBufs() const;
int doInit(int rate, unsigned latency);
public:
OpenAlEngine();
~OpenAlEngine();
int init(int rate, unsigned latency);
void uninit();
int write(void *buffer, unsigned samples);
const BufferState bufferState() const;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -30,14 +30,15 @@ OssEngine::OssEngine() :
AudioEngine("OSS"),
conf("Custom DSP device:", "/dev/dsp", "ossengine"),
audio_fd(-1),
bufSize(0)
bufSize(0),
prevfur(0)
{}
OssEngine::~OssEngine() {
uninit();
}
int OssEngine::init(int speed, const unsigned latency) {
int OssEngine::doInit(int speed, const unsigned latency) {
if ((audio_fd = open(conf.device(), O_WRONLY, 0)) == -1) {
perror(conf.device());
goto fail;
@ -96,9 +97,12 @@ int OssEngine::init(int speed, const unsigned latency) {
goto fail;
}
bufSize = info.fragstotal * info.fragsize >> 2;
bufSize = info.bytes >> 2;
}
prevfur = 0;
est.init(speed);
return speed;
fail:
@ -114,6 +118,17 @@ void OssEngine::uninit() {
}
int OssEngine::write(void *const buffer, const unsigned samples) {
const BufferState &bstate = bufferState();
if (prevfur > bstate.fromUnderrun && bstate.fromUnderrun != BufferState::NOT_SUPPORTED) {
if (bstate.fromUnderrun)
est.feed(prevfur - bstate.fromUnderrun);
else
est.init(std::min(est.result().est + (est.result().est >> 10), (long) rate() + (rate() >> 4)));
}
prevfur = bstate.fromUnderrun + samples;
if (::write(audio_fd, buffer, samples * 4) != static_cast<int>(samples * 4))
return -1;
@ -124,9 +139,14 @@ const AudioEngine::BufferState OssEngine::bufferState() const {
BufferState s;
audio_buf_info info;
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1 || info.bytes < 0) {
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) {
s.fromOverflow = s.fromUnderrun = BufferState::NOT_SUPPORTED;
} else {
if (info.bytes < 0)
info.bytes = 0;
else if (static_cast<unsigned>(info.bytes >> 2) > bufSize)
info.bytes = bufSize;
s.fromUnderrun = bufSize - (info.bytes >> 2);
s.fromOverflow = info.bytes >> 2;
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -24,16 +24,21 @@
class OssEngine : public AudioEngine {
CustomDevConf conf;
RateEst est;
int audio_fd;
unsigned bufSize;
unsigned prevfur;
int doInit(int rate, unsigned latency);
public:
OssEngine();
~OssEngine();
int init(int rate, unsigned latency);
void uninit();
int write(void *buffer, unsigned samples);
const RateEst::Result rateEstimate() const { return est.result(); }
const BufferState bufferState() const;
void pause() { prevfur = 0; est.init(est.result().est); }
QWidget* settingsWidget() { return conf.settingsWidget(); }
void acceptSettings() { conf.acceptSettings(); }
void rejectSettings() { conf.rejectSettings(); }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -17,11 +17,35 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "blitterwidget.h"
#include <adaptivesleep.h>
#include <cstdlib>
#include <iostream>
struct Time {
long sec;
long rsec;
};
void FtEst::init(const long frameTime) {
this->frameTime = frameTime;
ft = 0;
ftAvg = frameTime * COUNT;
ftVar = ftAvg >> 12;
last = 0;
count = COUNT;
}
void FtEst::update(const usec_t t) {
if (t - last < static_cast<unsigned long>(frameTime + (frameTime >> 2))) {
ft += t - last;
if (--count == 0) {
count = COUNT;
long oldFtAvg = ftAvg;
ftAvg = (ftAvg * 15 + ft + 8) >> 4;
ftVar = (ftVar * 15 + std::abs(ftAvg - oldFtAvg) + 8) >> 4;
std::cout << "ftAvg: " << est() << " ftVar: " << var() << std::endl;
ft = 0;
}
}
last = t;
}
#ifdef Q_WS_WIN
@ -38,7 +62,7 @@ public:
freq = qpf ? li.QuadPart : 1000;
}
void get(Time *const time, const long denom) const {
usec_t get() const {
LARGE_INTEGER li;
if (qpf)
@ -46,35 +70,33 @@ public:
else
li.QuadPart = timeGetTime();
time->sec = li.QuadPart / freq;
time->rsec = (li.QuadPart % freq) * denom / freq;
return static_cast<ULONGLONG>(li.QuadPart) * 1000000 / freq;
}
};
static Timer timer;
static void getTime(Time *const time, const long denom) {
timer.get(time, denom);
usec_t getusecs() {
return timer.get();
}
static void sleep(const long secnum, const long secdenom) {
Sleep(static_cast<qlonglong>(secnum) * 1000 / secdenom);
void usecsleep(const usec_t usecs) {
Sleep((usecs + 999) / 1000);
}
#else
#include <sys/time.h>
static void getTime(Time *const time, const long denom) {
usec_t getusecs() {
timeval t;
gettimeofday(&t, NULL);
time->sec = t.tv_sec;
time->rsec = static_cast<qlonglong>(t.tv_usec) * denom / 1000000;
return t.tv_sec * usec_t(1000000) + t.tv_usec;
}
static void sleep(const long secnum, const long secdenom) {
void usecsleep(const usec_t usecs) {
timespec tspec = { tv_sec: 0,
tv_nsec: static_cast<qlonglong>(secnum) * 1000000000 / secdenom };
tv_nsec: usecs * 1000 };
nanosleep(&tspec, NULL);
}
@ -82,65 +104,16 @@ static void sleep(const long secnum, const long secdenom) {
#endif /*Q_WS_WIN*/
class BlitterWidget::Impl {
Time last;
long ftnum;
long ftdenom;
long late;
unsigned noSleep;
AdaptiveSleep asleep;
usec_t last;
public:
Impl() : ftnum(Rational().numerator), ftdenom(Rational().denominator), late(0), noSleep(60) { getTime(&last, ftdenom); }
Impl() : last(0) {}
void setFrameTime(const Rational &ft) {
last.rsec = static_cast<qulonglong>(last.rsec) * ft.denominator / ftdenom;
ftnum = ft.numerator;
ftdenom = ft.denominator;
late = 0;
}
const Rational frameTime() const {
return Rational(ftnum, ftdenom);
}
int sync(const bool turbo) {
if (turbo)
return 0;
Time current;
getTime(&current, ftdenom);
long diff = (current.sec - last.sec) * ftdenom + current.rsec - last.rsec;
if (diff < ftnum) {
diff = ftnum - diff;
if (diff > late) {
sleep(diff - late, ftdenom);
getTime(&current, ftdenom);
late += ((current.sec - last.sec) * ftdenom + current.rsec - last.rsec - ftnum) / 2;
if (late < 0)
late = 0;
noSleep = 60;
} else if (noSleep-- == 0) {
noSleep = 60;
late = 0;
}
while ((current.sec - last.sec) * ftdenom + current.rsec - last.rsec < ftnum)
getTime(&current, ftdenom);
last.rsec += ftnum;
if (last.rsec >= ftdenom) {
last.rsec -= ftdenom;
++last.sec;
}
} else {
//quickfix:catches up to current time
last = current;
long sync(const long ft) {
if (ft) {
last += asleep.sleepUntil(last, ft);
last += ft;
}
return 0;
@ -153,6 +126,7 @@ BlitterWidget::BlitterWidget(PixelBufferSetter setPixelBuffer,
QWidget *parent) :
QWidget(parent),
impl(new Impl),
ft(1000000/60),
setPixelBuffer(setPixelBuffer),
nameString(name),
integerOnlyScaler(integerOnlyScaler)
@ -164,14 +138,6 @@ BlitterWidget::~BlitterWidget() {
delete impl;
}
void BlitterWidget::setFrameTime(Rational ft) {
impl->setFrameTime(ft);
}
const BlitterWidget::Rational BlitterWidget::frameTime() const {
return impl->frameTime();
}
int BlitterWidget::sync(const bool turbo) {
return impl->sync(turbo);
long BlitterWidget::sync(const long ft) {
return impl->sync(ft);
}

View File

@ -23,28 +23,46 @@
#include <QWidget>
#include <QString>
#include <memory>
#include <usec.h>
class QHBoxLayout;
class FtEst {
enum { COUNT_LOG2 = 4 };
enum { COUNT = 1 << COUNT_LOG2 };
long frameTime;
long ft;
long ftAvg;
long ftVar;
usec_t last;
unsigned count;
public:
FtEst(long frameTime = 0) { init(frameTime); }
void init(long frameTime);
void update(usec_t t);
long est() const { return (ftAvg + COUNT / 2) >> COUNT_LOG2; }
long var() const { return (ftVar + COUNT / 2) >> COUNT_LOG2; }
};
class BlitterWidget : public QWidget {
Q_OBJECT
class Impl;
Impl *const impl;
public:
struct Rational {
unsigned numerator;
unsigned denominator;
Rational(unsigned num = 1, unsigned den = 60) : numerator(num), denominator(den) {}
};
long ft;
protected:
PixelBufferSetter setPixelBuffer;
public:
struct Estimate {
long est;
long var;
};
const QString nameString;
const bool integerOnlyScaler;
@ -60,9 +78,10 @@ public:
virtual bool isUnusable() const { return false; }
virtual void setBufferDimensions(unsigned width, unsigned height) = 0;
virtual void setCorrectedGeometry(int w, int h, int new_w, int new_h) { setGeometry((w - new_w) >> 1, (h - new_h) >> 1, new_w, new_h); }
virtual void setFrameTime(Rational ft);
virtual const Rational frameTime() const;
virtual int sync(bool turbo);
virtual void setFrameTime(const long ft) { this->ft = ft; }
long frameTime() const { return ft; }
virtual const Estimate frameTimeEst() const { static const Estimate e = { 0, 0 }; return e; }
virtual long sync(long ft);
virtual QWidget* settingsWidget() { return NULL; }
virtual void setExclusive(bool) {}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -341,12 +341,12 @@ void Direct3DBlitter::setSwapInterval(const unsigned hz1, const unsigned hz2) {
if (newSwapInterval != swapInterval) {
swapInterval = newSwapInterval;
resetDevice();
ftEst.init(hz ? swapInterval * 1000000 / hz : 0);
}
}
void Direct3DBlitter::setSwapInterval() {
const Rational &ft = BlitterWidget::frameTime();
setSwapInterval((ft.denominator + (ft.numerator >> 1)) / ft.numerator, (ft.denominator * 2 + (ft.numerator >> 1)) / ft.numerator);
setSwapInterval((1000000 + (frameTime() >> 1)) / frameTime(), (1000000 * 2 + (frameTime() >> 1)) / frameTime());
}
void Direct3DBlitter::draw() {
@ -473,23 +473,26 @@ void Direct3DBlitter::blit() {
}
}
int Direct3DBlitter::sync(const bool turbo) {
long Direct3DBlitter::sync(const long ft) {
if (!swapInterval)
BlitterWidget::sync(turbo);
BlitterWidget::sync(ft);
present();
if (swapInterval)
ftEst.update(getusecs());
if (!device)
return -1;
return 0;
}
void Direct3DBlitter::setFrameTime(Rational ft) {
void Direct3DBlitter::setFrameTime(const long ft) {
BlitterWidget::setFrameTime(ft);
const unsigned hz1 = (ft.denominator + (ft.numerator >> 1)) / ft.numerator;
const unsigned hz2 = (ft.denominator * 2 + (ft.numerator >> 1)) / ft.numerator;
const unsigned hz1 = (1000000 + (ft >> 1)) / ft;
const unsigned hz2 = (1000000 * 2 + (ft >> 1)) / ft;
{
QString text("Sync to vertical blank in " + QString::number(hz1));
@ -503,12 +506,21 @@ void Direct3DBlitter::setFrameTime(Rational ft) {
setSwapInterval(hz1, hz2);
}
const BlitterWidget::Rational Direct3DBlitter::frameTime() const {
const BlitterWidget::Estimate Direct3DBlitter::frameTimeEst() const {
if (swapInterval) {
const Estimate est = { ftEst.est(), ftEst.var() };
return est;
}
return BlitterWidget::frameTimeEst();
}
/*const BlitterWidget::Rational Direct3DBlitter::frameTime() const {
if (swapInterval)
return Rational(swapInterval, hz);
return BlitterWidget::frameTime();
}
}*/
void Direct3DBlitter::setExclusive(const bool exclusive) {
if (exclusive != this->exclusive) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -29,6 +29,7 @@ class QComboBox;
class Direct3DBlitter : public BlitterWidget {
typedef IDirect3D9* (WINAPI *Direct3DCreate9Ptr)(UINT);
FtEst ftEst;
const std::auto_ptr<QWidget> confWidget;
QComboBox *const adapterSelector;
QCheckBox *const vblankBox;
@ -82,9 +83,9 @@ public:
void uninit();
void setBufferDimensions(unsigned w, unsigned h);
void blit();
int sync(bool turbo);
void setFrameTime(Rational ft);
const Rational frameTime() const;
long sync(long ft);
void setFrameTime(long ft);
const Estimate frameTimeEst() const;
void setExclusive(bool exclusive);
bool isUnusable() const { return !d3d; }
QWidget* settingsWidget() { return confWidget.get(); }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -387,10 +387,10 @@ void DirectDrawBlitter::uninit() {
}
}
void DirectDrawBlitter::setFrameTime(Rational ft) {
void DirectDrawBlitter::setFrameTime(const long ft) {
BlitterWidget::setFrameTime(ft);
vblankHz = (ft.denominator + (ft.numerator >> 1)) / ft.numerator;
vblankHz = (1000000 + (ft >> 1)) / ft;
QString text("Sync to vertical blank in ");
text += QString::number(vblankHz);
@ -398,15 +398,24 @@ void DirectDrawBlitter::setFrameTime(Rational ft) {
vblankBox->setText(text);
}
const BlitterWidget::Rational DirectDrawBlitter::frameTime() const {
const BlitterWidget::Estimate DirectDrawBlitter::frameTimeEst() const {
if (vblank && hz == vblankHz) {
const Estimate est = { ftEst.est(), ftEst.var() };
return est;
}
return BlitterWidget::frameTimeEst();
}
/*const BlitterWidget::Rational DirectDrawBlitter::frameTime() const {
if (vblank && hz == vblankHz) {
return Rational(1, hz);
}
return BlitterWidget::frameTime();
}
}*/
int DirectDrawBlitter::sync(const bool turbo) {
long DirectDrawBlitter::sync(const long ft) {
if (lpDDSPrimary == NULL)
return -1;
@ -415,13 +424,15 @@ int DirectDrawBlitter::sync(const bool turbo) {
RECT rcRectDest;
GetWindowRect(winId(), &rcRectDest);
if (vblank && hz == vblankHz) {
const bool vsync = vblank && hz == vblankHz;
if (vsync) {
if (!(exclusive && flipping))
lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
} else
BlitterWidget::sync(turbo);
BlitterWidget::sync(ft);
const bool dontwait = exclusive && flipping && !(vblank && hz == vblankHz);
const bool dontwait = exclusive && flipping && !vsync;
HRESULT ddrval = lpDDSBack->Blt(&rcRectDest, lpDDSVideo, NULL, dontwait ? DDBLT_DONOTWAIT : DDBLT_WAIT, NULL);
@ -436,6 +447,9 @@ int DirectDrawBlitter::sync(const bool turbo) {
ddrval = lpDDSPrimary->Flip(NULL, dontwait ? DDFLIP_DONOTWAIT : DDFLIP_WAIT);
}
if (vsync)
ftEst.update(getusecs());
if (ddrval != DD_OK && ddrval != DDERR_WASSTILLDRAWING) {
std::cout << "final blit failed" << std::endl;
return -1;
@ -485,6 +499,7 @@ void DirectDrawBlitter::rejectSettings() {
void DirectDrawBlitter::rateChange(int hz) {
this->hz = hz;
ftEst.init(hz ? 1000000 / hz : 0);
}
void DirectDrawBlitter::paintEvent(QPaintEvent */*event*/) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -28,6 +28,7 @@ class QCheckBox;
class QComboBox;
class DirectDrawBlitter : public BlitterWidget {
FtEst ftEst;
const std::auto_ptr<QWidget> confWidget;
QCheckBox *const vblankBox;
QCheckBox *const flippingBox;
@ -72,9 +73,9 @@ public:
void blit();
void init();
void setBufferDimensions(unsigned int w, unsigned int h);
void setFrameTime(Rational ft);
const Rational frameTime() const;
int sync(bool turbo);
void setFrameTime(long ft);
const Estimate frameTimeEst() const;
long sync(long turbo);
void uninit();
void setExclusive(bool exclusive);

View File

@ -368,11 +368,11 @@ void QGLBlitter::setCorrectedGeometry(int w, int h, int new_w, int new_h) {
subWidget->forcedResize();
}
void QGLBlitter::setFrameTime(Rational ft) {
void QGLBlitter::setFrameTime(const long ft) {
BlitterWidget::setFrameTime(ft);
hz1 = (ft.denominator + (ft.numerator >> 1)) / ft.numerator;
hz2 = (ft.denominator * 2 + (ft.numerator >> 1)) / ft.numerator;
hz1 = (1000000 + (ft >> 1)) / ft;
hz2 = (1000000 * 2 + (ft >> 1)) / ft;
QString text("Sync to vertical blank in ");
text += QString::number(hz1);
@ -390,18 +390,26 @@ void QGLBlitter::setFrameTime(Rational ft) {
resetSubWidget();
}
const BlitterWidget::Rational QGLBlitter::frameTime() const {
const BlitterWidget::Estimate QGLBlitter::frameTimeEst() const {
if (subWidget->getSwapInterval()) {
const Estimate est = { ftEst.est(), ftEst.var() };
return est;
}
return BlitterWidget::frameTimeEst();
}
/*const BlitterWidget::Rational QGLBlitter::frameTime() const {
if (subWidget->getSwapInterval()) {
return Rational(subWidget->getSwapInterval(), hz);
}
return BlitterWidget::frameTime();
}
}*/
int QGLBlitter::sync(const bool turbo) {
if (!subWidget->getSwapInterval()) {
BlitterWidget::sync(turbo);
}
long QGLBlitter::sync(const long ft) {
if (!subWidget->getSwapInterval())
BlitterWidget::sync(ft);
subWidget->makeCurrent();
@ -415,6 +423,9 @@ int QGLBlitter::sync(const bool turbo) {
subWidget->blitted = false;
// }
if (subWidget->getSwapInterval())
ftEst.update(getusecs());
return 0;
}
@ -443,6 +454,7 @@ void QGLBlitter::resetSubWidget() {
subWidget->corrected_h = corrected_h;
subWidget->setGeometry(0, 0, width(), height());
subWidget->show();
ftEst.init(hz ? swapInterval * 1000000 / hz : 0);
if (buffer)
subWidget->setBufferDimensions(w, h);

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -27,6 +27,7 @@ class QCheckBox;
class QGLBlitter : public BlitterWidget {
class SubWidget;
FtEst ftEst;
SubWidget *subWidget;
const std::auto_ptr<QWidget> confWidget;
QCheckBox *const vsyncBox;
@ -51,10 +52,10 @@ public:
bool isUnusable() const;
void setBufferDimensions(unsigned int width, unsigned int height);
void setCorrectedGeometry(int w, int h, int new_w, int new_h);
void setFrameTime(Rational ft);
void setFrameTime(long ft);
const Estimate frameTimeEst() const;
void blit();
const Rational frameTime() const;
int sync(bool turbo);
long sync(long ft);
QWidget* settingsWidget() { return confWidget.get(); }
// // public slots:

View File

@ -267,11 +267,11 @@ X11Blitter::~X11Blitter() {
settings.endGroup();
}
int X11Blitter::sync(const bool turbo) {
long X11Blitter::sync(const long ft) {
if (subBlitter->failed())
return -1;
return BlitterWidget::sync(turbo);
return BlitterWidget::sync(ft);
}
/*

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -55,7 +55,7 @@ public:
void init();
void uninit();
bool isUnusable() const;
int sync(bool turbo);
long sync(long ft);
void setBufferDimensions(const unsigned width, const unsigned height);
void blit();
QWidget* settingsWidget() { return confWidget.get(); }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -331,11 +331,11 @@ XvBlitter::~XvBlitter() {
settings.endGroup();
}
int XvBlitter::sync(const bool turbo) {
long XvBlitter::sync(const long ft) {
if (failed || subBlitter->failed())
return -1;
return BlitterWidget::sync(turbo);
return BlitterWidget::sync(ft);
}
void XvBlitter::paintEvent(QPaintEvent *event) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -63,7 +63,7 @@ public:
void init();
void uninit();
bool isUnusable() const;
int sync(bool turbo);
long sync(long turbo);
void setBufferDimensions(const unsigned int width, const unsigned int height);
void blit();

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -19,8 +19,8 @@
#include "gambattesource.h"
GambatteSource::GambatteSource() :
blitter(*this),
sampleBuffer(NULL) {
MediaSource(Rational(35112), 2064),
blitter(*this) {
gb.setInputStateGetter(&inputGetter);
gb.setVideoBlitter(&blitter);
}
@ -50,7 +50,7 @@ const MediaSource::SampleRateInfo GambatteSource::generateSampleRateInfo() {
srinfo.rates.push_back(48000);
srinfo.rates.push_back(44100);
srinfo.defaultRateIndex = 0;
srinfo.minCustomRate = 32000;
srinfo.minCustomRate = 8000;
srinfo.maxCustomRate = 192000;
return srinfo;
@ -168,7 +168,6 @@ void GambatteSource::setPixelBuffer(void *pixels, PixelFormat format, unsigned p
gb.videoBufferChange();
}
void GambatteSource::update(const unsigned samples) {
gb.runFor(70224);
gb.fill_buffer(reinterpret_cast<quint16*>(sampleBuffer), samples);
unsigned GambatteSource::update(qint16 *sampleBuf, const unsigned samples) {
return gb.runFor(reinterpret_cast<Gambatte::uint_least32_t*>(sampleBuf), samples);
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -48,7 +48,6 @@ class GambatteSource : public QObject, public MediaSource {
Gambatte::GB gb;
InputGetter inputGetter;
Blitter blitter;
qint16 *sampleBuffer;
public:
GambatteSource();
@ -73,9 +72,8 @@ public:
void buttonPressEvent(unsigned buttonIndex);
void buttonReleaseEvent(unsigned buttonIndex);
void setPixelBuffer(void *pixels, PixelFormat format, unsigned pitch);
void setSampleBuffer(qint16 *sampleBuffer, unsigned /*sampleRate*/) { this->sampleBuffer = sampleBuffer; }
void setVideoSource(unsigned videoSourceIndex) { gb.setVideoFilter(videoSourceIndex); }
void update(unsigned samples);
unsigned update(qint16 *soundBuf, unsigned samples);
public slots:
void saveState() { gb.saveState(); }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -25,6 +25,9 @@
#include <cassert>
#include <QApplication>
#include <QTimer>
#include <cstdlib>
#include <resample/resamplerinfo.h>
#include "blitterwidgets/qpainterblitter.h"
#include "blitterwidgets/qglblitter.h"
@ -96,6 +99,31 @@ MainWindow::JoystickIniter::~JoystickIniter() {
SDL_JoystickQuit();
}
MainWindow::SampleBuffer::SampleBuffer(const std::size_t maxInSamples) : sndInBuffer(maxInSamples * 2), spfnum(0), samplesBuffered(0) {}
std::size_t MainWindow::SampleBuffer::update(qint16 *const out, MediaSource *const source, Resampler *const resampler) {
spfnum += source->samplesPerFrame.num;
const long insamples = spfnum / source->samplesPerFrame.denom;
spfnum -= insamples * source->samplesPerFrame.denom;
samplesBuffered += source->update(sndInBuffer + samplesBuffered * 2, insamples - samplesBuffered);
samplesBuffered -= insamples;
std::size_t outsamples = 0;
if (out) {
if (resampler->inRate() == resampler->outRate()) {
std::memcpy(out, sndInBuffer, insamples * sizeof(qint16) * 2);
outsamples = insamples;
} else
outsamples = resampler->resample(out, sndInBuffer, insamples);
}
std::memmove(sndInBuffer, sndInBuffer + insamples * 2, samplesBuffered * sizeof(qint16) * 2);
return outsamples;
}
MainWindow::MainWindow(MediaSource *source,
const std::vector<MediaSource::ButtonInfo> &buttonInfos,
const std::vector<MediaSource::VideoSourceInfo> &videoSourceInfos,
@ -106,16 +134,16 @@ MainWindow::MainWindow(MediaSource *source,
buttonHandlers(buttonInfos.size(), ButtonHandler(0, 0)),
blitter(NULL),
fullModeToggler(getFullModeToggler(winId())),
sndBuffer(NULL),
sampleBuffer((source->samplesPerFrame.num - 1) / source->samplesPerFrame.denom + 1 + source->overupdate),
sndOutBuffer(0),
ae(NULL),
cursorTimer(NULL),
jsTimer(NULL),
samplesPrFrame(0),
ftNum(1),
ftDenom(60),
paused(0),
sampleRate(0),
timerId(0),
estSrate(0),
running(false),
turbo(false),
pauseOnDialogExec(true),
@ -193,8 +221,6 @@ MainWindow::~MainWindow() {
for (uint i = 0; i < audioEngines.size(); ++i)
delete audioEngines[i];
delete []sndBuffer;
}
void MainWindow::resetWindowSize(const QSize &s) {
@ -375,7 +401,7 @@ void MainWindow::videoSettingsChange() {
}
}
setSamplesPrFrame();
// setSampleRate();
blitterContainer->updateLayout();
}
@ -426,9 +452,7 @@ void MainWindow::setVideoSources(const std::vector<MediaSource::VideoSourceInfo>
void MainWindow::doSetFrameTime(unsigned num, unsigned denom) {
for (unsigned i = 0; i < blitters.size(); ++i)
blitters[i]->setFrameTime(BlitterWidget::Rational(num, denom));
setSamplesPrFrame();
blitters[i]->setFrameTime(num * 1000000.0f / denom + 0.5f);
}
void MainWindow::setFrameTime(unsigned num, unsigned denom) {
@ -445,18 +469,36 @@ void MainWindow::setFrameTime(unsigned num, unsigned denom) {
if (!turbo)
doSetFrameTime(num, denom);
setSampleRate();
}
void MainWindow::setSamplesPrFrame() {
const BlitterWidget::Rational r = blitter->frameTime();
const unsigned old = samplesPrFrame;
samplesPrFrame = (sampleRate * r.numerator) / r.denominator + 1;
static long maxSamplesPerFrame(const MediaSource *const source) {
return (source->samplesPerFrame.num - 1) / source->samplesPerFrame.denom + 1;
}
static void resetSndOutBuf(Array<qint16> &sndOutBuf, const Resampler *const resampler, const long maxspf) {
const std::size_t sz = resampler->maxOut(maxspf) * 2;
if (old != samplesPrFrame) {
delete []sndBuffer;
sndBuffer = new qint16[(samplesPrFrame + 4) * 2];
source->setSampleBuffer(sndBuffer, sampleRate);
samplesCalc.setBaseSamples(samplesPrFrame);
if (sz != sndOutBuf.size())
sndOutBuf.reset(sz);
}
static void adjustResamplerRate(Array<qint16> &sndOutBuf, Resampler *const resampler, const long maxspf, const long outRate) {
resampler->adjustRate(resampler->inRate(), outRate);
resetSndOutBuf(sndOutBuf, resampler, maxspf);
}
void MainWindow::setSampleRate() {
if (ae) {
const Rational fr(ftDenom, ftNum);
const long insrate = fr.toDouble() * source->samplesPerFrame.toDouble() + 0.5;
const long maxspf = maxSamplesPerFrame(source);
resampler.reset();
resampler.reset(ResamplerInfo::get(soundDialog->getResamplerNum()).create(insrate, ae->rate(), maxspf));
resetSndOutBuf(sndOutBuffer, resampler.get(), maxspf);
}
}
@ -466,12 +508,12 @@ void MainWindow::initAudio() {
ae = audioEngines[soundDialog->getEngineIndex()];
if ((sampleRate = ae->init(soundDialog->getRate(), soundDialog->getLatency())) < 0) {
if (ae->init(soundDialog->getRate(), soundDialog->getLatency()) < 0) {
ae = NULL;
sampleRate = 0;
}
} else
estSrate = ae->rate();
setSamplesPrFrame();
setSampleRate();
}
void MainWindow::soundEngineFailure() {
@ -487,9 +529,40 @@ void MainWindow::timerEvent(QTimerEvent */*event*/) {
updateJoysticks();
source->update(samplesCalc.getSamples());
const std::size_t outsamples = sampleBuffer.update(turbo ? NULL : static_cast<qint16*>(sndOutBuffer), source, resampler.get());
if (blitter->sync(turbo)) {
long syncft = 0;
if (!turbo) {
if (ae->write(sndOutBuffer, outsamples) < 0) {
ae->pause();
soundEngineFailure();
return;
}
{
const RateEst::Result &rsrate = ae->rateEstimate();
const long newEstSrate = rsrate.est + (rsrate.var * 2);
if (std::abs(newEstSrate - estSrate) > rsrate.var * 2)
estSrate = newEstSrate;
}
const long usecft = blitter->frameTime();
syncft = static_cast<float>(usecft - (usecft >> 11)) * ae->rate() / estSrate;
const BlitterWidget::Estimate &estft = blitter->frameTimeEst();
if (estft.est) {
long resorate = estSrate * static_cast<float>(estft.est + (estft.est >> 11) + estft.var * 2) / usecft;
if (static_cast<float>(std::abs(resorate - resampler->outRate())) * usecft > static_cast<float>(estft.var * 2) * estSrate)
adjustResamplerRate(sndOutBuffer, resampler.get(), maxSamplesPerFrame(source), resorate);
} else if (resampler->outRate() != ae->rate())
adjustResamplerRate(sndOutBuffer, resampler.get(), maxSamplesPerFrame(source), ae->rate());
}
if (blitter->sync(syncft) < 0) {
QMessageBox::critical(this, tr("Error"), tr("Video engine failure."));
uninitBlitter();
blitter->init();
@ -500,19 +573,6 @@ void MainWindow::timerEvent(QTimerEvent */*event*/) {
videoDialog->exec();
return;
}
if (!turbo) {
const unsigned lastSamples = samplesCalc.getSamples();
const AudioEngine::BufferState bufState = ae->bufferState();
if (bufState.fromUnderrun != AudioEngine::BufferState::NOT_SUPPORTED)
samplesCalc.update(bufState.fromUnderrun, bufState.fromOverflow);
if (ae->write(sndBuffer, lastSamples) < 0) {
ae->pause();
soundEngineFailure();
}
}
}
void MainWindow::run() {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -23,7 +23,7 @@
#include <vector>
#include <map>
#include <memory>
#include "samplescalculator.h"
#include <array.h>
#include "SDL_Joystick/include/SDL_joystick.h"
#include "SDL_Joystick/include/SDL_event.h"
#include "mediasource.h"
@ -38,6 +38,7 @@ class FullModeToggler;
class BlitterContainer;
class JoyObserver;
class QTimer;
class Resampler;
class MainWindow : public QMainWindow {
Q_OBJECT
@ -66,7 +67,17 @@ private:
JoystickIniter();
~JoystickIniter();
};
class SampleBuffer {
Array<qint16> sndInBuffer;
unsigned spfnum;
unsigned samplesBuffered;
public:
SampleBuffer(std::size_t maxInSamples);
std::size_t update(qint16 *out, MediaSource *source, Resampler *resampler);
};
typedef std::multimap<unsigned,InputObserver*> keymap_t;
typedef std::multimap<unsigned,JoyObserver*> joymap_t;
@ -83,19 +94,19 @@ private:
VideoDialog *videoDialog;
BlitterWidget *blitter;
const std::auto_ptr<FullModeToggler> fullModeToggler;
qint16 *sndBuffer;
std::auto_ptr<Resampler> resampler;
SampleBuffer sampleBuffer;
Array<qint16> sndOutBuffer;
AudioEngine *ae;
QTimer *cursorTimer;
QTimer *jsTimer;
unsigned samplesPrFrame;
unsigned ftNum;
unsigned ftDenom;
unsigned paused;
int sampleRate;
int timerId;
int estSrate;
SamplesCalculator samplesCalc;
JoystickIniter joyIniter;
bool running;
@ -104,7 +115,7 @@ private:
bool cursorHidden;
void initAudio();
void setSamplesPrFrame();
void setSampleRate();
void soundEngineFailure();
void clearInputVectors();
void pushInputObserver(const SDL_Event &data, InputObserver *observer);

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -22,6 +22,7 @@
#include <QtGlobal>
#include <QString>
#include <vector>
#include "rational.h"
//TODO: stop dictating audio/video formats.
// Choice based on:
@ -89,6 +90,11 @@ public:
int maxCustomRate;
};
const Rational samplesPerFrame;
const unsigned overupdate;
MediaSource(const Rational &samplesPerFrame, const unsigned overupdate = 0) : samplesPerFrame(samplesPerFrame), overupdate(overupdate) {}
/**
* Reimplement to get buttonPress events for buttons of corresponding index to the
* buttonInfos given to MainWindow.
@ -108,15 +114,6 @@ public:
*/
virtual void setPixelBuffer(void *pixels, PixelFormat format, unsigned pitch) = 0;
/**
* Called by MainWindow to set the buffer to output audio samples to (always in native endian
* signed 16-bit interleaved stereo for now.) Can be assumed to be big enough to have space
* for the number of samples requested on each update call. Also gives the currently active
* sample rate. which may differ slightly from the alternatives given to MainWindow, since
* some engines set a near sample rate if they can't give an exact native one.
*/
virtual void setSampleBuffer(qint16 *sampleBuffer, unsigned sampleRate) = 0;
/**
* Sets video source to the one indicated by the corresponding index of the VideoSourceInfos
* given to MainWindow. setPixelBuffer will be called if a buffer change is necessary.
@ -125,21 +122,17 @@ public:
*/
virtual void setVideoSource(unsigned videoSourceIndex) = 0;
/**
* Called at a time interval given by the frameTime given to MainWindow. Approximately one
* frame of video should be generated in the pixel buffer (followed by a call to MainWindow::blit)
* pr update for best results. The number of audio samples requested should be outputted to the
* (start of the) sample buffer. It's advised to adjust how much you update according to how many
* samples are requested, rather than strictly doing one frame of video pr update, unless you
* do resampling or dynamically generate samples in a way that allows you to always output the
* right number of samples pr video frame. If the number of video frames generated pr update is
* far off from one pr update, you could try adjusting the frame time (MainWindow::setFrameTime).
* Obviously, if the sample rate selected for a fixed sample rate source is wrong, you can
* expect the number of samples requested to be way off.
/** Updates until at least 'samples' stereo sound samples are produced in the supplied buffer.
* May run for uptil overupdate stereo samples too long.
* A stereo sample consists of two native endian 2s complement 16-bit PCM samples,
* with the left sample preceding the right one.
*
* @param samples The number of stereo samples that should be written to sampleBuffer.
* @param soundBuf buffer with space >= samples + overupdate
* @param samples number of stereo samples to produce
* @return actual number of samples produced
*/
virtual void update(unsigned samples) = 0;
virtual unsigned update(qint16 *soundBuf, unsigned samples) = 0;
virtual ~MediaSource() {}
};

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -38,6 +38,7 @@
#include <QSettings>
#include <QInputDialog>
#include <QMessageBox>
#include <cstring>
ColorPicker::ColorPicker(QRgb color, QWidget *parent)
: QFrame(parent), w(new QWidget)
@ -216,6 +217,7 @@ static const QModelIndex schemeIndexOf(const QAbstractItemModel *const model, co
PaletteDialog::PaletteDialog(const QString &savepath, const PaletteDialog *global, QWidget *parent)
: QDialog(parent), global(global), listView(new QListView), rmSchemeButton(new QPushButton("Remove Scheme"))
{
std::memset(currentColors, 0, sizeof(currentColors));
setWindowTitle(global ? "Current ROM Palette" : "Global Palette");
QBoxLayout *const mainLayout = new QVBoxLayout;
@ -228,7 +230,7 @@ PaletteDialog::PaletteDialog(const QString &savepath, const PaletteDialog *globa
QBoxLayout *const frameLayout = new QVBoxLayout;
savedir = savepath + "/";
QDir::root().mkpath(savedir + "stored/");
listView->setModel(new ImmutableStringListModel);
listView->setModel(new ImmutableStringListModel(this));
setSchemeList();
frameLayout->addWidget(listView);

View File

@ -0,0 +1,31 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RATIONAL_H
#define RATIONAL_H
struct Rational {
long num;
long denom;
Rational(const long num = 1, const long denom = 1) : num(num), denom(denom) {}
float toFloat() const { return static_cast<float>(num) / denom; }
double toDouble() const { return static_cast<double>(num) / denom; }
};
#endif

View File

@ -1,66 +0,0 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "samplescalculator.h"
SamplesCalculator::SamplesCalculator(const unsigned baseSamples, const unsigned maxDiff)
: maxDiff(maxDiff)
{
setBaseSamples(baseSamples);
}
void SamplesCalculator::setBaseSamples(const unsigned baseSamples) {
this->baseSamples = baseSamples;
samples = baseSamples;
lastFromUnderrun = baseSamples;
lastOverflowTime = lastUnderrunTime = updates = samplesOverflowed = 0;
}
// #include <cstdio>
void SamplesCalculator::update(const unsigned fromUnderrun, const unsigned fromOverflow) {
++updates;
if (fromUnderrun < samples * 2) {
if (fromUnderrun <= lastFromUnderrun && samples < baseSamples + maxDiff && updates - lastUnderrunTime >= 60) {
++samples;
lastFromUnderrun = fromUnderrun;
lastUnderrunTime = updates;
// std::printf("samples: %u\n", samples);
lastOverflowTime = samplesOverflowed = 0;
}
} else {
const unsigned of = samples - fromOverflow;
if (!(of & 0x80000000)) {
if (!lastOverflowTime)
lastOverflowTime = updates;
else {
samplesOverflowed += of;
if (samples > baseSamples - maxDiff && samplesOverflowed >= (updates - lastOverflowTime) * 2 + samples && updates - lastOverflowTime >= 300) {
--samples;
lastFromUnderrun = 0xFFFFFFFF;
// std::printf("samples: %u\n", samples);
samplesOverflowed = 0;
lastOverflowTime = updates;
}
}
}
}
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -17,7 +17,7 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "sounddialog.h"
#include <resample/resamplerinfo.h>
#include <QComboBox>
#include <QSpinBox>
#include <QVBoxLayout>
@ -83,9 +83,14 @@ SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const MediaSo
engines(engines),
topLayout(new QVBoxLayout),
engineSelector(new QComboBox(this)),
resamplerSelector(new QComboBox(this)),
rateSelector(new QComboBox(this)),
latencySelector(new QSpinBox(this)),
engineWidget(NULL)
engineWidget(NULL),
engineIndex(0),
resamplerNum(1),
rate(0),
latency(68)
{
setWindowTitle(tr("Sound Settings"));
@ -102,6 +107,17 @@ SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const MediaSo
topLayout->addLayout(hLayout);
}
{
QHBoxLayout *const hLayout = new QHBoxLayout;
hLayout->addWidget(new QLabel(tr("Resampler:")));
for (unsigned i = 0; i < ResamplerInfo::num(); ++i)
resamplerSelector->addItem(ResamplerInfo::get(i).desc);
hLayout->addWidget(resamplerSelector);
topLayout->addLayout(hLayout);
}
{
QHBoxLayout *const hLayout = new QHBoxLayout;
hLayout->addWidget(new QLabel(tr("Sample rate:")));
@ -144,9 +160,10 @@ SoundDialog::SoundDialog(const std::vector<AudioEngine*> &engines, const MediaSo
QSettings settings;
settings.beginGroup("sound");
engineIndex = filterValue(settings.value("engineIndex", 0).toInt(), engineSelector->count());
engineIndex = filterValue(settings.value("engineIndex", engineIndex).toInt(), engineSelector->count());
resamplerNum = filterValue(settings.value("resamplerNum", resamplerNum).toInt(), resamplerSelector->count(), 0, resamplerNum);
setRate(rateSelector, settings.value("rate", rateSelector->itemData(rateSelector->currentIndex())).toInt());
latency = filterValue(settings.value("latency", 67).toInt(), latencySelector->maximum(), latencySelector->minimum(), 67);
latency = filterValue(settings.value("latency", latency).toInt(), latencySelector->maximum(), latencySelector->minimum(), latency);
settings.endGroup();
rate = rateSelector->itemData(rateSelector->currentIndex()).toInt();
@ -161,6 +178,7 @@ SoundDialog::~SoundDialog() {
QSettings settings;
settings.beginGroup("sound");
settings.setValue("engineIndex", engineIndex);
settings.setValue("resamplerNum", resamplerNum);
settings.setValue("rate", rate);
settings.setValue("latency", latency);
settings.endGroup();
@ -196,6 +214,7 @@ void SoundDialog::store() {
engines[i]->acceptSettings();
engineIndex = engineSelector->currentIndex();
resamplerNum = resamplerSelector->currentIndex();
rate = rateSelector->itemData(rateSelector->currentIndex()).toInt();
latency = latencySelector->value();
}
@ -205,6 +224,7 @@ void SoundDialog::restore() {
engines[i]->rejectSettings();
engineSelector->setCurrentIndex(engineIndex);
resamplerSelector->setCurrentIndex(resamplerNum);
setRate(rateSelector, rate);
latencySelector->setValue(latency);
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -34,10 +34,12 @@ class SoundDialog : public QDialog {
const std::vector<AudioEngine*> &engines;
QVBoxLayout *const topLayout;
QComboBox *const engineSelector;
QComboBox *const resamplerSelector;
QComboBox *const rateSelector;
QSpinBox *const latencySelector;
QWidget *engineWidget;
int engineIndex;
int resamplerNum;
int rate;
int latency;
@ -52,6 +54,7 @@ public:
SoundDialog(const std::vector<AudioEngine*> &engines, const MediaSource::SampleRateInfo &rateInfo, QWidget *parent = 0);
~SoundDialog();
int getEngineIndex() const { return engineIndex; }
int getResamplerNum() const { return resamplerNum; }
int getRate() const { return rate; }
int getLatency() const { return latency; };
void setRates(const MediaSource::SampleRateInfo &rateInfo);

View File

@ -2,7 +2,6 @@ SOURCES += main.cpp \
videodialog.cpp \
blittercontainer.cpp \
inputdialog.cpp \
samplescalculator.cpp \
blitterwidgets/qglblitter.cpp \
blitterwidgets/qpainterblitter.cpp \
SDL_Joystick/src/SDL_event.cpp \
@ -16,6 +15,11 @@ SOURCES += main.cpp \
gambattemenuhandler.cpp \
mainwindow.cpp \
blitterwidget.cpp
SOURCES += ../../common/resample/chainresampler.cpp \
../../common/resample/u48div.cpp \
../../common/resample/resamplerinfo.cpp \
../../common/adaptivesleep.cpp \
../../common/rateest.cpp
HEADERS += blitterwidget.h \
fullmodetoggler.h \
videodialog.h \
@ -23,7 +27,6 @@ HEADERS += blitterwidget.h \
resinfo.h \
inputdialog.h \
audioengine.h \
samplescalculator.h \
addaudioengines.h \
addblitterwidgets.h \
getfullmodetoggler.h \
@ -47,7 +50,26 @@ HEADERS += blitterwidget.h \
pixelbuffersetter.h \
gambattemenuhandler.h \
mainwindow.h \
swscale.h
swscale.h \
rational.h
HEADERS += ../../common/resample/blackmansinc.h \
../../common/resample/chainresampler.h \
../../common/resample/cic2.h \
../../common/resample/cic3.h \
../../common/resample/cic4.h \
../../common/resample/convoluter.h \
../../common/resample/hammingsinc.h \
../../common/resample/linint.h \
../../common/resample/makesinckernel.h \
../../common/resample/rectsinc.h \
../../common/resample/resampler.h \
../../common/resample/subresampler.h \
../../common/resample/u48div.h \
../../common/resample/upsampler.h \
../../common/resample/resamplerinfo.h \
../../common/adaptivesleep.h \
../../common/usec.h \
../../common/rateest.h
TEMPLATE = app
CONFIG += warn_on \
thread \
@ -56,7 +78,8 @@ CONFIG += warn_on \
QT += opengl
TARGET = ../bin/gambatte_qt
INCLUDEPATH += ../../libgambatte/include \
SDL_Joystick/include
SDL_Joystick/include \
../../common
LIBS += -L../../libgambatte \
-lgambatte
DEFINES += HAVE_STDINT_H

View File

@ -2,7 +2,7 @@ global_cflags = ARGUMENTS.get('CFLAGS', '-Wall -Wextra -O2 -fomit-frame-pointer'
global_cxxflags = ARGUMENTS.get('CXXFLAGS', global_cflags + ' -fno-exceptions -fno-rtti')
global_defines = ' -DHAVE_STDINT_H'
env = Environment(CPPPATH = ['src', '../libgambatte/include'],
env = Environment(CPPPATH = ['src', '../libgambatte/include', '../common'],
LIBS = ['gambatte'],
LIBPATH = '../libgambatte',
CFLAGS = global_cflags + global_defines,
@ -12,10 +12,16 @@ env.ParseConfig('sdl-config --cflags --libs')
sourceFiles = Split('''
src/gambatte_sdl.cpp
src/audiodata.cpp
src/parser.cpp
src/sdlblitter.cpp
src/str_to_sdlkey.cpp
src/syncfunc.cpp
../common/adaptivesleep.cpp
../common/resample/chainresampler.cpp
../common/resample/u48div.cpp
../common/resample/resamplerinfo.cpp
../common/rateest.cpp
''')
conf = env.Configure()

View File

@ -0,0 +1,93 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "audiodata.h"
#include <SDL_thread.h>
#include <cstdio>
static unsigned ceiledPowerOf2(unsigned t) {
--t;
t |= t >> 1;
t |= t >> 2;
t |= t >> 4;
t |= t >> 8;
t |= t >> 16;
++t;
return t;
}
AudioData::AudioData(const unsigned srate) :
rbuf(ceiledPowerOf2(((srate * 4389) / 262144) + 2) * 8),
rateEst(srate),
mut(SDL_CreateMutex()),
bufReadyCond(SDL_CreateCond()),
failed(false) {
rbuf.fill(0);
SDL_AudioSpec spec;
spec.freq = srate;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.samples = rbuf.size() >> 3;
spec.callback = fill_buffer;
spec.userdata = this;
if (SDL_OpenAudio(&spec, NULL) < 0) {
std::fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
failed = true;
}
}
AudioData::~AudioData() {
SDL_PauseAudio(1);
SDL_CloseAudio();
SDL_DestroyCond(bufReadyCond);
SDL_DestroyMutex(mut);
}
const RateEst::Result AudioData::write(const Sint16 *const inBuf, const unsigned samples) {
if (failed)
return rateEst.result();
SDL_mutexP(mut);
while (rbuf.avail() < samples * 2)
SDL_CondWait(bufReadyCond, mut);
rbuf.write(reinterpret_cast<const Sint16*>(inBuf), samples * 2);
const RateEst::Result srateEst = rateEst.result();
SDL_mutexV(mut);
return srateEst;
}
void AudioData::read(Uint8 *const stream, const int len) {
if (failed)
return;
SDL_mutexP(mut);
rbuf.read(reinterpret_cast<Sint16*>(stream), std::min(static_cast<std::size_t>(len) / 2, rbuf.used()));
rateEst.feed(len / 4);
SDL_CondSignal(bufReadyCond);
SDL_mutexV(mut);
}

View File

@ -0,0 +1,44 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef AUDIODATA_H
#define AUDIODATA_H
#include "ringbuffer.h"
#include <rateest.h>
#include <SDL.h>
struct AudioData {
AudioData(unsigned sampleRate);
~AudioData();
const RateEst::Result write(const Sint16 *inBuf, unsigned samples);
void read(Uint8 *stream, int len);
private:
RingBuffer<Sint16> rbuf;
RateEst rateEst;
SDL_mutex *const mut;
SDL_cond *const bufReadyCond;
bool failed;
static void fill_buffer(void *const data, Uint8 *const stream, const int len) {
reinterpret_cast<AudioData*>(data)->read(stream, len);
}
};
#endif

View File

@ -17,13 +17,18 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <gambatte.h>
#include <array.h>
#include <resample/resamplerinfo.h>
#include <rateest.h>
#include <SDL.h>
#include <SDL_thread.h>
#include <cstdlib>
#include <cstring>
#include <string>
#include <sstream>
#include <algorithm>
#include <memory>
#include "audiodata.h"
#include "syncfunc.h"
#include "parser.h"
#include "str_to_sdlkey.h"
@ -63,13 +68,13 @@ public:
void exec(const char *const *argv, int index) {
int tmp = std::atoi(argv[index + 1]);
if (tmp < 32000 || tmp > 192000)
if (tmp < 4000 || tmp > 192000)
return;
rate = tmp;
}
const char* getDesc() const { return " N\t\tUse sound sample rate of N Hz\n\t\t\t\t 32000 <= N <= 192000, default: 48000\n"; }
const char* getDesc() const { return " N\t\tUse audio sample rate of N Hz\n\t\t\t\t 4000 <= N <= 192000, default: 48000\n"; }
unsigned getRate() const { return rate; }
};
@ -121,6 +126,39 @@ public:
bool useYuv() const { return yuv; }
};
class ResamplerOption : public DescOption {
std::string s;
unsigned resamplerNo;
public:
ResamplerOption();
void exec(const char *const *argv, int index) {
const unsigned tmp = std::atoi(argv[index + 1]);
if (tmp < ResamplerInfo::num())
resamplerNo = tmp;
}
const char* getDesc() const { return s.c_str(); }
unsigned resamplerNumber() const { return resamplerNo; }
};
ResamplerOption::ResamplerOption() : DescOption("resampler", 0, 1), resamplerNo(1) {
std::stringstream ss;
ss << " N\t\tUse audio resampler number N\n";
for (std::size_t i = 0; i < ResamplerInfo::num(); ++i) {
ss << "\t\t\t\t " << i << " = " << ResamplerInfo::get(i).desc;
if (i == resamplerNo)
ss << " [default]";
ss << "\n";
}
s = ss.str();
}
struct JoyData {
enum { CENTERED = SDL_HAT_CENTERED, LEFT = SDL_HAT_LEFT, RIGHT = SDL_HAT_RIGHT, UP = SDL_HAT_UP, DOWN = SDL_HAT_DOWN };
@ -268,121 +306,6 @@ public:
}
};
static unsigned ceiledPowerOf2(unsigned t) {
--t;
t |= t >> 1;
t |= t >> 2;
t |= t >> 4;
t |= t >> 8;
t |= t >> 16;
++t;
return t;
}
struct AudioData {
const unsigned samplesPrFrame;
AudioData(unsigned sampleRate);
~AudioData();
void write(const Uint16 *inBuf);
void read(Uint8 *stream, int len);
private:
const unsigned bufSz;
SDL_mutex *const mut;
SDL_cond *const bufReadyCond;
Sint16 *const buffer;
unsigned rPos;
unsigned wPos;
bool failed;
static void fill_buffer(void *const data, Uint8 *const stream, const int len) {
reinterpret_cast<AudioData*>(data)->read(stream, len);
}
};
AudioData::AudioData(const unsigned srate) :
samplesPrFrame(((srate * 4389) / 262144) + 2),
bufSz(ceiledPowerOf2(samplesPrFrame) * 8),
mut(SDL_CreateMutex()),
bufReadyCond(SDL_CreateCond()),
buffer(new Sint16[bufSz]),
rPos(0),
wPos(0),
failed(false) {
std::memset(buffer, 0, bufSz * sizeof(Sint16));
SDL_AudioSpec spec;
spec.freq = srate;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.samples = bufSz >> 3;
spec.callback = fill_buffer;
spec.userdata = this;
if (SDL_OpenAudio(&spec, NULL) < 0) {
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
failed = true;
}
}
AudioData::~AudioData() {
SDL_PauseAudio(1);
SDL_CloseAudio();
delete []buffer;
SDL_DestroyCond(bufReadyCond);
SDL_DestroyMutex(mut);
}
void AudioData::write(const Uint16 *const inBuf) {
if (failed)
return;
SDL_mutexP(mut);
if ((rPos - wPos + (wPos > rPos ? bufSz : 0)) >> 1 < samplesPrFrame)
SDL_CondWait(bufReadyCond, mut);
{
const unsigned samples1 = std::min((bufSz - wPos) >> 1, samplesPrFrame);
const unsigned samples2 = samplesPrFrame - samples1;
std::memcpy(buffer + wPos, inBuf, samples1 * 4);
if (samples2)
std::memcpy(buffer, inBuf + samples1 * 2, samples2 * 4);
if ((wPos += samplesPrFrame * 2) >= bufSz)
wPos -= bufSz;
}
SDL_mutexV(mut);
}
void AudioData::read(Uint8 *const stream, const int len) {
if (failed)
return;
SDL_mutexP(mut);
const unsigned bytes1 = std::min(static_cast<unsigned>(len), (bufSz - rPos) * 2);
const unsigned bytes2 = len - bytes1;
std::memcpy(stream, buffer + rPos, bytes1);
if (bytes2)
std::memcpy(stream + bytes1, buffer, bytes2);
if ((rPos += len >> 1) >= bufSz)
rPos -= bufSz;
if ((rPos - wPos + (wPos > rPos ? bufSz : 0)) >> 1 >= samplesPrFrame)
SDL_CondSignal(bufReadyCond);
SDL_mutexV(mut);
}
class SdlIniter {
bool failed;
public:
@ -406,7 +329,9 @@ class GambatteSdl {
typedef std::multimap<SDLKey,bool*> keymap_t;
typedef std::multimap<JoyData,bool*> jmap_t;
Uint16 *tmpBuf;
Array<Sint16> inBuf;
Array<Sint16> tmpBuf;
std::auto_ptr<Resampler> resampler;
GB gambatte;
SdlIniter sdlIniter;
SdlBlitter blitter;
@ -427,13 +352,11 @@ public:
int exec();
};
GambatteSdl::GambatteSdl(int argc, char **argv) : tmpBuf(NULL), sampleRate(48000) {
GambatteSdl::GambatteSdl(int argc, char **argv) : inBuf((35112 + 2064) * 2), sampleRate(48000) {
failed = init(argc, argv);
}
GambatteSdl::~GambatteSdl() {
delete []tmpBuf;
for (std::size_t i = 0; i < joysticks.size(); ++i)
SDL_JoystickClose(joysticks[i]);
}
@ -473,6 +396,8 @@ bool GambatteSdl::init(int argc, char **argv) {
v.push_back(&lkOption);
RateOption rateOption;
v.push_back(&rateOption);
ResamplerOption resamplerOption;
v.push_back(&resamplerOption);
ScaleOption scaleOption;
v.push_back(&scaleOption);
VfOption vfOption(gambatte.filterInfo());
@ -532,6 +457,7 @@ bool GambatteSdl::init(int argc, char **argv) {
blitter.setScale(scaleOption.getScale());
blitter.setYuv(yuvOption.useYuv());
gambatte.setVideoFilter(vfOption.filterNumber());
resampler.reset(ResamplerInfo::get(resamplerOption.resamplerNumber()).create(2097152, sampleRate, 35112));
bool* gbbuts[8] = {
&inputGetter.is.startButton, &inputGetter.is.selectButton,
@ -588,11 +514,13 @@ int GambatteSdl::exec() {
return 1;
AudioData adata(sampleRate);
tmpBuf = new Uint16[adata.samplesPrFrame * 2];
tmpBuf.reset(resampler->maxOut(35112) * 2);
gambatte.setVideoBlitter(&blitter);
Uint8 *keys = SDL_GetKeyState(NULL);
unsigned samples = 0;
long estSrate = sampleRate;
SDL_PauseAudio(0);
@ -680,16 +608,20 @@ int GambatteSdl::exec() {
}
}
gambatte.runFor(70224);
samples += gambatte.runFor(reinterpret_cast<Gambatte::uint_least32_t*>(static_cast<Sint16*>(inBuf)) + samples, 35112 - samples);
const unsigned tmpsamples = resampler->resample(tmpBuf, inBuf, 35112);
samples -= 35112;
std::memmove(inBuf, inBuf + 35112 * 2, samples * sizeof(Sint16) * 2);
if (!keys[SDLK_TAB]) {
gambatte.fill_buffer(tmpBuf, adata.samplesPrFrame);
const RateEst::Result &rsrate = adata.write(tmpBuf, tmpsamples);
const long newEstSrate = rsrate.est + (rsrate.var * 2);
if (std::abs(newEstSrate - estSrate) > rsrate.var * 2)
estSrate = newEstSrate;
adata.write(tmpBuf);
syncFunc();
} else
gambatte.fill_buffer(0, 0);
syncfunc((16743ul - (16743 / 2048)) * sampleRate / estSrate);
}
}
return 0;

View File

@ -0,0 +1,103 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RINGBUFFER_H
#define RINGBUFFER_H
#include <cstddef>
#include <algorithm>
#include <cstring>
template<typename T>
class RingBuffer {
T *const buf;
const std::size_t sz;
std::size_t rpos;
std::size_t wpos;
public:
RingBuffer(const std::size_t sz_in) : buf(new T[sz_in + 1]), sz(sz_in + 1), rpos(0), wpos(0) {}
~RingBuffer() { delete []buf; }
std::size_t avail() const {
return (wpos < rpos ? 0 : sz) + rpos - wpos - 1;
}
void clear() {
wpos = rpos = 0;
}
void fill(T value);
void read(T *out, std::size_t num);
std::size_t size() const {
return sz - 1;
}
std::size_t used() const {
return (wpos < rpos ? sz : 0) + wpos - rpos;
}
void write(const T *in, std::size_t num);
};
template<typename T>
void RingBuffer<T>::fill(const T value) {
std::fill(buf, buf + sz, value);
rpos = 0;
wpos = sz - 1;
}
template<typename T>
void RingBuffer<T>::read(T *out, std::size_t num) {
if (rpos + num > sz) {
const std::size_t n = sz - rpos;
std::memcpy(out, buf + rpos, n * sizeof(T));
rpos = 0;
num -= n;
out += n;
}
std::memcpy(out, buf + rpos, num * sizeof(T));
if ((rpos += num) == sz)
rpos = 0;
}
template<typename T>
void RingBuffer<T>::write(const T *in, std::size_t num) {
if (wpos + num > sz) {
const std::size_t n = sz - wpos;
std::memcpy(buf + wpos, in, n * sizeof(T));
wpos = 0;
num -= n;
in += n;
}
std::memcpy(buf + wpos, in, num * sizeof(T));
if ((wpos += num) == sz)
wpos = 0;
}
#endif

View File

@ -1 +0,0 @@
../../gambatte_qt/src/scalebuffer.h

View File

@ -0,0 +1,57 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SCALEBUFFER_H_
#define SCALEBUFFER_H_
#include <cstring>
template<typename T>
static inline void do_scaleBuffer(const T *s, T *d, const unsigned srcW, const unsigned srcH, const unsigned dstPitch, const unsigned scale) {
const unsigned dstW = srcW * scale;
for (unsigned h = srcH; h--;) {
for (unsigned w = srcW; w--;) {
for (unsigned n = scale; n--;)
*d++ = *s;
++s;
}
s += dstPitch - dstW;
for (unsigned n = scale; --n; d += dstPitch)
std::memcpy(d, d - dstPitch, dstW * sizeof(T));
}
}
template<typename T>
void scaleBuffer(const T *s, T *d, const unsigned srcW, const unsigned srcH, const unsigned dstPitch, const unsigned scale) {
switch (scale) {
case 2: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 2); break;
case 3: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 3); break;
case 4: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 4); break;
case 5: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 5); break;
case 6: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 6); break;
case 7: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 7); break;
case 8: do_scaleBuffer(s, d, srcW, srcH, dstPitch, 8); break;
default: do_scaleBuffer(s, d, srcW, srcH, dstPitch, scale); break;
}
}
#endif /*SCALEBUFFER_H_*/

View File

@ -15,76 +15,23 @@
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
***************************************************************************/
#include "syncfunc.h"
#include <adaptivesleep.h>
#include <SDL.h>
namespace SyncFunc {
struct timeval {
long tv_sec;
long tv_usec;
};
void gettimeofday(timeval *const t, void *) {
const unsigned long tv_msec = SDL_GetTicks();
t->tv_sec = tv_msec / 1000;
t->tv_usec = (tv_msec % 1000) * 1000;
}
usec_t getusecs() {
return SDL_GetTicks() * usec_t(1000);
}
void syncFunc() {
using SyncFunc::timeval;
using SyncFunc::gettimeofday;
timeval t;
gettimeofday(&t, NULL);
static timeval time = t;
static long late = 0;
static unsigned noSleep = 60;
if (time.tv_sec > t.tv_sec || (time.tv_sec == t.tv_sec && time.tv_usec > t.tv_usec)) {
timeval tmp;
tmp.tv_sec = 0;
tmp.tv_usec = time.tv_usec - t.tv_usec;
if (time.tv_sec != t.tv_sec)
tmp.tv_usec += 1000000;
if (tmp.tv_usec > late) {
tmp.tv_usec -= late;
if (tmp.tv_usec >= 1000000) {
tmp.tv_usec -= 1000000;
++tmp.tv_sec;
}
SDL_Delay(tmp.tv_sec * 1000 + (tmp.tv_usec + 500) / 1000);
gettimeofday(&t, NULL);
late -= ((time.tv_sec - t.tv_sec) * 1000000 + time.tv_usec - t.tv_usec) >> 1;
// printf("late: %d\n", late);
if (late < 0)
late = 0;
noSleep = 60;
} else if (noSleep-- == 0) {
noSleep = 60;
late = 0;
}
while (time.tv_sec > t.tv_sec || (time.tv_sec == t.tv_sec && time.tv_usec > t.tv_usec)) {
gettimeofday(&t, NULL);
}
} else
time = t;
time.tv_usec += 16743;
if (time.tv_usec >= 1000000) {
time.tv_usec -= 1000000;
++time.tv_sec;
}
void usecsleep(const usec_t usecs) {
SDL_Delay((usecs + 999) / 1000);
}
void syncfunc(const long inc) {
static AdaptiveSleep asleep;
static usec_t last = getusecs();
last += asleep.sleepUntil(last, inc);
last += inc;
}

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -19,6 +19,6 @@
#ifndef SYNCFUNC_H
#define SYNCFUNC_H
void syncFunc();
void syncfunc(long inc);
#endif

View File

@ -2,7 +2,7 @@ global_cflags = ARGUMENTS.get('CFLAGS', '-Wall -Wextra -O2 -fomit-frame-pointer'
global_cxxflags = ARGUMENTS.get('CXXFLAGS', global_cflags + ' -fno-exceptions -fno-rtti')
global_defines = ' -DHAVE_STDINT_H -DCHAR_WIDTH_8'
env = Environment(CPPPATH = ['src', 'include'],
env = Environment(CPPPATH = ['src', 'include', '../common'],
CFLAGS = global_cflags + global_defines,
CXXFLAGS = global_cxxflags + global_defines)

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -39,7 +39,21 @@ public:
GB();
~GB();
bool load(const char* romfile);
void runFor(unsigned long cycles);
/** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer.
* There are 35112 stereo sound samples in a video frame.
* May run for uptil 2064 stereo samples too long.
* A stereo sample consists of two native endian 2s complement 16-bit PCM samples,
* with the left sample preceding the right one. Usually casting soundBuf to/from
* short* is OK and recommended. The reason for not using a short* in the interface
* is to avoid implementation defined behaviour without compromising performance.
*
* @param soundBuf buffer with space >= samples + 2064
* @param samples number of stereo samples to produce
* @return actual number of samples produced
*/
unsigned runFor(Gambatte::uint_least32_t *soundBuf, unsigned samples);
void reset();
void setVideoBlitter(VideoBlitter *vb);
void videoBufferChange();
@ -50,8 +64,6 @@ public:
void setVideoFilter(unsigned n);
std::vector<const FilterInfo*> filterInfo() const;
void setInputStateGetter(InputStateGetter *getInput);
void fill_buffer(uint_least16_t *stream, unsigned samples);
void set_savedir(const char *sdir);
bool isCgb() const;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -530,7 +530,7 @@ void CPU::loadState(const SaveState &state) {
} while (0)
void CPU::process(const unsigned long cycles) {
memory.inc_endtime(cycles);
memory.setEndtime(cycleCounter_, cycles);
unsigned char A = A_;
unsigned long cycleCounter = cycleCounter_;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -98,9 +98,8 @@ public:
bool load(const char* romfile);
void sound_fill_buffer(Gambatte::uint_least16_t *const stream, const unsigned samples) {
memory.sound_fill_buffer(stream, samples, cycleCounter_);
}
void setSoundBuffer(Gambatte::uint_least32_t *const buf) { memory.setSoundBuffer(buf); }
unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); }
bool isCgb() const { return memory.isCgb(); }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -48,8 +48,11 @@ GB::~GB() {
delete z80;
}
void GB::runFor(const unsigned long cycles) {
z80->runFor(cycles);
unsigned GB::runFor(Gambatte::uint_least32_t *const soundBuf, const unsigned samples) {
z80->setSoundBuffer(soundBuf);
z80->runFor(samples * 2);
return z80->fillSoundBuffer();
}
void GB::reset() {
@ -115,10 +118,6 @@ bool GB::load(const char* romfile) {
return failed;
}
void GB::fill_buffer(uint_least16_t *const stream, const unsigned samples) {
z80->sound_fill_buffer(stream, samples);
}
bool GB::isCgb() const {
return z80->isCgb();
}

View File

@ -234,12 +234,17 @@ void Memory::ei(const unsigned long cycleCounter) {
rescheduleIrq(cycleCounter);
}
void Memory::inc_endtime(const unsigned long inc) {
void Memory::incEndtime(const unsigned long inc) {
active = true;
next_endtime += inc << isDoubleSpeed();
set_event();
}
void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
next_endtime = cycleCounter;
incEndtime(inc);
}
void Memory::set_irqEvent() {
next_irqEventTime = next_timatime;
next_irqEvent = TIMA;
@ -1831,9 +1836,9 @@ void Memory::set_savedir(const char *dir) {
}
}
void Memory::sound_fill_buffer(Gambatte::uint_least16_t *const stream, const unsigned samples, const unsigned long cycleCounter) {
unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
sound.generate_samples(cycleCounter, isDoubleSpeed());
sound.fill_buffer(stream, samples);
return sound.fillBuffer();
}
void Memory::setVideoBlitter(Gambatte::VideoBlitter *const vb) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -207,9 +207,11 @@ public:
}
void schedule_unhalt();
void inc_endtime(unsigned long inc);
void incEndtime(unsigned long inc);
void setEndtime(unsigned long cc, unsigned long inc);
void sound_fill_buffer(Gambatte::uint_least16_t *stream, unsigned samples, unsigned long cycleCounter);
void setSoundBuffer(Gambatte::uint_least32_t *const buf) { sound.setBuffer(buf); }
unsigned fillSoundBuffer(unsigned long cc);
void setVideoBlitter(Gambatte::VideoBlitter * vb);
void setVideoFilter(unsigned int n);

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -41,22 +41,18 @@ S 1 - Clock -
S) start step on sound power on.
*/
static const unsigned bufferSize = 35112 + 16 + 2048; //FIXME: DMA can prevent process from returning for up to 4096 cycles.
// static const unsigned bufferSize = 35112 + 16 + 2048; //FIXME: DMA can prevent process from returning for up to 4096 cycles.
PSG::PSG() :
buffer(new Gambatte::uint_least32_t[bufferSize]),
buffer(NULL),
lastUpdate(0),
soVol(0),
rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later
bufferPos(0),
enabled(false)
{}
PSG::~PSG() {
delete[] buffer;
}
void PSG::init(const bool cgb) {
// bufferPos = 0;
ch1.init(cgb);
ch2.init(cgb);
ch3.init(cgb);
@ -96,6 +92,7 @@ void PSG::loadState(const SaveState &state) {
void PSG::accumulate_channels(const unsigned long cycles) {
Gambatte::uint_least32_t *const buf = buffer + bufferPos;
std::memset(buf, 0, cycles * sizeof(Gambatte::uint_least32_t));
ch1.update(buf, soVol, cycles);
ch2.update(buf, soVol, cycles);
ch3.update(buf, soVol, cycles);
@ -106,9 +103,6 @@ void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doub
const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
lastUpdate += cycles << (1 + doubleSpeed);
if (bufferSize - bufferPos < cycles)
bufferPos = bufferSize - cycles;
if (cycles)
accumulate_channels(cycles);
@ -120,65 +114,40 @@ void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, con
lastUpdate = newCc - (oldCc - lastUpdate);
}
void PSG::fill_buffer(Gambatte::uint_least16_t *stream, const unsigned samples) {
const unsigned long endPos = std::min(bufferPos, 35112U);
unsigned PSG::fillBuffer() {
Gambatte::uint_least32_t sum = rsum;
Gambatte::uint_least32_t *b = buffer;
unsigned n = bufferPos;
if (stream && samples && endPos >= samples) {
Gambatte::uint_least16_t *const streamEnd = stream + samples * 2;
const Gambatte::uint_least32_t *buf = buffer;
const unsigned long ratio = (endPos << 16) / samples;
unsigned whole = ratio >> 16;
unsigned frac = ratio & 0xFFFF;
unsigned long so1 = 0;
unsigned long so2 = 0;
while (stream < streamEnd) {
{
unsigned long soTmp = 0;
for (const Gambatte::uint_least32_t *const end = buf + whole; buf != end;)
soTmp += *buf++;
so1 += soTmp & 0xFFFF0000;
so2 += soTmp << 16 & 0xFFFFFFFF;
}
{
const unsigned long borderSample = *buf++;
const unsigned long so1Tmp = (borderSample >> 16) * frac;
const unsigned long so2Tmp = (borderSample & 0xFFFF) * frac;
so1 += so1Tmp;
so2 += so2Tmp;
*stream++ = ((so2 / 4389) * samples + 8192) >> 14;
*stream++ = ((so1 / 4389) * samples + 8192) >> 14;
so1 = (borderSample & 0xFFFF0000) - so1Tmp;
so2 = (borderSample << 16 & 0xFFFFFFFF) - so2Tmp;
}
const unsigned long nextTotal = ratio - ((1ul << 16) - frac);
whole = nextTotal >> 16;
frac = nextTotal & 0xFFFF;
}
while (n--) {
sum += *b;
*b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word
}
bufferPos -= endPos;
std::memmove(buffer, buffer + endPos, bufferPos * sizeof(Gambatte::uint_least32_t));
rsum = sum;
return bufferPos;
}
#ifdef WORDS_BIGENDIAN
static const unsigned long so1Mul = 0x00000001;
static const unsigned long so2Mul = 0x00010000;
#else
static const unsigned long so1Mul = 0x00010000;
static const unsigned long so2Mul = 0x00000001;
#endif
void PSG::set_so_volume(const unsigned nr50) {
soVol = (((nr50 & 0x7) + 1ul) << 16) | ((nr50 >> 4 & 0x7) + 1);
soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64;
}
void PSG::map_so(const unsigned nr51) {
ch1.setSo(nr51 & 0x01, (nr51 & 0x10) != 0);
ch2.setSo((nr51 & 0x02) != 0, (nr51 & 0x20) != 0);
ch3.setSo((nr51 & 0x04) != 0, (nr51 & 0x40) != 0);
ch4.setSo((nr51 & 0x08) != 0, (nr51 & 0x80) != 0);
const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
ch1.setSo((tmp & 0x00010001) * 0xFFFF);
ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF);
ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF);
}
unsigned PSG::getStatus() const {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -34,10 +34,13 @@ class PSG {
Channel3 ch3;
Channel4 ch4;
Gambatte::uint_least32_t *const buffer;
Gambatte::uint_least32_t *buffer;
unsigned long lastUpdate;
unsigned long soVol;
Gambatte::uint_least32_t rsum;
unsigned bufferPos;
bool enabled;
@ -46,7 +49,6 @@ class PSG {
public:
PSG();
~PSG();
void init(bool cgb);
void reset();
void setStatePtrs(SaveState &state);
@ -55,7 +57,8 @@ public:
void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
void fill_buffer(Gambatte::uint_least16_t *stream, const unsigned samples);
unsigned fillBuffer();
void setBuffer(Gambatte::uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
bool isEnabled() const { return enabled; }
void setEnabled(bool value) { enabled = value; }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -109,6 +109,7 @@ Channel1::Channel1() :
sweepUnit(disableMaster, dutyUnit),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
@ -116,8 +117,8 @@ Channel1::Channel1() :
}
void Channel1::setEvent() {
nextEventUnit = &dutyUnit;
if (sweepUnit.getCounter() < nextEventUnit->getCounter())
// nextEventUnit = &dutyUnit;
// if (sweepUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &sweepUnit;
if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
@ -168,8 +169,8 @@ void Channel1::setNr4(const unsigned data) {
setEvent();
}
void Channel1::setSo(const bool so1, const bool so2) {
soMask = (so1 ? 0xFFFF0000 : 0) | (so2 ? 0xFFFF : 0);
void Channel1::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
@ -213,26 +214,36 @@ void Channel1::loadState(const SaveState &state) {
void Channel1::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
while (cycleCounter < endCycles) {
const unsigned long out = 15 * 8 * 4 * 0x00010001ul +
outBase * ((master && dutyUnit.isHighState()) ? envelopeUnit.getVolume() * 2 - 15ul : 0 - 15ul);
for (;;) {
const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
unsigned long multiplier = nextEventUnit->getCounter();
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf = out - prevOut;
prevOut = out;
buf += dutyUnit.getCounter() - cycleCounter;
cycleCounter = dutyUnit.getCounter();
dutyUnit.event();
out = dutyUnit.isHighState() ? outHigh : outLow;
}
if (multiplier <= endCycles) {
if (cycleCounter < nextMajorEvent) {
*buf = out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
multiplier = endCycles;
multiplier -= cycleCounter;
cycleCounter += multiplier;
std::fill_n(buf, multiplier, out);
buf += multiplier;
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -62,6 +62,7 @@ class Channel1 {
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned char nr4;
bool master;
@ -76,7 +77,7 @@ public:
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(bool so1, bool so2);
void setSo(unsigned long soMask);
bool isActive() const { return master; }
void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -26,6 +26,7 @@ Channel2::Channel2() :
envelopeUnit(staticOutputTest),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
@ -33,8 +34,8 @@ Channel2::Channel2() :
}
void Channel2::setEvent() {
nextEventUnit = &dutyUnit;
if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
// nextEventUnit = &dutyUnit;
// if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
@ -77,8 +78,8 @@ void Channel2::setNr4(const unsigned data) {
setEvent();
}
void Channel2::setSo(const bool so1, const bool so2) {
soMask = (so1 ? 0xFFFF0000 : 0) | (so2 ? 0xFFFF : 0);
void Channel2::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
@ -118,30 +119,36 @@ void Channel2::loadState(const SaveState &state) {
void Channel2::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
while (cycleCounter < endCycles) {
const unsigned long out = outBase * ((master && dutyUnit.isHighState()) ? envelopeUnit.getVolume() * 2 - 15ul : 0 - 15ul);
for (;;) {
const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
unsigned long multiplier = nextEventUnit->getCounter();
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += dutyUnit.getCounter() - cycleCounter;
cycleCounter = dutyUnit.getCounter();
dutyUnit.event();
out = dutyUnit.isHighState() ? outHigh : outLow;
}
if (multiplier <= endCycles) {
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
multiplier = endCycles;
multiplier -= cycleCounter;
cycleCounter += multiplier;
Gambatte::uint_least32_t *const bufend = buf + multiplier;
if (out) {
while (buf != bufend)
(*buf++) += out;
}
buf = bufend;
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -41,6 +41,7 @@ class Channel2 {
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned char nr4;
bool master;
@ -54,7 +55,7 @@ public:
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(bool so1, bool so2);
void setSo(unsigned long soMask);
// void deactivate() { disableMaster(); setEvent(); }
bool isActive() const { return master; }

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -30,6 +30,7 @@ Channel3::Channel3() :
lengthCounter(disableMaster, 0xFF),
cycleCounter(0),
soMask(0),
prevOut(0),
waveCounter(SoundUnit::COUNTER_DISABLED),
lastReadTime(0),
nr0(0),
@ -77,8 +78,8 @@ void Channel3::setNr4(const unsigned data) {
}
}
void Channel3::setSo(const bool so1, const bool so2) {
soMask = (so1 ? 0xFFFF0000 : 0) | (so2 ? 0xFFFF : 0);
void Channel3::setSo(const unsigned long soMask) {
this->soMask = soMask;
}
void Channel3::reset() {
@ -146,43 +147,42 @@ void Channel3::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseV
if (outBase && rShift != 4) {
const unsigned long endCycles = cycleCounter + cycles;
while (cycleCounter < endCycles) {
const unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
for (;;) {
const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
while (waveCounter <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += waveCounter - cycleCounter;
cycleCounter = waveCounter;
unsigned long multiplier = endCycles;
if (waveCounter <= multiplier || lengthCounter.getCounter() <= multiplier) {
if (lengthCounter.getCounter() < waveCounter) {
multiplier = lengthCounter.getCounter();
lengthCounter.event();
} else {
lastReadTime = multiplier = waveCounter;
waveCounter += toPeriod(nr3, nr4);
++wavePos;
wavePos &= 0x1F;
sampleBuf = waveRam[wavePos >> 1];
}
lastReadTime = waveCounter;
waveCounter += toPeriod(nr3, nr4);
++wavePos;
wavePos &= 0x1F;
sampleBuf = waveRam[wavePos >> 1];
out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/);
}
multiplier -= cycleCounter;
cycleCounter += multiplier;
Gambatte::uint_least32_t *const bufend = buf + multiplier;
if (out) {
while (buf != bufend)
(*buf++) += out;
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
buf = bufend;
if (lengthCounter.getCounter() == nextMajorEvent) {
lengthCounter.event();
} else
break;
}
} else {
if (outBase) {
const unsigned long out = outBase * (0 - 15ul);
Gambatte::uint_least32_t *const bufend = buf + cycles;
while (buf != bufend)
(*buf++) += out;
*buf += out - prevOut;
prevOut = out;
}
cycleCounter += cycles;

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -42,6 +42,7 @@ class Channel3 {
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned long waveCounter;
unsigned long lastReadTime;
@ -70,7 +71,7 @@ public:
void setNr2(unsigned data);
void setNr3(unsigned data) { nr3 = data; }
void setNr4(unsigned data);
void setSo(bool so1, bool so2);
void setSo(unsigned long soMask);
void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
unsigned waveRamRead(unsigned index) const {

View File

@ -89,7 +89,7 @@ void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
3, 1, 1, 2, 2, 1, 1, 6,
};*/
void Channel4::Lfsr::event() {
inline void Channel4::Lfsr::event() {
if (nr3 < 0xE0) {
const unsigned shifted = reg >> 1;
const unsigned xored = (reg ^ shifted) & 1;
@ -168,6 +168,7 @@ Channel4::Channel4() :
envelopeUnit(staticOutputTest),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
@ -175,8 +176,8 @@ Channel4::Channel4() :
}
void Channel4::setEvent() {
nextEventUnit = &lfsr;
if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
// nextEventUnit = &lfsr;
// if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
@ -216,8 +217,8 @@ void Channel4::setNr4(const unsigned data) {
setEvent();
}
void Channel4::setSo(const bool so1, const bool so2) {
soMask = (so1 ? 0xFFFF0000 : 0) | (so2 ? 0xFFFF : 0);
void Channel4::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
@ -257,30 +258,36 @@ void Channel4::loadState(const SaveState &state) {
void Channel4::update(Gambatte::uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
while (cycleCounter < endCycles) {
const unsigned long out = outBase * ((/*master && */lfsr.isHighState()) ? envelopeUnit.getVolume() * 2 - 15ul : 0 - 15ul);
for (;;) {
const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = lfsr.isHighState() ? outHigh : outLow;
unsigned long multiplier = nextEventUnit->getCounter();
while (lfsr.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += lfsr.getCounter() - cycleCounter;
cycleCounter = lfsr.getCounter();
lfsr.event();
out = lfsr.isHighState() ? outHigh : outLow;
}
if (multiplier <= endCycles) {
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
multiplier = endCycles;
multiplier -= cycleCounter;
cycleCounter += multiplier;
Gambatte::uint_least32_t *const bufend = buf + multiplier;
if (out) {
while (buf != bufend)
(*buf++) += out;
}
buf = bufend;
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
@ -71,6 +71,7 @@ class Channel4 {
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned char nr4;
bool master;
@ -84,7 +85,7 @@ public:
void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
void setNr4(unsigned data);
void setSo(bool so1, bool so2);
void setSo(unsigned long soMask);
bool isActive() const { return master; }
void update(Gambatte::uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *

View File

@ -26,6 +26,8 @@
#include "filterinfo.h"
#include "savestate.h"
#include "video/basic_add_event.h"
#include <cstring>
#include <algorithm>
static void addEventIfActivated(event_queue<VideoEvent*,VideoEventComparer> &q, VideoEvent *const e, const unsigned long newTime) {
e->setTime(newTime);
@ -118,6 +120,9 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram_in)
bgEnable(false),
spriteEnable(false)
{
std::memset(bgpData, 0, sizeof(bgpData));
std::memset(objpData, 0, sizeof(objpData));
for (unsigned i = 0; i < sizeof(dmgColorsRgb32) / sizeof(unsigned long); ++i) {
setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
}