mirror of
https://github.com/libretro/gambatte-libretro.git
synced 2024-11-26 17:30:23 +00:00
- 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:
parent
6aff6b2cbd
commit
5bbabdcfdd
56
common/adaptivesleep.cpp
Normal file
56
common/adaptivesleep.cpp
Normal 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
34
common/adaptivesleep.h
Normal 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
|
@ -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
48
common/rateest.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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
|
100
common/resample/blackmansinc.h
Normal file
100
common/resample/blackmansinc.h
Normal 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
|
118
common/resample/chainresampler.cpp
Normal file
118
common/resample/chainresampler.cpp
Normal 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();
|
||||
}
|
189
common/resample/chainresampler.h
Normal file
189
common/resample/chainresampler.h
Normal 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
198
common/resample/cic2.h
Normal 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
382
common/resample/cic3.h
Normal 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
237
common/resample/cic4.h
Normal 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
|
156
common/resample/convoluter.h
Normal file
156
common/resample/convoluter.h
Normal 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
|
100
common/resample/hammingsinc.h
Normal file
100
common/resample/hammingsinc.h
Normal 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
129
common/resample/linint.h
Normal 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
|
104
common/resample/makesinckernel.h
Normal file
104
common/resample/makesinckernel.h
Normal 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
|
99
common/resample/rectsinc.h
Normal file
99
common/resample/rectsinc.h
Normal 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
|
43
common/resample/resampler.h
Normal file
43
common/resample/resampler.h
Normal 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
|
61
common/resample/resamplerinfo.cpp
Normal file
61
common/resample/resamplerinfo.cpp
Normal 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);
|
36
common/resample/resamplerinfo.h
Normal file
36
common/resample/resamplerinfo.h
Normal 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
|
33
common/resample/subresampler.h
Normal file
33
common/resample/subresampler.h
Normal 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
|
54
common/resample/u48div.cpp
Normal file
54
common/resample/u48div.cpp
Normal 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
24
common/resample/u48div.h
Normal 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
|
@ -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
31
common/usec.h
Normal 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
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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(); }
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(); }
|
||||
|
@ -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(¤t, 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(¤t, 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(¤t, 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);
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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(); }
|
||||
|
@ -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*/) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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(); }
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(); }
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
31
gambatte_qt/src/rational.h
Normal file
31
gambatte_qt/src/rational.h
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
93
gambatte_sdl/src/audiodata.cpp
Normal file
93
gambatte_sdl/src/audiodata.cpp
Normal 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);
|
||||
}
|
44
gambatte_sdl/src/audiodata.h
Normal file
44
gambatte_sdl/src/audiodata.h
Normal 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
|
@ -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;
|
||||
|
103
gambatte_sdl/src/ringbuffer.h
Normal file
103
gambatte_sdl/src/ringbuffer.h
Normal 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
|
@ -1 +0,0 @@
|
||||
../../gambatte_qt/src/scalebuffer.h
|
57
gambatte_sdl/src/scalebuffer.h
Normal file
57
gambatte_sdl/src/scalebuffer.h
Normal 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_*/
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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_;
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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; }
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user