mirror of https://github.com/bsnes-emu/bsnes.git
Update to v084r03 release.
(r02 was not posted to the WIP thread) byuu says: Internally, all color is processed with 30-bit precision. The filters also operate at 30-bit depth. There's a new config file setting, video.depth, which defaults to 24. This causes the final output to downsample to 24-bit, as most will require. If you set it to 30-bit, the downsampling will not occur, and bsnes will ask ruby for a 30-bit surface. If you don't have one available, you're going to get bad colors. Or maybe even a crash with OpenGL. I don't yet have detection code to make sure you have an appropriate visual in place. 30-bit mode will really only work if you are running Linux, running Xorg at Depth 30, use the OpenGL or XShm driver, have an nVidia Quadro or AMD FireGL card with the official drivers, and have a 30-bit capable monitor. Lots of planning and work for very little gain here, but it's nice that it's finally finished. Oh, I had to change the contrast/brightness formulas a tiny bit, but they still work and look nice.
This commit is contained in:
parent
2cc077e12b
commit
ad0805b168
|
@ -12,7 +12,7 @@ ui := ui
|
|||
# compiler
|
||||
c := $(compiler) -std=gnu99
|
||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||
flags := -O3 -fomit-frame-pointer -I.
|
||||
flags := -I. -O3 -fomit-frame-pointer
|
||||
link :=
|
||||
objects := libco
|
||||
|
||||
|
@ -29,8 +29,6 @@ endif
|
|||
|
||||
# platform
|
||||
ifeq ($(platform),x)
|
||||
# tree vectorization causes code generation errors with Linux/GCC 4.6.1
|
||||
flags += -fno-tree-vectorize
|
||||
link += -s -ldl -lX11 -lXext
|
||||
else ifeq ($(platform),osx)
|
||||
else ifeq ($(platform),win)
|
||||
|
|
|
@ -8,11 +8,49 @@ namespace nall {
|
|||
struct compositor {
|
||||
inline static bool enabled();
|
||||
inline static bool enable(bool status);
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
|
||||
inline static Compositor detect();
|
||||
|
||||
inline static bool enabled_metacity();
|
||||
inline static bool enable_metacity(bool status);
|
||||
|
||||
inline static bool enabled_xfwm4();
|
||||
inline static bool enable_xfwm4(bool status);
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
|
||||
bool compositor::enabled() {
|
||||
//Metacity
|
||||
|
||||
bool compositor::enabled_metacity() {
|
||||
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||
|
||||
if(!memcmp(buffer, "true", 4)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable_metacity(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
|
||||
} else {
|
||||
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
|
||||
}
|
||||
if(fp == 0) return false;
|
||||
pclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Xfwm4
|
||||
|
||||
bool compositor::enabled_xfwm4() {
|
||||
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
|
@ -23,7 +61,7 @@ bool compositor::enabled() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
bool compositor::enable_xfwm4(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
|
||||
|
@ -35,6 +73,41 @@ bool compositor::enable(bool status) {
|
|||
return true;
|
||||
}
|
||||
|
||||
//General
|
||||
|
||||
compositor::Compositor compositor::detect() {
|
||||
Compositor result = Compositor::Unknown;
|
||||
|
||||
FILE *fp;
|
||||
char buffer[512];
|
||||
|
||||
fp = popen("pidof metacity", "r");
|
||||
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
|
||||
pclose(fp);
|
||||
|
||||
fp = popen("pidof xfwm4", "r");
|
||||
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
|
||||
pclose(fp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compositor::enabled() {
|
||||
switch(detect()) {
|
||||
case Compositor::Metacity: return enabled_metacity();
|
||||
case Compositor::Xfwm4: return enabled_xfwm4();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
switch(detect()) {
|
||||
case Compositor::Metacity: return enable_metacity(status);
|
||||
case Compositor::Xfwm4: return enable_xfwm4(status);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
bool compositor::enabled() {
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
#ifndef NALL_IMAGE_HPP
|
||||
#define NALL_IMAGE_HPP
|
||||
|
||||
#include <nall/bmp.hpp>
|
||||
#include <nall/interpolation.hpp>
|
||||
#include <nall/png.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct image {
|
||||
uint8_t *data;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned pitch;
|
||||
|
||||
bool endian; //0 = little, 1 = big
|
||||
unsigned depth;
|
||||
unsigned stride;
|
||||
|
||||
struct Channel {
|
||||
uint64_t mask;
|
||||
unsigned depth;
|
||||
unsigned shift;
|
||||
} alpha, red, green, blue;
|
||||
|
||||
typedef double (*interpolation)(double, double, double, double, double);
|
||||
static inline unsigned bitDepth(uint64_t color);
|
||||
static inline unsigned bitShift(uint64_t color);
|
||||
static inline uint64_t normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth);
|
||||
|
||||
inline image& operator=(const image &source);
|
||||
inline image& operator=(image &&source);
|
||||
inline image(const image &source);
|
||||
inline image(image &&source);
|
||||
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
inline ~image();
|
||||
|
||||
inline uint64_t read(const uint8_t *data) const;
|
||||
inline void write(uint8_t *data, uint64_t value) const;
|
||||
|
||||
inline void free();
|
||||
inline void allocate(unsigned width, unsigned height);
|
||||
inline bool load(const string &filename);
|
||||
inline void scale(unsigned width, unsigned height, interpolation op);
|
||||
inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
|
||||
inline void alphaBlend(uint64_t alphaColor);
|
||||
|
||||
protected:
|
||||
inline uint64_t interpolate(double mu, const uint64_t *s, interpolation op);
|
||||
inline void scaleX(unsigned width, interpolation op);
|
||||
inline void scaleY(unsigned height, interpolation op);
|
||||
inline bool loadBMP(const string &filename);
|
||||
inline bool loadPNG(const string &filename);
|
||||
};
|
||||
|
||||
//static
|
||||
|
||||
unsigned image::bitDepth(uint64_t color) {
|
||||
unsigned depth = 0;
|
||||
if(color) while((color & 1) == 0) color >>= 1;
|
||||
while((color & 1) == 1) { color >>= 1; depth++; }
|
||||
return depth;
|
||||
}
|
||||
|
||||
unsigned image::bitShift(uint64_t color) {
|
||||
unsigned shift = 0;
|
||||
if(color) while((color & 1) == 0) { color >>= 1; shift++; }
|
||||
return shift;
|
||||
}
|
||||
|
||||
uint64_t image::normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) {
|
||||
while(sourceDepth < targetDepth) {
|
||||
color = (color << sourceDepth) | color;
|
||||
sourceDepth += sourceDepth;
|
||||
}
|
||||
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
|
||||
return color;
|
||||
}
|
||||
|
||||
//public
|
||||
|
||||
image& image::operator=(const image &source) {
|
||||
free();
|
||||
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
|
||||
endian = source.endian;
|
||||
stride = source.stride;
|
||||
|
||||
alpha = source.alpha;
|
||||
red = source.red;
|
||||
green = source.green;
|
||||
blue = source.blue;
|
||||
|
||||
data = new uint8_t[width * height * stride];
|
||||
memcpy(data, source.data, width * height * stride);
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::operator=(image &&source) {
|
||||
width = source.width;
|
||||
height = source.height;
|
||||
pitch = source.pitch;
|
||||
|
||||
endian = source.endian;
|
||||
stride = source.stride;
|
||||
|
||||
alpha = source.alpha;
|
||||
red = source.red;
|
||||
green = source.green;
|
||||
blue = source.blue;
|
||||
|
||||
data = source.data;
|
||||
source.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
image::image(const image &source) : data(nullptr) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
image::image(image &&source) : data(nullptr) {
|
||||
operator=(std::forward<image>(source));
|
||||
}
|
||||
|
||||
image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) : data(nullptr) {
|
||||
width = 0, height = 0, pitch = 0;
|
||||
|
||||
this->endian = endian;
|
||||
this->depth = depth;
|
||||
this->stride = (depth / 8) + ((depth & 7) > 0);
|
||||
|
||||
alpha.mask = alphaMask, red.mask = redMask, green.mask = greenMask, blue.mask = blueMask;
|
||||
alpha.depth = bitDepth(alpha.mask), alpha.shift = bitShift(alpha.mask);
|
||||
red.depth = bitDepth(red.mask), red.shift = bitShift(red.mask);
|
||||
green.depth = bitDepth(green.mask), green.shift = bitShift(green.mask);
|
||||
blue.depth = bitDepth(blue.mask), blue.shift = bitShift(blue.mask);
|
||||
}
|
||||
|
||||
image::~image() {
|
||||
free();
|
||||
}
|
||||
|
||||
uint64_t image::read(const uint8_t *data) const {
|
||||
uint64_t result = 0;
|
||||
if(endian == 0) {
|
||||
for(signed n = stride - 1; n >= 0; n--) result = (result << 8) | data[n];
|
||||
} else {
|
||||
for(signed n = 0; n < stride; n++) result = (result << 8) | data[n];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void image::write(uint8_t *data, uint64_t value) const {
|
||||
if(endian == 0) {
|
||||
for(signed n = 0; n < stride; n++) { data[n] = value; value >>= 8; }
|
||||
} else {
|
||||
for(signed n = stride - 1; n >= 0; n--) { data[n] = value; value >>= 8; }
|
||||
}
|
||||
}
|
||||
|
||||
void image::free() {
|
||||
if(data) delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
void image::allocate(unsigned width, unsigned height) {
|
||||
free();
|
||||
data = new uint8_t[width * height * stride]();
|
||||
pitch = width * stride;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
bool image::load(const string &filename) {
|
||||
if(loadBMP(filename) == true) return true;
|
||||
if(loadPNG(filename) == true) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) {
|
||||
scaleX(outputWidth, op);
|
||||
scaleY(outputHeight, op);
|
||||
}
|
||||
|
||||
void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) {
|
||||
image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask);
|
||||
output.allocate(width, height);
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t *dp = output.data + output.pitch * y;
|
||||
uint8_t *sp = data + pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint64_t color = read(sp);
|
||||
sp += stride;
|
||||
|
||||
uint64_t a = (color & alpha.mask) >> alpha.shift;
|
||||
uint64_t r = (color & red.mask) >> red.shift;
|
||||
uint64_t g = (color & green.mask) >> green.shift;
|
||||
uint64_t b = (color & blue.mask) >> blue.shift;
|
||||
|
||||
a = normalize(a, alpha.depth, output.alpha.depth);
|
||||
r = normalize(r, red.depth, output.red.depth);
|
||||
g = normalize(g, green.depth, output.green.depth);
|
||||
b = normalize(b, blue.depth, output.blue.depth);
|
||||
|
||||
output.write(dp, (a << output.alpha.shift) + (r << output.red.shift) + (g << output.green.shift) + (b << output.blue.shift));
|
||||
dp += output.stride;
|
||||
}
|
||||
}
|
||||
|
||||
operator=(std::move(output));
|
||||
}
|
||||
|
||||
void image::alphaBlend(uint64_t alphaColor) {
|
||||
uint64_t alphaR = (alphaColor & red.mask) >> red.shift;
|
||||
uint64_t alphaG = (alphaColor & green.mask) >> green.shift;
|
||||
uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t *dp = data + pitch * y;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint64_t color = read(dp);
|
||||
|
||||
uint64_t colorA = (color & alpha.mask) >> alpha.shift;
|
||||
uint64_t colorR = (color & red.mask) >> red.shift;
|
||||
uint64_t colorG = (color & green.mask) >> green.shift;
|
||||
uint64_t colorB = (color & blue.mask) >> blue.shift;
|
||||
double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1);
|
||||
|
||||
colorA = (1 << alpha.depth) - 1;
|
||||
colorR = (colorR * alphaScale) + (alphaR * (1.0 - alphaScale));
|
||||
colorG = (colorG * alphaScale) + (alphaG * (1.0 - alphaScale));
|
||||
colorB = (colorB * alphaScale) + (alphaB * (1.0 - alphaScale));
|
||||
|
||||
write(dp, (colorA << alpha.shift) + (colorR << red.shift) + (colorG << green.shift) + (colorB << blue.shift));
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//protected
|
||||
|
||||
uint64_t image::interpolate(double mu, const uint64_t *s, double (*op)(double, double, double, double, double)) {
|
||||
uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift,
|
||||
ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift;
|
||||
uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift,
|
||||
bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift;
|
||||
uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift,
|
||||
cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift;
|
||||
uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift,
|
||||
dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift;
|
||||
|
||||
int64_t A = op(mu, aa, ba, ca, da);
|
||||
int64_t R = op(mu, ar, br, cr, dr);
|
||||
int64_t G = op(mu, ag, bg, cg, dg);
|
||||
int64_t B = op(mu, ab, bb, cb, db);
|
||||
|
||||
A = max(0, min(A, (1 << alpha.depth) - 1));
|
||||
R = max(0, min(R, (1 << red.depth) - 1));
|
||||
G = max(0, min(G, (1 << green.depth) - 1));
|
||||
B = max(0, min(B, (1 << blue.depth) - 1));
|
||||
|
||||
return (A << alpha.shift) + (R << red.shift) + (G << green.shift) + (B << blue.shift);
|
||||
}
|
||||
|
||||
void image::scaleX(unsigned outputWidth, interpolation op) {
|
||||
uint8_t *outputData = new uint8_t[outputWidth * height * stride];
|
||||
unsigned outputPitch = outputWidth * stride;
|
||||
double step = (double)width / (double)outputWidth;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
uint8_t *dp = outputData + outputPitch * y;
|
||||
uint8_t *sp = data + pitch * y;
|
||||
|
||||
double fraction = 0.0;
|
||||
uint64_t s[4] = { read(sp), read(sp), read(sp), read(sp) };
|
||||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
if(sp >= data + pitch * height) break;
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
s[2] = s[3];
|
||||
s[3] = read(sp);
|
||||
|
||||
while(fraction <= 1.0) {
|
||||
if(dp >= outputData + outputPitch * height) break;
|
||||
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
|
||||
dp += stride;
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
sp += stride;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
width = outputWidth;
|
||||
pitch = width * stride;
|
||||
}
|
||||
|
||||
void image::scaleY(unsigned outputHeight, interpolation op) {
|
||||
uint8_t *outputData = new uint8_t[width * outputHeight * stride];
|
||||
double step = (double)height / (double)outputHeight;
|
||||
|
||||
#pragma omp parallel for
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint8_t *dp = outputData + stride * x;
|
||||
uint8_t *sp = data + stride * x;
|
||||
|
||||
double fraction = 0.0;
|
||||
uint64_t s[4] = { read(sp), read(sp), read(sp), read(sp) };
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
if(sp >= data + pitch * height) break;
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
s[2] = s[3];
|
||||
s[3] = read(sp);
|
||||
|
||||
while(fraction <= 1.0) {
|
||||
if(dp >= outputData + pitch * outputHeight) break;
|
||||
write(dp, interpolate(fraction, (const uint64_t*)&s, op));
|
||||
dp += pitch;
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
sp += pitch;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
free();
|
||||
data = outputData;
|
||||
height = outputHeight;
|
||||
}
|
||||
|
||||
bool image::loadBMP(const string &filename) {
|
||||
uint32_t *outputData;
|
||||
unsigned outputWidth, outputHeight;
|
||||
if(bmp::read(filename, outputData, outputWidth, outputHeight) == false) return false;
|
||||
|
||||
allocate(outputWidth, outputHeight);
|
||||
const uint32_t *sp = outputData;
|
||||
uint8_t *dp = data;
|
||||
|
||||
for(unsigned y = 0; y < outputHeight; y++) {
|
||||
for(unsigned x = 0; x < outputWidth; x++) {
|
||||
uint32_t color = *sp++;
|
||||
uint64_t a = normalize((uint8_t)(color >> 24), 8, alpha.depth);
|
||||
uint64_t r = normalize((uint8_t)(color >> 16), 8, red.depth);
|
||||
uint64_t g = normalize((uint8_t)(color >> 8), 8, green.depth);
|
||||
uint64_t b = normalize((uint8_t)(color >> 0), 8, blue.depth);
|
||||
write(dp, (a << alpha.shift) + (r << red.shift) + (g << green.shift) + (b << blue.shift));
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] outputData;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool image::loadPNG(const string &filename) {
|
||||
png source;
|
||||
if(source.decode(filename) == false) return false;
|
||||
|
||||
allocate(source.info.width, source.info.height);
|
||||
const uint8_t *sp = source.data;
|
||||
uint8_t *dp = data;
|
||||
|
||||
auto decode = [&]() -> uint64_t {
|
||||
uint64_t p, r, g, b, a;
|
||||
|
||||
switch(source.info.colorType) {
|
||||
case 0: //L
|
||||
r = g = b = source.readbits(sp);
|
||||
a = (1 << source.info.bitDepth) - 1;
|
||||
break;
|
||||
case 2: //R,G,B
|
||||
r = source.readbits(sp);
|
||||
g = source.readbits(sp);
|
||||
b = source.readbits(sp);
|
||||
a = (1 << source.info.bitDepth) - 1;
|
||||
break;
|
||||
case 3: //P
|
||||
p = source.readbits(sp);
|
||||
r = source.info.palette[p][0];
|
||||
g = source.info.palette[p][1];
|
||||
b = source.info.palette[p][2];
|
||||
a = (1 << source.info.bitDepth) - 1;
|
||||
break;
|
||||
case 4: //L,A
|
||||
r = g = b = source.readbits(sp);
|
||||
a = source.readbits(sp);
|
||||
break;
|
||||
case 6: //R,G,B,A
|
||||
r = source.readbits(sp);
|
||||
g = source.readbits(sp);
|
||||
b = source.readbits(sp);
|
||||
a = source.readbits(sp);
|
||||
break;
|
||||
}
|
||||
|
||||
a = normalize(a, source.info.bitDepth, alpha.depth);
|
||||
r = normalize(r, source.info.bitDepth, red.depth);
|
||||
g = normalize(g, source.info.bitDepth, green.depth);
|
||||
b = normalize(b, source.info.bitDepth, blue.depth);
|
||||
|
||||
return (a << alpha.shift) + (r << red.shift) + (g << green.shift) + (b << blue.shift);
|
||||
};
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
write(dp, decode());
|
||||
dp += stride;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef NALL_INTERPOLATION_HPP
|
||||
#define NALL_INTERPOLATION_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct Interpolation {
|
||||
static inline double Nearest(double mu, double a, double b, double c, double d) {
|
||||
return (mu < 0.5 ? c : d);
|
||||
}
|
||||
|
||||
static inline double Sublinear(double mu, double a, double b, double c, double d) {
|
||||
mu = ((mu - 0.5) * 2.0) + 0.5;
|
||||
if(mu < 0) mu = 0;
|
||||
if(mu > 1) mu = 1;
|
||||
return c * (1.0 - mu) + d * mu;
|
||||
}
|
||||
|
||||
static inline double Linear(double mu, double a, double b, double c, double d) {
|
||||
return c * (1.0 - mu) + d * mu;
|
||||
}
|
||||
|
||||
static inline double Cosine(double mu, double a, double b, double c, double d) {
|
||||
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||
return c * (1.0 - mu) + d * mu;
|
||||
}
|
||||
|
||||
static inline double Cubic(double mu, double a, double b, double c, double d) {
|
||||
double A = d - c - a + b;
|
||||
double B = a - b - A;
|
||||
double C = c - a;
|
||||
double D = b;
|
||||
return A * (mu * mu * mu) + B * (mu * mu) + C * mu + D;
|
||||
}
|
||||
|
||||
static inline double Hermite(double mu1, double a, double b, double c, double d) {
|
||||
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||
double mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0;
|
||||
m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,9 +10,12 @@
|
|||
namespace nall {
|
||||
|
||||
struct png {
|
||||
uint32_t *data;
|
||||
unsigned size;
|
||||
|
||||
//colorType:
|
||||
//0 = L
|
||||
//2 = R,G,B
|
||||
//3 = P
|
||||
//4 = L,A
|
||||
//6 = R,G,B,A
|
||||
struct Info {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
@ -28,13 +31,14 @@ struct png {
|
|||
uint8_t palette[256][3];
|
||||
} info;
|
||||
|
||||
uint8_t *rawData;
|
||||
unsigned rawSize;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
||||
inline bool decode(const string &filename);
|
||||
inline bool decode(const uint8_t *sourceData, unsigned sourceSize);
|
||||
inline void transform();
|
||||
inline void alphaTransform(uint32_t rgb = 0xffffff);
|
||||
inline unsigned readbits(const uint8_t *&data);
|
||||
unsigned bitpos;
|
||||
|
||||
inline png();
|
||||
inline ~png();
|
||||
|
||||
|
@ -46,16 +50,11 @@ protected:
|
|||
IEND = 0x49454e44,
|
||||
};
|
||||
|
||||
unsigned bitpos;
|
||||
|
||||
inline unsigned interlace(unsigned pass, unsigned index);
|
||||
inline unsigned inflateSize();
|
||||
inline bool deinterlace(const uint8_t *&inputData, unsigned pass);
|
||||
inline bool filter(uint8_t *outputData, const uint8_t *inputData, unsigned width, unsigned height);
|
||||
inline unsigned read(const uint8_t *data, unsigned length);
|
||||
inline unsigned decode(const uint8_t *&data);
|
||||
inline unsigned readbits(const uint8_t *&data);
|
||||
inline unsigned scale(unsigned n);
|
||||
};
|
||||
|
||||
bool png::decode(const string &filename) {
|
||||
|
@ -146,14 +145,14 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
|||
return false;
|
||||
}
|
||||
|
||||
rawSize = info.width * info.height * info.bytesPerPixel;
|
||||
rawData = new uint8_t[rawSize];
|
||||
size = info.width * info.height * info.bytesPerPixel;
|
||||
data = new uint8_t[size];
|
||||
|
||||
if(info.interlaceMethod == 0) {
|
||||
if(filter(rawData, interlacedData, info.width, info.height) == false) {
|
||||
if(filter(data, interlacedData, info.width, info.height) == false) {
|
||||
delete[] interlacedData;
|
||||
delete[] rawData;
|
||||
rawData = 0;
|
||||
delete[] data;
|
||||
data = 0;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -161,8 +160,8 @@ bool png::decode(const uint8_t *sourceData, unsigned sourceSize) {
|
|||
for(unsigned pass = 0; pass < 7; pass++) {
|
||||
if(deinterlace(passData, pass) == false) {
|
||||
delete[] interlacedData;
|
||||
delete[] rawData;
|
||||
rawData = 0;
|
||||
delete[] data;
|
||||
data = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +215,7 @@ bool png::deinterlace(const uint8_t *&inputData, unsigned pass) {
|
|||
|
||||
const uint8_t *rd = outputData;
|
||||
for(unsigned y = yo; y < info.height; y += yd) {
|
||||
uint8_t *wr = rawData + y * info.pitch;
|
||||
uint8_t *wr = data + y * info.pitch;
|
||||
for(unsigned x = xo; x < info.width; x += xd) {
|
||||
for(unsigned b = 0; b < info.bytesPerPixel; b++) {
|
||||
wr[x * info.bytesPerPixel + b] = *rd++;
|
||||
|
@ -298,42 +297,6 @@ unsigned png::read(const uint8_t *data, unsigned length) {
|
|||
return result;
|
||||
}
|
||||
|
||||
unsigned png::decode(const uint8_t *&data) {
|
||||
unsigned p, r, g, b, a;
|
||||
|
||||
switch(info.colorType) {
|
||||
case 0: //L
|
||||
r = g = b = scale(readbits(data));
|
||||
a = 0xff;
|
||||
break;
|
||||
case 2: //R,G,B
|
||||
r = scale(readbits(data));
|
||||
g = scale(readbits(data));
|
||||
b = scale(readbits(data));
|
||||
a = 0xff;
|
||||
break;
|
||||
case 3: //P
|
||||
p = readbits(data);
|
||||
r = info.palette[p][0];
|
||||
g = info.palette[p][1];
|
||||
b = info.palette[p][2];
|
||||
a = 0xff;
|
||||
break;
|
||||
case 4: //L,A
|
||||
r = g = b = scale(readbits(data));
|
||||
a = scale(readbits(data));
|
||||
break;
|
||||
case 6: //R,G,B,A
|
||||
r = scale(readbits(data));
|
||||
g = scale(readbits(data));
|
||||
b = scale(readbits(data));
|
||||
a = scale(readbits(data));
|
||||
break;
|
||||
}
|
||||
|
||||
return (a << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
|
||||
unsigned png::readbits(const uint8_t *&data) {
|
||||
unsigned result = 0;
|
||||
switch(info.bitDepth) {
|
||||
|
@ -363,62 +326,12 @@ unsigned png::readbits(const uint8_t *&data) {
|
|||
return result;
|
||||
}
|
||||
|
||||
unsigned png::scale(unsigned n) {
|
||||
switch(info.bitDepth) {
|
||||
case 1: return n ? 0xff : 0x00;
|
||||
case 2: return n * 0x55;
|
||||
case 4: return n * 0x11;
|
||||
case 8: return n;
|
||||
case 16: return n >> 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void png::transform() {
|
||||
if(data) delete[] data;
|
||||
data = new uint32_t[info.width * info.height];
|
||||
|
||||
png::png() : data(nullptr) {
|
||||
bitpos = 0;
|
||||
const uint8_t *rd = rawData;
|
||||
for(unsigned y = 0; y < info.height; y++) {
|
||||
uint32_t *wr = data + y * info.width;
|
||||
for(unsigned x = 0; x < info.width; x++) {
|
||||
wr[x] = decode(rd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void png::alphaTransform(uint32_t rgb) {
|
||||
transform();
|
||||
|
||||
uint8_t ir = rgb >> 16;
|
||||
uint8_t ig = rgb >> 8;
|
||||
uint8_t ib = rgb >> 0;
|
||||
|
||||
uint32_t *p = data;
|
||||
for(unsigned y = 0; y < info.height; y++) {
|
||||
for(unsigned x = 0; x < info.width; x++) {
|
||||
uint32_t pixel = *p;
|
||||
uint8_t a = pixel >> 24;
|
||||
uint8_t r = pixel >> 16;
|
||||
uint8_t g = pixel >> 8;
|
||||
uint8_t b = pixel >> 0;
|
||||
|
||||
r = (r * a) + (ir * (255 - a)) >> 8;
|
||||
g = (g * a) + (ig * (255 - a)) >> 8;
|
||||
b = (b * a) + (ib * (255 - a)) >> 8;
|
||||
|
||||
*p++ = (255 << 24) | (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
png::png() : data(nullptr), rawData(nullptr) {
|
||||
}
|
||||
|
||||
png::~png() {
|
||||
if(data) delete[] data;
|
||||
if(rawData) delete[] rawData;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <nall/string/cstring.hpp>
|
||||
#include <nall/string/filename.hpp>
|
||||
#include <nall/string/math.hpp>
|
||||
#include <nall/string/math-fixed-point.hpp>
|
||||
#include <nall/string/math-floating-point.hpp>
|
||||
#include <nall/string/platform.hpp>
|
||||
#include <nall/string/strl.hpp>
|
||||
#include <nall/string/strpos.hpp>
|
||||
|
|
|
@ -183,8 +183,8 @@ namespace nall {
|
|||
template<unsigned length = 0, char padding = ' '> inline string ldecimal(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string hex(uintmax_t value);
|
||||
template<unsigned length = 0, char padding = '0'> inline string binary(uintmax_t value);
|
||||
inline unsigned fp(char *str, double value);
|
||||
inline string fp(double value);
|
||||
inline unsigned fp(char *str, long double value);
|
||||
inline string fp(long double value);
|
||||
|
||||
//variadic.hpp
|
||||
template<typename... Args> inline void print(Args&&... args);
|
||||
|
|
|
@ -101,19 +101,19 @@ template<unsigned bits> struct stringify<uint_t<bits>> {
|
|||
template<> struct stringify<float> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(float value) { snprintf(data, 255, "%f", value); }
|
||||
stringify(float value) { fp(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<double> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(double value) { snprintf(data, 255, "%f", value); }
|
||||
stringify(double value) { fp(data, value); }
|
||||
};
|
||||
|
||||
template<> struct stringify<long double> {
|
||||
char data[256];
|
||||
operator const char*() const { return data; }
|
||||
stringify(long double value) { snprintf(data, 255, "%Lf", value); }
|
||||
stringify(long double value) { fp(data, value); }
|
||||
};
|
||||
|
||||
// strings
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace fixedpoint {
|
||||
|
||||
static nall::function<intmax_t (const char *&)> eval_fallback;
|
||||
|
||||
static intmax_t eval_integer(const char *& s) {
|
||||
if(!*s) throw "unrecognized integer";
|
||||
intmax_t value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
//hexadecimal
|
||||
if(x == '0' && (y == 'X' || y == 'x')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
|
||||
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
|
||||
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//binary
|
||||
if(x == '0' && (y == 'B' || y == 'b')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//octal (or decimal '0')
|
||||
if(x == '0') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//decimal
|
||||
if(x >= '0' && x <= '9') {
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//char
|
||||
if(x == '\'' && y != '\'') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
value = value * 256 + *s++;
|
||||
if(*s == '\'') { s += 1; return value; }
|
||||
if(!*s) throw "mismatched char";
|
||||
}
|
||||
}
|
||||
|
||||
throw "unrecognized integer";
|
||||
}
|
||||
|
||||
static intmax_t eval(const char *&s, int depth = 0) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) throw "unrecognized token";
|
||||
intmax_t value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
if(*s == '(') {
|
||||
value = eval(++s, 1);
|
||||
if(*s++ != ')') throw "mismatched group";
|
||||
}
|
||||
|
||||
else if(x == '!') value = !eval(++s, 13);
|
||||
else if(x == '~') value = ~eval(++s, 13);
|
||||
else if(x == '+') value = +eval(++s, 13);
|
||||
else if(x == '-') value = -eval(++s, 13);
|
||||
|
||||
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
|
||||
|
||||
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
|
||||
|
||||
else throw "unrecognized token";
|
||||
|
||||
while(true) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) break;
|
||||
x = *s, y = *(s + 1);
|
||||
|
||||
if(depth >= 13) break;
|
||||
if(x == '*') { value *= eval(++s, 13); continue; }
|
||||
if(x == '/') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value /= result; continue; }
|
||||
if(x == '%') { intmax_t result = eval(++s, 13); if(result == 0) throw "division by zero"; value %= result; continue; }
|
||||
|
||||
if(depth >= 12) break;
|
||||
if(x == '+') { value += eval(++s, 12); continue; }
|
||||
if(x == '-') { value -= eval(++s, 12); continue; }
|
||||
|
||||
if(depth >= 11) break;
|
||||
if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; }
|
||||
if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; }
|
||||
|
||||
if(depth >= 10) break;
|
||||
if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; }
|
||||
if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; }
|
||||
if(x == '<') { value = value < eval(++s, 10); continue; }
|
||||
if(x == '>') { value = value > eval(++s, 10); continue; }
|
||||
|
||||
if(depth >= 9) break;
|
||||
if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; }
|
||||
if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; }
|
||||
|
||||
if(depth >= 8) break;
|
||||
if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; }
|
||||
|
||||
if(depth >= 7) break;
|
||||
if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; }
|
||||
|
||||
if(depth >= 6) break;
|
||||
if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; }
|
||||
|
||||
if(depth >= 5) break;
|
||||
if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; }
|
||||
|
||||
if(depth >= 4) break;
|
||||
if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; }
|
||||
|
||||
if(depth >= 3) break;
|
||||
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
||||
|
||||
if(x == '?') {
|
||||
intmax_t lhs = eval(++s, 2);
|
||||
if(*s != ':') throw "mismatched ternary";
|
||||
intmax_t rhs = eval(++s, 2);
|
||||
value = value ? lhs : rhs;
|
||||
continue;
|
||||
}
|
||||
if(depth >= 2) break;
|
||||
|
||||
if(depth > 0 && x == ')') break;
|
||||
|
||||
throw "unrecognized token";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool eval(const char *s, intmax_t &result) {
|
||||
try {
|
||||
result = eval(s);
|
||||
return true;
|
||||
} catch(const char*) {
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,149 @@
|
|||
#ifdef NALL_STRING_INTERNAL_HPP
|
||||
|
||||
namespace floatingpoint {
|
||||
|
||||
static nall::function<double (const char *&)> eval_fallback;
|
||||
|
||||
static double eval_integer(const char *&s) {
|
||||
if(!*s) throw "unrecognized integer";
|
||||
intmax_t value = 0, radix = 0, x = *s, y = *(s + 1);
|
||||
|
||||
//hexadecimal
|
||||
if(x == '0' && (y == 'X' || y == 'x')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; }
|
||||
if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; }
|
||||
if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//binary
|
||||
if(x == '0' && (y == 'B' || y == 'b')) {
|
||||
s += 2;
|
||||
while(true) {
|
||||
if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//octal (or decimal '0')
|
||||
if(x == '0' && y != '.') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; }
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
//decimal
|
||||
if(x >= '0' && x <= '9') {
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; }
|
||||
if(*s == '.') { s++; break; }
|
||||
return value;
|
||||
}
|
||||
//floating-point
|
||||
while(true) {
|
||||
if(*s >= '0' && *s <= '9') { radix = radix * 10 + (*s++ - '0'); continue; }
|
||||
return atof(nall::string{ nall::decimal(value), ".", nall::decimal(radix) });
|
||||
}
|
||||
}
|
||||
|
||||
//char
|
||||
if(x == '\'' && y != '\'') {
|
||||
s += 1;
|
||||
while(true) {
|
||||
value = value * 256 + *s++;
|
||||
if(*s == '\'') { s += 1; return value; }
|
||||
if(!*s) throw "mismatched char";
|
||||
}
|
||||
}
|
||||
|
||||
throw "unrecognized integer";
|
||||
}
|
||||
|
||||
static double eval(const char *&s, int depth = 0) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) throw "unrecognized token";
|
||||
double value = 0, x = *s, y = *(s + 1);
|
||||
|
||||
if(*s == '(') {
|
||||
value = eval(++s, 1);
|
||||
if(*s++ != ')') throw "mismatched group";
|
||||
}
|
||||
|
||||
else if(x == '!') value = !eval(++s, 9);
|
||||
else if(x == '+') value = +eval(++s, 9);
|
||||
else if(x == '-') value = -eval(++s, 9);
|
||||
|
||||
else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s);
|
||||
|
||||
else if(eval_fallback) value = eval_fallback(s); //optional user-defined syntax parsing
|
||||
|
||||
else throw "unrecognized token";
|
||||
|
||||
while(true) {
|
||||
while(*s == ' ' || *s == '\t') s++; //trim whitespace
|
||||
if(!*s) break;
|
||||
x = *s, y = *(s + 1);
|
||||
|
||||
if(depth >= 9) break;
|
||||
if(x == '*') { value *= eval(++s, 9); continue; }
|
||||
if(x == '/') { double result = eval(++s, 9); if(result == 0.0) throw "division by zero"; value /= result; continue; }
|
||||
|
||||
if(depth >= 8) break;
|
||||
if(x == '+') { value += eval(++s, 8); continue; }
|
||||
if(x == '-') { value -= eval(++s, 8); continue; }
|
||||
|
||||
if(depth >= 7) break;
|
||||
if(x == '<' && y == '=') { value = value <= eval(++++s, 7); continue; }
|
||||
if(x == '>' && y == '=') { value = value >= eval(++++s, 7); continue; }
|
||||
if(x == '<') { value = value < eval(++s, 7); continue; }
|
||||
if(x == '>') { value = value > eval(++s, 7); continue; }
|
||||
|
||||
if(depth >= 6) break;
|
||||
if(x == '=' && y == '=') { value = value == eval(++++s, 6); continue; }
|
||||
if(x == '!' && y == '=') { value = value != eval(++++s, 6); continue; }
|
||||
|
||||
if(depth >= 5) break;
|
||||
if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; }
|
||||
|
||||
if(depth >= 4) break;
|
||||
if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; }
|
||||
|
||||
if(depth >= 3) break;
|
||||
if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; }
|
||||
|
||||
if(x == '?') {
|
||||
double lhs = eval(++s, 2);
|
||||
if(*s != ':') throw "mismatched ternary";
|
||||
double rhs = eval(++s, 2);
|
||||
value = value ? lhs : rhs;
|
||||
continue;
|
||||
}
|
||||
if(depth >= 2) break;
|
||||
|
||||
if(depth > 0 && x == ')') break;
|
||||
|
||||
throw "unrecognized token";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool eval(const char *s, double &result) {
|
||||
try {
|
||||
result = eval(s);
|
||||
return true;
|
||||
} catch(const char*e) {
|
||||
printf("%s\n", e);
|
||||
result = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -253,9 +253,14 @@ template<unsigned length_, char padding> string binary(uintmax_t value) {
|
|||
//using sprintf is certainly not the most ideal method to convert
|
||||
//a double to a string ... but attempting to parse a double by
|
||||
//hand, digit-by-digit, results in subtle rounding errors.
|
||||
unsigned fp(char *str, double value) {
|
||||
unsigned fp(char *str, long double value) {
|
||||
char buffer[256];
|
||||
sprintf(buffer, "%f", value);
|
||||
#ifdef _WIN32
|
||||
//Windows C-runtime does not support long double via sprintf()
|
||||
sprintf(buffer, "%f", (double)value);
|
||||
#else
|
||||
sprintf(buffer, "%Lf", value);
|
||||
#endif
|
||||
|
||||
//remove excess 0's in fraction (2.500000 -> 2.5)
|
||||
for(char *p = buffer; *p; p++) {
|
||||
|
@ -274,7 +279,7 @@ unsigned fp(char *str, double value) {
|
|||
return length + 1;
|
||||
}
|
||||
|
||||
string fp(double value) {
|
||||
string fp(long double value) {
|
||||
string temp;
|
||||
temp.reserve(fp(0, value));
|
||||
fp(temp(), value);
|
||||
|
|
|
@ -42,7 +42,6 @@ void Input::connect(bool port, Device device) {
|
|||
}
|
||||
|
||||
void Input::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Input::reset() {
|
||||
|
|
|
@ -16,7 +16,6 @@ void Scheduler::exit(ExitReason reason) {
|
|||
}
|
||||
|
||||
void Scheduler::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Scheduler::reset() {
|
||||
|
|
|
@ -27,85 +27,6 @@ Font::Font(const string &description):
|
|||
description(description) {
|
||||
}
|
||||
|
||||
//Image
|
||||
//=====
|
||||
|
||||
bool Image::load(const string &filename, const Color &alpha) {
|
||||
if(data) { delete[] data; data = nullptr; }
|
||||
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::read) == false) return false;
|
||||
uint8_t d0 = fp.read();
|
||||
uint8_t d1 = fp.read();
|
||||
uint8_t d2 = fp.read();
|
||||
uint8_t d3 = fp.read();
|
||||
fp.close();
|
||||
|
||||
if(d0 == 'B' && d1 == 'M') {
|
||||
bmp::read(filename, data, width, height);
|
||||
}
|
||||
|
||||
if(d0 == 0x89 && d1 == 'P' && d2 == 'N' && d3 == 'G') {
|
||||
png image;
|
||||
if(image.decode(filename)) {
|
||||
image.alphaTransform((alpha.red << 16) + (alpha.green << 8) + (alpha.blue << 0));
|
||||
width = image.info.width, height = image.info.height;
|
||||
data = new uint32_t[width * height];
|
||||
memcpy(data, image.data, width * height * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Image::load(const uint32_t *data, const Size &size) {
|
||||
if(data) { delete[] data; data = nullptr; }
|
||||
width = size.width, height = size.height;
|
||||
this->data = new uint32_t[width * height];
|
||||
memcpy(this->data, data, width * height * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
Image& Image::operator=(const Image &source) {
|
||||
if(this == &source) return *this;
|
||||
if(data) { delete[] data; data = nullptr; }
|
||||
if(source.data == nullptr) return *this;
|
||||
width = source.width, height = source.height;
|
||||
data = new uint32_t[width * height];
|
||||
memcpy(data, source.data, width * height * sizeof(uint32_t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Image& Image::operator=(Image &&source) {
|
||||
if(this == &source) return *this;
|
||||
if(data) { delete[] data; data = nullptr; }
|
||||
data = source.data, width = source.width, height = source.height;
|
||||
source.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Image::Image() : data(nullptr) {
|
||||
}
|
||||
|
||||
Image::Image(const string &filename, const Color &alpha) : data(nullptr) {
|
||||
load(filename, alpha);
|
||||
}
|
||||
|
||||
Image::Image(const uint32_t *data, const Size &size) {
|
||||
load(data, size);
|
||||
}
|
||||
|
||||
Image::Image(const Image &source) : data(nullptr) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
Image::Image(Image &&source) : data(nullptr) {
|
||||
operator=(std::forward<Image>(source));
|
||||
}
|
||||
|
||||
Image::~Image() {
|
||||
if(data) delete[] data;
|
||||
}
|
||||
|
||||
//Object
|
||||
//======
|
||||
|
||||
|
@ -739,7 +660,7 @@ uint32_t* Canvas::data() {
|
|||
return state.data;
|
||||
}
|
||||
|
||||
bool Canvas::setImage(const Image &image) {
|
||||
bool Canvas::setImage(const nall::image &image) {
|
||||
if(image.data == nullptr || image.width == 0 || image.height == 0) return false;
|
||||
state.width = image.width;
|
||||
state.height = image.height;
|
||||
|
|
|
@ -72,21 +72,6 @@ struct Font {
|
|||
Font(const nall::string &description = "");
|
||||
};
|
||||
|
||||
struct Image {
|
||||
uint32_t *data;
|
||||
unsigned width, height;
|
||||
bool load(const nall::string &filename, const Color &alpha = Color{255, 255, 255});
|
||||
void load(const uint32_t *data, const Size &size);
|
||||
Image& operator=(const Image &source);
|
||||
Image& operator=(Image &&source);
|
||||
Image();
|
||||
Image(const nall::string &filename, const Color &alpha = Color{255, 255, 255});
|
||||
Image(const uint32_t *data, const Size &size);
|
||||
Image(const Image &source);
|
||||
Image(Image &&source);
|
||||
~Image();
|
||||
};
|
||||
|
||||
struct Object {
|
||||
Object(pObject &p);
|
||||
Object& operator=(const Object&) = delete;
|
||||
|
@ -330,7 +315,7 @@ struct Button : private nall::base_from_member<pButton&>, Widget {
|
|||
|
||||
struct Canvas : private nall::base_from_member<pCanvas&>, Widget {
|
||||
uint32_t* data();
|
||||
bool setImage(const Image &image);
|
||||
bool setImage(const nall::image &image);
|
||||
void setSize(const Size &size);
|
||||
Size size();
|
||||
void update();
|
||||
|
|
|
@ -130,17 +130,17 @@ bool pHexEdit::keyPress(unsigned scancode) {
|
|||
unsigned cursorY = position / lineWidth;
|
||||
unsigned cursorX = position % lineWidth;
|
||||
|
||||
if(scancode == GDK_KEY_Home) {
|
||||
if(scancode == GDK_Home) {
|
||||
setCursorPosition(cursorY * lineWidth + 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_End) {
|
||||
if(scancode == GDK_End) {
|
||||
setCursorPosition(cursorY * lineWidth + 10 + (hexEdit.state.columns * 3 - 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Up) {
|
||||
if(scancode == GDK_Up) {
|
||||
if(cursorY != 0) return false;
|
||||
|
||||
signed newOffset = hexEdit.state.offset - hexEdit.state.columns;
|
||||
|
@ -151,7 +151,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Down) {
|
||||
if(scancode == GDK_Down) {
|
||||
if(cursorY != hexEdit.state.rows - 1) return false;
|
||||
|
||||
signed newOffset = hexEdit.state.offset + hexEdit.state.columns;
|
||||
|
@ -162,7 +162,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Page_Up) {
|
||||
if(scancode == GDK_Page_Up) {
|
||||
signed newOffset = hexEdit.state.offset - hexEdit.state.columns * hexEdit.state.rows;
|
||||
if(newOffset >= 0) {
|
||||
hexEdit.setOffset(newOffset);
|
||||
|
@ -173,7 +173,7 @@ bool pHexEdit::keyPress(unsigned scancode) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_Page_Down) {
|
||||
if(scancode == GDK_Page_Down) {
|
||||
signed newOffset = hexEdit.state.offset + hexEdit.state.columns * hexEdit.state.rows;
|
||||
for(unsigned n = 0; n < hexEdit.state.rows; n++) {
|
||||
if(newOffset + hexEdit.state.columns * hexEdit.state.rows - (hexEdit.state.columns - 1) <= hexEdit.state.length) {
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
#define PHOENIX_HPP
|
||||
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bmp.hpp>
|
||||
#include <nall/config.hpp>
|
||||
#include <nall/function.hpp>
|
||||
#include <nall/png.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/reference_array.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'platform.moc.hpp'
|
||||
**
|
||||
** Created: Wed Nov 9 02:07:41 2011
|
||||
** by: The Qt Meta Object Compiler version 62 (Qt 4.7.0)
|
||||
** Created: Tue Nov 29 20:27:15 2011
|
||||
** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
*****************************************************************************/
|
||||
|
@ -10,7 +10,7 @@
|
|||
#if !defined(Q_MOC_OUTPUT_REVISION)
|
||||
#error "The header file 'platform.moc.hpp' doesn't include <QObject>."
|
||||
#elif Q_MOC_OUTPUT_REVISION != 62
|
||||
#error "This file was generated using the moc from 4.7.0. It"
|
||||
#error "This file was generated using the moc from 4.6.3. It"
|
||||
#error "cannot be used with the include files from this version of Qt."
|
||||
#error "(The moc has changed too much.)"
|
||||
#endif
|
||||
|
@ -19,7 +19,7 @@ QT_BEGIN_MOC_NAMESPACE
|
|||
static const uint qt_meta_data_pTimer[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -80,7 +80,7 @@ int pTimer::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pWindow[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
0, 0, // methods
|
||||
|
@ -131,7 +131,7 @@ int pWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pItem[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -192,7 +192,7 @@ int pItem::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pCheckItem[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -253,7 +253,7 @@ int pCheckItem::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pRadioItem[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -314,7 +314,7 @@ int pRadioItem::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pButton[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -375,7 +375,7 @@ int pButton::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pCanvas[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
0, 0, // methods
|
||||
|
@ -426,7 +426,7 @@ int pCanvas::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pCheckBox[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -487,7 +487,7 @@ int pCheckBox::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pComboBox[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -548,7 +548,7 @@ int pComboBox::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pHexEdit[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -609,7 +609,7 @@ int pHexEdit::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pHorizontalScrollBar[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -670,7 +670,7 @@ int pHorizontalScrollBar::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pHorizontalSlider[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -731,7 +731,7 @@ int pHorizontalSlider::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pLineEdit[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
2, 14, // methods
|
||||
|
@ -794,7 +794,7 @@ int pLineEdit::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pListView[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
3, 14, // methods
|
||||
|
@ -861,7 +861,7 @@ int pListView::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pRadioBox[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -922,7 +922,7 @@ int pRadioBox::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pTextEdit[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -983,7 +983,7 @@ int pTextEdit::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pVerticalScrollBar[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
@ -1044,7 +1044,7 @@ int pVerticalScrollBar::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|||
static const uint qt_meta_data_pVerticalSlider[] = {
|
||||
|
||||
// content:
|
||||
5, // revision
|
||||
4, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
1, 14, // methods
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
void pCanvas::setSize(const Size &size) {
|
||||
delete qtImage;
|
||||
qtImage = new QImage(size.width, size.height, QImage::Format_RGB32);
|
||||
qtImage = new QImage(size.width, size.height, QImage::Format_ARGB32);
|
||||
}
|
||||
|
||||
void pCanvas::update() {
|
||||
|
@ -10,7 +10,7 @@ void pCanvas::update() {
|
|||
|
||||
void pCanvas::constructor() {
|
||||
qtWidget = qtCanvas = new QtCanvas(*this);
|
||||
qtImage = new QImage(canvas.state.width, canvas.state.height, QImage::Format_RGB32);
|
||||
qtImage = new QImage(canvas.state.width, canvas.state.height, QImage::Format_ARGB32);
|
||||
memcpy(qtImage->bits(), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t));
|
||||
|
||||
pWidget::synchronizeState();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
class Audio {
|
||||
public:
|
||||
struct Audio {
|
||||
static const char *Handle;
|
||||
static const char *Synchronize;
|
||||
static const char *Frequency;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
class Input {
|
||||
public:
|
||||
struct Input {
|
||||
static const char *Handle;
|
||||
static const char *KeyboardSupport;
|
||||
static const char *MouseSupport;
|
||||
|
|
|
@ -14,6 +14,7 @@ InputInterface input;
|
|||
|
||||
const char *Video::Handle = "Handle";
|
||||
const char *Video::Synchronize = "Synchronize";
|
||||
const char *Video::Depth = "Depth";
|
||||
const char *Video::Filter = "Filter";
|
||||
const char *Video::Shader = "Shader";
|
||||
const char *Video::FragmentShader = "FragmentShader";
|
||||
|
@ -58,6 +59,10 @@ void VideoInterface::driver(const char *driver) {
|
|||
else if(!strcmp(driver, "OpenGL")) p = new VideoWGL();
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO_XSHM
|
||||
else if(!strcmp(driver, "XShm")) p = new VideoXShm();
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO_XV
|
||||
else if(!strcmp(driver, "X-Video")) p = new VideoXv();
|
||||
#endif
|
||||
|
@ -75,6 +80,8 @@ const char* VideoInterface::default_driver() {
|
|||
return "DirectDraw";
|
||||
#elif defined(VIDEO_GDI)
|
||||
return "GDI";
|
||||
#elif defined(VIDEO_XSHM)
|
||||
return "XShm";
|
||||
#elif defined(VIDEO_QTOPENGL)
|
||||
return "Qt-OpenGL";
|
||||
#elif defined(VIDEO_QTRASTER)
|
||||
|
@ -126,6 +133,10 @@ const char* VideoInterface::driver_list() {
|
|||
"X-Video;"
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_XSHM)
|
||||
"XShm;"
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_QTRASTER)
|
||||
"Qt-Raster;"
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
ruby
|
||||
version: 0.07 (2011-08-14)
|
||||
version: 0.08 (2011-11-25)
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
|
|
|
@ -82,6 +82,10 @@ using namespace nall;
|
|||
#include <ruby/video/wgl.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO_XSHM
|
||||
#include <ruby/video/xshm.cpp>
|
||||
#endif
|
||||
|
||||
#ifdef VIDEO_XV
|
||||
#include <ruby/video/xv.cpp>
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class Video {
|
||||
public:
|
||||
struct Video {
|
||||
static const char *Handle;
|
||||
static const char *Synchronize;
|
||||
static const char *Depth;
|
||||
static const char *Filter;
|
||||
static const char *Shader;
|
||||
static const char *FragmentShader;
|
||||
|
|
|
@ -27,11 +27,6 @@
|
|||
|
||||
namespace ruby {
|
||||
|
||||
//returns true once window is mapped (created and displayed onscreen)
|
||||
static Bool glx_wait_for_map_notify(Display *d, XEvent *e, char *arg) {
|
||||
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
|
||||
}
|
||||
|
||||
class pVideoGLX : public OpenGL {
|
||||
public:
|
||||
int (*glSwapInterval)(int);
|
||||
|
@ -52,15 +47,18 @@ public:
|
|||
struct {
|
||||
Window handle;
|
||||
bool synchronize;
|
||||
unsigned depth;
|
||||
unsigned filter;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
} settings;
|
||||
|
||||
bool cap(const string& name) {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Depth) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
if(name == Video::Shader) return true;
|
||||
if(name == Video::FragmentShader) return true;
|
||||
|
@ -71,6 +69,7 @@ public:
|
|||
any get(const string& name) {
|
||||
if(name == Video::Handle) return (uintptr_t)settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
if(name == Video::Depth) return settings.depth;
|
||||
if(name == Video::Filter) return settings.filter;
|
||||
return false;
|
||||
}
|
||||
|
@ -89,6 +88,19 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if(name == Video::Depth) {
|
||||
unsigned depth = any_cast<unsigned>(value);
|
||||
switch(depth) {
|
||||
case 15u: ibpp = 2; iformat = GL_UNSIGNED_SHORT_1_5_5_5_REV; break;
|
||||
case 16u: ibpp = 2; iformat = GL_UNSIGNED_SHORT_5_6_5_REV; break;
|
||||
case 24u: ibpp = 4; iformat = GL_UNSIGNED_INT_8_8_8_8_REV; break;
|
||||
case 30u: ibpp = 4; iformat = GL_UNSIGNED_INT_2_10_10_10_REV; break;
|
||||
default: return false;
|
||||
}
|
||||
settings.depth = depth;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Filter) {
|
||||
settings.filter = any_cast<unsigned>(value);
|
||||
return true;
|
||||
|
@ -159,8 +171,21 @@ public:
|
|||
|
||||
//let GLX determine the best Visual to use for GL output; provide a few hints
|
||||
//note: some video drivers will override double buffering attribute
|
||||
int attributelist[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
|
||||
XVisualInfo *vi = glXChooseVisual(display, screen, attributelist);
|
||||
int attributeList[] = {
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
GLX_RED_SIZE, (settings.depth / 3),
|
||||
GLX_GREEN_SIZE, (settings.depth / 3) + (settings.depth % 3),
|
||||
GLX_BLUE_SIZE, (settings.depth / 3),
|
||||
None,
|
||||
};
|
||||
|
||||
int fbCount;
|
||||
GLXFBConfig *fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount);
|
||||
if(fbCount == 0) return false;
|
||||
|
||||
XVisualInfo *vi = glXGetVisualFromFBConfig(display, fbConfig[0]);
|
||||
|
||||
//Window settings.handle has already been realized, most likely with DefaultVisual.
|
||||
//GLX requires that the GL output window has the same Visual as the GLX context.
|
||||
|
@ -170,16 +195,19 @@ public:
|
|||
XSetWindowAttributes attributes;
|
||||
attributes.colormap = colormap;
|
||||
attributes.border_pixel = 0;
|
||||
attributes.event_mask = StructureNotifyMask;
|
||||
xwindow = XCreateWindow(display, /* parent = */ settings.handle,
|
||||
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
||||
/* border_width = */ 0, vi->depth, InputOutput, vi->visual,
|
||||
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
||||
CWColormap | CWBorderPixel, &attributes);
|
||||
XSetWindowBackground(display, xwindow, /* color = */ 0);
|
||||
XMapWindow(display, xwindow);
|
||||
XEvent event;
|
||||
XFlush(display);
|
||||
|
||||
//window must be realized (appear onscreen) before we make the context current
|
||||
XIfEvent(display, &event, glx_wait_for_map_notify, (char*)xwindow);
|
||||
while(XPending(display)) {
|
||||
XEvent event;
|
||||
XNextEvent(display, &event);
|
||||
}
|
||||
|
||||
glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE);
|
||||
glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
|
||||
|
@ -224,6 +252,10 @@ public:
|
|||
pVideoGLX() : glSwapInterval(0) {
|
||||
settings.handle = 0;
|
||||
settings.synchronize = false;
|
||||
settings.depth = 24u;
|
||||
|
||||
iformat = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
ibpp = 4;
|
||||
xwindow = 0;
|
||||
colormap = 0;
|
||||
glxcontext = 0;
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
bool shader_support;
|
||||
|
||||
uint32_t *buffer;
|
||||
unsigned iwidth, iheight;
|
||||
unsigned iwidth, iheight, iformat, ibpp;
|
||||
|
||||
void resize(unsigned width, unsigned height) {
|
||||
if(gltexture == 0) glGenTextures(1, &gltexture);
|
||||
|
@ -46,18 +46,18 @@ public:
|
|||
glBindTexture(GL_TEXTURE_2D, gltexture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
/* mip-map level = */ 0, /* internal format = */ GL_RGBA,
|
||||
/* mip-map level = */ 0, /* internal format = */ GL_RGB10_A2,
|
||||
iwidth, iheight, /* border = */ 0, /* format = */ GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
iformat, buffer);
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch) {
|
||||
pitch = iwidth * sizeof(uint32_t);
|
||||
pitch = iwidth * ibpp;
|
||||
return data = buffer;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
memset(buffer, 0, iwidth * iheight * sizeof(uint32_t));
|
||||
memset(buffer, 0, iwidth * iheight * ibpp);
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glFlush();
|
||||
|
@ -96,7 +96,7 @@ public:
|
|||
glPixelStorei(GL_UNPACK_ROW_LENGTH, iwidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
/* mip-map level = */ 0, /* x = */ 0, /* y = */ 0,
|
||||
inwidth, inheight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
|
||||
inwidth, inheight, GL_BGRA, iformat, buffer);
|
||||
|
||||
//OpenGL projection sets 0,0 as *bottom-left* of screen.
|
||||
//therefore, below vertices flip image to support top-left source.
|
||||
|
|
|
@ -141,6 +141,8 @@ public:
|
|||
settings.synchronize = false;
|
||||
settings.filter = 0;
|
||||
|
||||
iformat = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
ibpp = 4;
|
||||
window = 0;
|
||||
wglcontext = 0;
|
||||
glwindow = 0;
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
#include <sys/shm.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
namespace ruby {
|
||||
|
||||
struct pVideoXShm {
|
||||
struct {
|
||||
Display *display;
|
||||
int screen;
|
||||
Visual *visual;
|
||||
int depth;
|
||||
Window window;
|
||||
|
||||
XShmSegmentInfo shmInfo;
|
||||
XImage *image;
|
||||
uint32_t *buffer;
|
||||
unsigned width, height;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
uintptr_t handle;
|
||||
|
||||
uint32_t *buffer;
|
||||
unsigned width, height;
|
||||
} settings;
|
||||
|
||||
bool cap(const string &name) {
|
||||
if(name == Video::Handle) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
any get(const string& name) {
|
||||
if(name == Video::Handle) return settings.handle;
|
||||
}
|
||||
|
||||
bool set(const string& name, const any& value) {
|
||||
if(name == Video::Handle) {
|
||||
settings.handle = any_cast<uintptr_t>(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lock(uint32_t *&data, unsigned &pitch, unsigned width, unsigned height) {
|
||||
if(settings.buffer == nullptr || settings.width != width || settings.height != height) {
|
||||
if(settings.buffer) delete[] settings.buffer;
|
||||
settings.width = width, settings.height = height;
|
||||
settings.buffer = new uint32_t[width * height]();
|
||||
}
|
||||
|
||||
data = settings.buffer;
|
||||
pitch = settings.width * sizeof(uint32_t);
|
||||
return true;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if(settings.buffer == nullptr) return;
|
||||
memset(settings.buffer, 0, settings.width * settings.height * sizeof(uint32_t));
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
if(settings.buffer == nullptr) return;
|
||||
size();
|
||||
|
||||
float xRatio = (float)settings.width / (float)device.width;
|
||||
float yRatio = (float)settings.height / (float)device.height;
|
||||
float yStep = 0;
|
||||
for(unsigned y = 0; y < device.height; y++) {
|
||||
uint32_t *sp = settings.buffer + (unsigned)yStep * settings.width;
|
||||
uint32_t *dp = device.buffer + y * device.width;
|
||||
yStep += yRatio;
|
||||
float xStep = 0;
|
||||
for(unsigned x = 0; x < device.width; x++) {
|
||||
uint32_t color = sp[(unsigned)xStep];
|
||||
xStep += xRatio;
|
||||
*dp++ = ((color >> 20) & 0x000003ff) | ((color) & 0x000ffc00) | ((color << 20) & 0x3ff00000);
|
||||
}
|
||||
}
|
||||
|
||||
GC gc = XCreateGC(device.display, device.window, 0, 0);
|
||||
XShmPutImage(
|
||||
device.display, device.window, gc, device.image,
|
||||
0, 0, 0, 0, device.width, device.height, False
|
||||
);
|
||||
XFreeGC(device.display, gc);
|
||||
XFlush(device.display);
|
||||
}
|
||||
|
||||
bool init() {
|
||||
device.display = XOpenDisplay(0);
|
||||
device.screen = DefaultScreen(device.display);
|
||||
device.visual = DefaultVisual(device.display, device.screen);
|
||||
device.depth = DefaultDepth(device.display, device.screen);
|
||||
|
||||
XSetWindowAttributes attributes;
|
||||
attributes.border_pixel = 0;
|
||||
device.window = XCreateWindow(device.display, (Window)settings.handle,
|
||||
0, 0, 256, 256,
|
||||
0, device.depth, InputOutput, device.visual,
|
||||
CWBorderPixel, &attributes
|
||||
);
|
||||
XSetWindowBackground(device.display, device.window, 0);
|
||||
XMapWindow(device.display, device.window);
|
||||
XFlush(device.display);
|
||||
|
||||
while(XPending(device.display)) {
|
||||
XEvent event;
|
||||
XNextEvent(device.display, &event);
|
||||
}
|
||||
|
||||
if(size() == false) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void term() {
|
||||
free();
|
||||
}
|
||||
|
||||
pVideoXShm() {
|
||||
device.buffer = nullptr;
|
||||
|
||||
settings.buffer = nullptr;
|
||||
}
|
||||
|
||||
~pVideoXShm() {
|
||||
term();
|
||||
}
|
||||
|
||||
//internal:
|
||||
bool size() {
|
||||
XWindowAttributes windowAttributes;
|
||||
XGetWindowAttributes(device.display, settings.handle, &windowAttributes);
|
||||
|
||||
if(device.buffer && device.width == windowAttributes.width && device.height == windowAttributes.height) return true;
|
||||
device.width = windowAttributes.width, device.height = windowAttributes.height;
|
||||
XResizeWindow(device.display, device.window, device.width, device.height);
|
||||
free();
|
||||
|
||||
//create
|
||||
device.shmInfo.shmid = shmget(IPC_PRIVATE, device.width * device.height * sizeof(uint32_t), IPC_CREAT | 0777);
|
||||
if(device.shmInfo.shmid < 0) return false;
|
||||
|
||||
device.shmInfo.shmaddr = (char*)shmat(device.shmInfo.shmid, 0, 0);
|
||||
device.shmInfo.readOnly = False;
|
||||
XShmAttach(device.display, &device.shmInfo);
|
||||
device.buffer = (uint32_t*)device.shmInfo.shmaddr;
|
||||
device.image = XShmCreateImage(device.display, device.visual, device.depth,
|
||||
ZPixmap, device.shmInfo.shmaddr, &device.shmInfo, device.width, device.height
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void free() {
|
||||
if(device.buffer == nullptr) return;
|
||||
device.buffer = nullptr;
|
||||
XShmDetach(device.display, &device.shmInfo);
|
||||
XDestroyImage(device.image);
|
||||
shmdt(device.shmInfo.shmaddr);
|
||||
shmctl(device.shmInfo.shmid, IPC_RMID, 0);
|
||||
}
|
||||
};
|
||||
|
||||
DeclareVideo(XShm)
|
||||
|
||||
}
|
|
@ -26,12 +26,10 @@ else ifeq ($(profile),performance)
|
|||
snessmp := $(snes)/alt/smp
|
||||
snesdsp := $(snes)/alt/dsp
|
||||
snesppu := $(snes)/alt/ppu-performance
|
||||
else
|
||||
$(error Unknown profile: $(profile))
|
||||
endif
|
||||
|
||||
obj/snes-interface.o : $(snes)/interface/interface.cpp $(call rwildcard,$(snes)/interface)
|
||||
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/) $(call rwildcard,$(snes)/audio/) $(call rwildcard,$(snes)/input/)
|
||||
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/)
|
||||
obj/snes-controller.o: $(snes)/controller/controller.cpp $(call rwildcard,$(snes)/controller/)
|
||||
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
|
||||
obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
bool PPU::display_disable() const { return r[0x00] & 0x80; }
|
||||
unsigned PPU::display_brightness() const { return r[0x00] & 0x0f; }
|
||||
|
||||
uint8 PPU::mmio_read(unsigned addr) {
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void PPU::mmio_write(unsigned addr, uint8 data) {
|
||||
r[addr & 0x3f] = data;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,81 @@
|
|||
#include <snes/snes.hpp>
|
||||
|
||||
#define PPU_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "mmio.cpp"
|
||||
PPU ppu;
|
||||
|
||||
void PPU::latch_counters() {
|
||||
}
|
||||
|
||||
bool PPU::interlace() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PPU::overscan() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PPU::hires() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void PPU::enter() {
|
||||
scanline();
|
||||
clock += lineclocks();
|
||||
tick(lineclocks());
|
||||
}
|
||||
|
||||
void PPU::scanline() {
|
||||
if(vcounter() == 0) return frame();
|
||||
if(vcounter() > 224) return;
|
||||
}
|
||||
|
||||
void PPU::frame() {
|
||||
}
|
||||
|
||||
void PPU::enable() {
|
||||
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, { &PPU::mmio_read, this }, { &PPU::mmio_write, this });
|
||||
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, { &PPU::mmio_read, this }, { &PPU::mmio_write, this });
|
||||
}
|
||||
|
||||
void PPU::power() {
|
||||
for(unsigned n = 0; n < 512 * 480; n++) output[n] = rand() & ((1 << 19) - 1);
|
||||
|
||||
for(auto &n : vram) n = 0;
|
||||
for(auto &n : oam) n = 0;
|
||||
for(auto &n : cgram) n = 0;
|
||||
for(auto &n : r) n = 0;
|
||||
reset();
|
||||
}
|
||||
|
||||
void PPU::reset() {
|
||||
PPUcounter::reset();
|
||||
}
|
||||
|
||||
void PPUcounter::serialize(serializer &s) {
|
||||
s.integer(status.interlace);
|
||||
s.integer(status.field);
|
||||
s.integer(status.vcounter);
|
||||
s.integer(status.hcounter);
|
||||
|
||||
s.array(history.field);
|
||||
s.array(history.vcounter);
|
||||
s.array(history.hcounter);
|
||||
s.integer(history.index);
|
||||
}
|
||||
|
||||
void PPU::serialize(serializer &s) {
|
||||
}
|
||||
|
||||
PPU::PPU() {
|
||||
surface = new uint32[512 * 512];
|
||||
output = surface + 16 * 512;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] surface;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
struct PPU : public Processor, public PPUcounter {
|
||||
uint8 vram[64 * 1024];
|
||||
uint8 oam[544];
|
||||
uint8 cgram[512];
|
||||
uint8 r[64];
|
||||
|
||||
enum : bool { Threaded = false };
|
||||
|
||||
void latch_counters();
|
||||
bool interlace() const;
|
||||
bool overscan() const;
|
||||
bool hires() const;
|
||||
|
||||
void enter();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void scanline();
|
||||
void frame();
|
||||
|
||||
void serialize(serializer&);
|
||||
PPU();
|
||||
~PPU();
|
||||
|
||||
private:
|
||||
uint32 *surface;
|
||||
uint32 *output;
|
||||
|
||||
//mmio.cpp
|
||||
alwaysinline bool display_disable() const;
|
||||
alwaysinline unsigned display_brightness() const;
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
friend class Video;
|
||||
};
|
||||
|
||||
extern PPU ppu;
|
|
@ -19,7 +19,6 @@ void BSXCartridge::unload() {
|
|||
}
|
||||
|
||||
void BSXCartridge::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void BSXCartridge::reset() {
|
||||
|
|
|
@ -16,7 +16,6 @@ void BSXFlash::unload() {
|
|||
}
|
||||
|
||||
void BSXFlash::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void BSXFlash::reset() {
|
||||
|
|
|
@ -14,7 +14,6 @@ void BSXSatellaview::unload() {
|
|||
}
|
||||
|
||||
void BSXSatellaview::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void BSXSatellaview::reset() {
|
||||
|
|
|
@ -52,7 +52,6 @@ void HitachiDSP::unload() {
|
|||
}
|
||||
|
||||
void HitachiDSP::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void HitachiDSP::reset() {
|
||||
|
|
|
@ -41,8 +41,6 @@ void ICD2::unload() {
|
|||
void ICD2::power() {
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(4 * 1024 * 1024);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void ICD2::reset() {
|
||||
|
|
|
@ -40,7 +40,6 @@ void Link::unload() {
|
|||
|
||||
void Link::power() {
|
||||
if(link_power) link_power();
|
||||
create(Link::Enter, frequency);
|
||||
}
|
||||
|
||||
void Link::reset() {
|
||||
|
|
|
@ -60,8 +60,6 @@ void MSU1::unload() {
|
|||
void MSU1::power() {
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(44100.0);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void MSU1::reset() {
|
||||
|
@ -78,60 +76,39 @@ void MSU1::reset() {
|
|||
}
|
||||
|
||||
uint8 MSU1::mmio_read(unsigned addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr == 0x2000) {
|
||||
switch(addr & 7) {
|
||||
case 0:
|
||||
return (mmio.data_busy << 7)
|
||||
| (mmio.audio_busy << 6)
|
||||
| (mmio.audio_repeat << 5)
|
||||
| (mmio.audio_play << 4)
|
||||
| (Revision << 0);
|
||||
}
|
||||
|
||||
if(addr == 0x2001) {
|
||||
case 1:
|
||||
if(mmio.data_busy) return 0x00;
|
||||
mmio.data_offset++;
|
||||
if(datafile.open()) return datafile.read();
|
||||
return 0x00;
|
||||
case 2: return 'S';
|
||||
case 3: return '-';
|
||||
case 4: return 'M';
|
||||
case 5: return 'S';
|
||||
case 6: return 'U';
|
||||
case 7: return '0' + Revision;
|
||||
}
|
||||
|
||||
if(addr == 0x2002) return 'S';
|
||||
if(addr == 0x2003) return '-';
|
||||
if(addr == 0x2004) return 'M';
|
||||
if(addr == 0x2005) return 'S';
|
||||
if(addr == 0x2006) return 'U';
|
||||
if(addr == 0x2007) return '0' + Revision;
|
||||
|
||||
return 0x00;
|
||||
throw;
|
||||
}
|
||||
|
||||
void MSU1::mmio_write(unsigned addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr == 0x2000) {
|
||||
mmio.data_offset = (mmio.data_offset & 0xffffff00) | (data << 0);
|
||||
}
|
||||
|
||||
if(addr == 0x2001) {
|
||||
mmio.data_offset = (mmio.data_offset & 0xffff00ff) | (data << 8);
|
||||
}
|
||||
|
||||
if(addr == 0x2002) {
|
||||
mmio.data_offset = (mmio.data_offset & 0xff00ffff) | (data << 16);
|
||||
}
|
||||
|
||||
if(addr == 0x2003) {
|
||||
mmio.data_offset = (mmio.data_offset & 0x00ffffff) | (data << 24);
|
||||
switch(addr & 7) {
|
||||
case 0: mmio.data_offset = (mmio.data_offset & 0xffffff00) | (data << 0); break;
|
||||
case 1: mmio.data_offset = (mmio.data_offset & 0xffff00ff) | (data << 8); break;
|
||||
case 2: mmio.data_offset = (mmio.data_offset & 0xff00ffff) | (data << 16); break;
|
||||
case 3: mmio.data_offset = (mmio.data_offset & 0x00ffffff) | (data << 24);
|
||||
if(datafile.open()) datafile.seek(mmio.data_offset);
|
||||
mmio.data_busy = false;
|
||||
}
|
||||
|
||||
if(addr == 0x2004) {
|
||||
mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
|
||||
}
|
||||
|
||||
if(addr == 0x2005) {
|
||||
mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0);
|
||||
case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
||||
if(audiofile.open()) audiofile.close();
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
uint32 header = audiofile.readm(4);
|
||||
|
@ -145,15 +122,14 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
|
|||
mmio.audio_busy = false;
|
||||
mmio.audio_repeat = false;
|
||||
mmio.audio_play = false;
|
||||
}
|
||||
|
||||
if(addr == 0x2006) {
|
||||
break;
|
||||
case 6:
|
||||
mmio.audio_volume = data;
|
||||
}
|
||||
|
||||
if(addr == 0x2007) {
|
||||
break;
|
||||
case 7:
|
||||
mmio.audio_repeat = data & 2;
|
||||
mmio.audio_play = data & 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -266,8 +266,6 @@ void NECDSP::power() {
|
|||
regs.rp.bits(11);
|
||||
regs.dp.bits(11);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void NECDSP::reset() {
|
||||
|
|
|
@ -16,7 +16,6 @@ void OBC1::unload() {
|
|||
}
|
||||
|
||||
void OBC1::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void OBC1::reset() {
|
||||
|
|
|
@ -128,7 +128,6 @@ void SA1::unload() {
|
|||
void SA1::power() {
|
||||
regs.a = regs.x = regs.y = 0x0000;
|
||||
regs.s = 0x01ff;
|
||||
reset();
|
||||
}
|
||||
|
||||
void SA1::reset() {
|
||||
|
|
|
@ -22,7 +22,6 @@ void SDD1::unload() {
|
|||
}
|
||||
|
||||
void SDD1::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void SDD1::reset() {
|
||||
|
|
|
@ -22,7 +22,6 @@ void SPC7110::unload() {
|
|||
}
|
||||
|
||||
void SPC7110::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void SPC7110::reset() {
|
||||
|
|
|
@ -21,7 +21,6 @@ void SRTC::unload() {
|
|||
}
|
||||
|
||||
void SRTC::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void SRTC::reset() {
|
||||
|
|
|
@ -55,7 +55,6 @@ void ST0018::unload() {
|
|||
}
|
||||
|
||||
void ST0018::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void ST0018::reset() {
|
||||
|
|
|
@ -51,7 +51,6 @@ void SuperFX::unload() {
|
|||
|
||||
void SuperFX::power() {
|
||||
clockmode = config.superfx.speed;
|
||||
reset();
|
||||
}
|
||||
|
||||
void SuperFX::reset() {
|
||||
|
|
|
@ -7,7 +7,7 @@ Configuration::Configuration() {
|
|||
controller_port2 = Input::Device::Joypad;
|
||||
expansion_port = System::ExpansionPortDevice::BSX;
|
||||
region = System::Region::Autodetect;
|
||||
random = false; //true;
|
||||
random = true;
|
||||
|
||||
cpu.version = 2;
|
||||
cpu.ntsc_frequency = 21477272; //315 / 88 * 6000000
|
||||
|
|
|
@ -125,8 +125,6 @@ void CPU::power() {
|
|||
mmio_power();
|
||||
dma_power();
|
||||
timing_power();
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void CPU::reset() {
|
||||
|
|
|
@ -274,8 +274,6 @@ void DSP::power() {
|
|||
voice[i].t_envx_out = 0;
|
||||
voice[i].hidden_env = 0;
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void DSP::reset() {
|
||||
|
|
|
@ -98,8 +98,6 @@ void PPU::power() {
|
|||
for(auto &n : vram) n = random(0x00);
|
||||
for(auto &n : oam) n = random(0x00);
|
||||
for(auto &n : cgram) n = random(0x00);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void PPU::reset() {
|
||||
|
|
|
@ -94,13 +94,7 @@ uint8 SMPcore::op_ror(uint8 x) {
|
|||
}
|
||||
|
||||
uint8 SMPcore::op_sbc(uint8 x, uint8 y) {
|
||||
int r = x - y - !regs.p.c;
|
||||
regs.p.n = r & 0x80;
|
||||
regs.p.v = (x ^ y) & (x ^ r) & 0x80;
|
||||
regs.p.h = !((x ^ y ^ r) & 0x10);
|
||||
regs.p.z = (uint8)r == 0;
|
||||
regs.p.c = r >= 0;
|
||||
return r;
|
||||
return op_adc(x, ~y);
|
||||
}
|
||||
|
||||
uint8 SMPcore::op_st(uint8 x, uint8 y) {
|
||||
|
|
|
@ -24,18 +24,20 @@ struct word_t {
|
|||
};
|
||||
|
||||
inline operator unsigned() const { return w; }
|
||||
inline unsigned operator=(unsigned data) { w = data; return w; }
|
||||
inline unsigned operator=(unsigned data) { return w = data; }
|
||||
|
||||
inline unsigned operator++() { return ++w; }
|
||||
inline unsigned operator--() { return --w; }
|
||||
|
||||
inline unsigned operator++(int) { unsigned data = w++; return data; }
|
||||
inline unsigned operator--(int) { unsigned data = w--; return data; }
|
||||
|
||||
inline unsigned operator|=(unsigned data) { w |= data; return w; }
|
||||
inline unsigned operator^=(unsigned data) { w ^= data; return w; }
|
||||
inline unsigned operator&=(unsigned data) { w &= data; return w; }
|
||||
inline unsigned operator+=(unsigned data) { w += data; return w; }
|
||||
inline unsigned operator-=(unsigned data) { w -= data; return w; }
|
||||
inline unsigned operator+=(unsigned data) { return w += data;; }
|
||||
inline unsigned operator-=(unsigned data) { return w -= data;; }
|
||||
|
||||
inline unsigned operator|=(unsigned data) { return w |= data; }
|
||||
inline unsigned operator^=(unsigned data) { return w ^= data; }
|
||||
inline unsigned operator&=(unsigned data) { return w &= data; }
|
||||
};
|
||||
|
||||
struct regs_t {
|
||||
|
|
|
@ -53,8 +53,6 @@ void SMP::power() {
|
|||
timer0.target = 0;
|
||||
timer1.target = 0;
|
||||
timer2.target = 0;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void SMP::reset() {
|
||||
|
|
|
@ -10,10 +10,9 @@ System system;
|
|||
#include <snes/scheduler/scheduler.cpp>
|
||||
#include <snes/random/random.cpp>
|
||||
|
||||
#include <snes/video/video.cpp>
|
||||
#include <snes/audio/audio.cpp>
|
||||
#include <snes/input/input.cpp>
|
||||
|
||||
#include "video.cpp"
|
||||
#include "audio.cpp"
|
||||
#include "input.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
void System::run() {
|
||||
|
@ -181,17 +180,7 @@ void System::power() {
|
|||
if(cartridge.has_msu1()) msu1.power();
|
||||
if(cartridge.has_link()) link.power();
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2);
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
||||
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
|
||||
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
||||
if(cartridge.has_link()) cpu.coprocessors.append(&link);
|
||||
|
||||
scheduler.init();
|
||||
input.connect(0, config.controller_port1);
|
||||
input.connect(1, config.controller_port2);
|
||||
reset();
|
||||
}
|
||||
|
||||
void System::reset() {
|
||||
|
|
|
@ -42,9 +42,9 @@ private:
|
|||
friend class Input;
|
||||
};
|
||||
|
||||
#include <snes/video/video.hpp>
|
||||
#include <snes/audio/audio.hpp>
|
||||
#include <snes/input/input.hpp>
|
||||
#include "video.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "input.hpp"
|
||||
|
||||
#include <snes/config/config.hpp>
|
||||
#include <snes/debugger/debugger.hpp>
|
||||
|
|
|
@ -3,6 +3,7 @@ Config *config = 0;
|
|||
|
||||
Config::Config() {
|
||||
attach(video.driver = "", "Video::Driver");
|
||||
attach(video.depth = 24u, "Video::Depth");
|
||||
attach(video.filter = "None", "Video::Filter");
|
||||
attach(video.shader = "Blur", "Video::Shader");
|
||||
attach(video.synchronize = true, "Video::Synchronize");
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
struct Config : public configuration {
|
||||
struct Video {
|
||||
string driver;
|
||||
unsigned depth;
|
||||
|
||||
string filter;
|
||||
string shader;
|
||||
|
||||
bool synchronize;
|
||||
bool correctAspectRatio;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const s
|
|||
}
|
||||
|
||||
GameBoy::interface = this;
|
||||
GameBoy::video.generate(GameBoy::Video::Format::RGB24);
|
||||
GameBoy::video.generate(GameBoy::Video::Format::RGB30);
|
||||
interface->loadCartridge(::Interface::Mode::GameBoy);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
|
|||
uint32_t *dp = output + y * outputPitch;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint32_t color = *sp++;
|
||||
*dp++ = (palette[color >> 16] << 16) + (palette[color >> 8] << 8) + (palette[color >> 0] << 0);
|
||||
*dp++ = palette((color >> 20) & 1023, (color >> 10) & 1023, (color >> 0) & 1023);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
}
|
||||
|
||||
interface->loadCartridge(::Interface::Mode::NES);
|
||||
NES::video.generate(NES::Video::Format::RGB24);
|
||||
NES::video.generate(NES::Video::Format::RGB30);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Palette palette;
|
||||
|
||||
uint8_t Palette::operator[](uint8_t n) {
|
||||
return color[n];
|
||||
unsigned Palette::operator()(unsigned r, unsigned g, unsigned b) const {
|
||||
return red[r] + green[g] + blue[b];
|
||||
}
|
||||
|
||||
/* 5-bit -> 8-bit
|
||||
|
@ -13,35 +13,38 @@ const uint8_t Palette::gammaRamp[32] = {
|
|||
};
|
||||
*/
|
||||
|
||||
uint8_t Palette::contrastAdjust(uint8_t input) {
|
||||
signed contrast = config->video.contrast - 100;
|
||||
signed result = input - contrast + (2 * contrast * input + 127) / 255;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Palette::brightnessAdjust(uint8_t input) {
|
||||
signed brightness = config->video.brightness - 100;
|
||||
signed result = input + brightness;
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
uint8_t Palette::gammaAdjust(uint8_t input) {
|
||||
signed result = (signed)(pow(((double)input / 255.0), (double)config->video.gamma / 100.0) * 255.0 + 0.5);
|
||||
return max(0, min(255, result));
|
||||
}
|
||||
|
||||
void Palette::update() {
|
||||
double exponent = 1.0 + (double)config->video.gamma * 0.01;
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
unsigned result = (n < 128 ? 127 * pow(((double)n / 127), exponent) : n);
|
||||
for(unsigned n = 0; n < 1024; n++) {
|
||||
unsigned result = (n < 512 ? 511 * pow(((double)n / 511), exponent) : n);
|
||||
color[n] = result;
|
||||
}
|
||||
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
color[n] = contrastAdjust(color[n]);
|
||||
double contrast = config->video.contrast * 0.01;
|
||||
for(unsigned n = 0; n < 1024; n++) {
|
||||
signed result = color[n] * contrast;
|
||||
color[n] = max(0, min(1023, result));
|
||||
}
|
||||
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
color[n] = brightnessAdjust(color[n]);
|
||||
signed brightness = (config->video.brightness - 100) * 4;
|
||||
for(unsigned n = 0; n < 1024; n++) {
|
||||
signed result = color[n] + brightness;
|
||||
color[n] = max(0, min(1023, result));
|
||||
}
|
||||
|
||||
if(config->video.depth == 30) {
|
||||
for(unsigned n = 0; n < 1024; n++) {
|
||||
red[n] = color[n] << 20;
|
||||
green[n] = color[n] << 10;
|
||||
blue[n] = color[n] << 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(config->video.depth == 24) {
|
||||
for(unsigned n = 0; n < 1024; n++) {
|
||||
red[n] = (color[n] >> 2) << 16;
|
||||
green[n] = (color[n] >> 2) << 8;
|
||||
blue[n] = (color[n] >> 2) << 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
struct Palette {
|
||||
alwaysinline uint8_t operator[](uint8_t color);
|
||||
|
||||
uint8_t contrastAdjust(uint8_t);
|
||||
uint8_t brightnessAdjust(uint8_t);
|
||||
uint8_t gammaAdjust(uint8_t);
|
||||
alwaysinline unsigned operator()(unsigned r, unsigned g, unsigned b) const;
|
||||
void update();
|
||||
|
||||
private:
|
||||
uint32_t color[256];
|
||||
uint32_t color[1024];
|
||||
uint32_t red[1024], green[1024], blue[1024];
|
||||
};
|
||||
|
||||
extern Palette palette;
|
||||
|
|
|
@ -51,7 +51,7 @@ bool InterfaceSNES::loadCartridge(const string &basename) {
|
|||
|
||||
loadMemory();
|
||||
interface->loadCartridge(::Interface::Mode::SNES);
|
||||
SNES::video.generate(SNES::Video::Format::RGB24);
|
||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, cons
|
|||
|
||||
loadMemory();
|
||||
interface->loadCartridge(::Interface::Mode::SNES);
|
||||
SNES::video.generate(SNES::Video::Format::RGB24);
|
||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const strin
|
|||
|
||||
loadMemory();
|
||||
interface->loadCartridge(::Interface::Mode::SNES);
|
||||
SNES::video.generate(SNES::Video::Format::RGB24);
|
||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const strin
|
|||
|
||||
loadMemory();
|
||||
interface->loadCartridge(::Interface::Mode::SNES);
|
||||
SNES::video.generate(SNES::Video::Format::RGB24);
|
||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri
|
|||
|
||||
loadMemory();
|
||||
interface->loadCartridge(::Interface::Mode::SNES);
|
||||
SNES::video.generate(SNES::Video::Format::RGB24);
|
||||
SNES::video.generate(SNES::Video::Format::RGB30);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,13 @@ void Application::run() {
|
|||
}
|
||||
|
||||
Application::Application(int argc, char **argv) {
|
||||
title = "bsnes v084.01";
|
||||
title = "bsnes v084.03";
|
||||
|
||||
application = this;
|
||||
quit = false;
|
||||
pause = false;
|
||||
autopause = false;
|
||||
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
auto unused = ::realpath(argv[0], path);
|
||||
|
@ -46,6 +47,7 @@ Application::Application(int argc, char **argv) {
|
|||
}
|
||||
mkdir(userpath, 0755);
|
||||
}
|
||||
|
||||
config = new Config;
|
||||
interface = new Interface;
|
||||
inputManager = new InputManager;
|
||||
|
@ -76,6 +78,7 @@ Application::Application(int argc, char **argv) {
|
|||
video.driver(config->video.driver);
|
||||
video.set(Video::Handle, mainWindow->viewport.handle());
|
||||
video.set(Video::Synchronize, config->video.synchronize);
|
||||
video.set(Video::Depth, config->video.depth);
|
||||
if(video.init() == false) {
|
||||
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->video.driver, " video driver." });
|
||||
video.driver("None");
|
||||
|
|
|
@ -34,13 +34,14 @@ const uint8_t hqTable[256] = {
|
|||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
|
||||
};
|
||||
|
||||
static uint16_t rgb555(uint32_t C) {
|
||||
return ((C >> 9) & 0x7c00) + ((C >> 6) & 0x03e0) + ((C >> 3) & 0x001f);
|
||||
static uint16_t rgb15(uint32_t C) {
|
||||
return ((C >> 15) & 0x7c00) + ((C >> 10) & 0x03e0) + ((C >> 5) & 0x001f);
|
||||
}
|
||||
|
||||
static uint32_t rgb888(uint16_t C) {
|
||||
return ((C & 0x7c00) << 9) + ((C & 0x03e0) << 6) + ((C & 0x001f) << 3)
|
||||
+ ((C & 0x7000) << 4) + ((C & 0x0380) << 1) + ((C & 0x001c) >> 2);
|
||||
static uint32_t rgb30(uint16_t C) {
|
||||
return ((C & 0x7c00) << 15) + ((C & 0x7c00) << 10)
|
||||
+ ((C & 0x03e0) << 10) + ((C & 0x03e0) << 5)
|
||||
+ ((C & 0x001f) << 5) + ((C & 0x001f) << 0);
|
||||
}
|
||||
|
||||
static void initialize() {
|
||||
|
@ -51,11 +52,13 @@ static void initialize() {
|
|||
yuvTable = new uint32_t[32768];
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
uint32_t C = rgb888(i);
|
||||
uint8_t R = (i >> 10) & 31;
|
||||
uint8_t G = (i >> 5) & 31;
|
||||
uint8_t B = (i >> 0) & 31;
|
||||
|
||||
double r = (uint8_t)(C >> 16);
|
||||
double g = (uint8_t)(C >> 8);
|
||||
double b = (uint8_t)(C >> 0);
|
||||
double r = (R << 3) | (R >> 2);
|
||||
double g = (G << 3) | (G >> 2);
|
||||
double b = (B << 3) | (B >> 2);
|
||||
|
||||
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
|
||||
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
|
||||
|
@ -173,15 +176,15 @@ dllexport void filter_render(
|
|||
*out1++ = 0; *out1++ = 0;
|
||||
|
||||
for(unsigned x = 1; x < width - 1; x++) {
|
||||
uint16_t A = rgb555(*(in - prevline - 1));
|
||||
uint16_t B = rgb555(*(in - prevline + 0));
|
||||
uint16_t C = rgb555(*(in - prevline + 1));
|
||||
uint16_t D = rgb555(*(in - 1));
|
||||
uint16_t E = rgb555(*(in + 0));
|
||||
uint16_t F = rgb555(*(in + 1));
|
||||
uint16_t G = rgb555(*(in + nextline - 1));
|
||||
uint16_t H = rgb555(*(in + nextline + 0));
|
||||
uint16_t I = rgb555(*(in + nextline + 1));
|
||||
uint16_t A = rgb15(*(in - prevline - 1));
|
||||
uint16_t B = rgb15(*(in - prevline + 0));
|
||||
uint16_t C = rgb15(*(in - prevline + 1));
|
||||
uint16_t D = rgb15(*(in - 1));
|
||||
uint16_t E = rgb15(*(in + 0));
|
||||
uint16_t F = rgb15(*(in + 1));
|
||||
uint16_t G = rgb15(*(in + nextline - 1));
|
||||
uint16_t H = rgb15(*(in + nextline + 0));
|
||||
uint16_t I = rgb15(*(in + nextline + 1));
|
||||
uint32_t e = yuvTable[E] + diff_offset;
|
||||
|
||||
uint8_t pattern;
|
||||
|
@ -194,10 +197,10 @@ dllexport void filter_render(
|
|||
pattern |= diff(e, H) << 6;
|
||||
pattern |= diff(e, I) << 7;
|
||||
|
||||
*(out0 + 0) = rgb888(blend(hqTable[pattern], E, A, B, D, F, H)); pattern = rotate[pattern];
|
||||
*(out0 + 1) = rgb888(blend(hqTable[pattern], E, C, F, B, H, D)); pattern = rotate[pattern];
|
||||
*(out1 + 1) = rgb888(blend(hqTable[pattern], E, I, H, F, D, B)); pattern = rotate[pattern];
|
||||
*(out1 + 0) = rgb888(blend(hqTable[pattern], E, G, D, H, B, F));
|
||||
*(out0 + 0) = rgb30(blend(hqTable[pattern], E, A, B, D, F, H)); pattern = rotate[pattern];
|
||||
*(out0 + 1) = rgb30(blend(hqTable[pattern], E, C, F, B, H, D)); pattern = rotate[pattern];
|
||||
*(out1 + 1) = rgb30(blend(hqTable[pattern], E, I, H, F, D, B)); pattern = rotate[pattern];
|
||||
*(out1 + 0) = rgb30(blend(hqTable[pattern], E, G, D, H, B, F));
|
||||
|
||||
in++;
|
||||
out0 += 2;
|
||||
|
|
|
@ -17,6 +17,7 @@ dllexport void filter_render(
|
|||
const uint32_t *input, unsigned inputPitch,
|
||||
unsigned width, unsigned height
|
||||
) {
|
||||
enum : unsigned { Mask = (1 << 20) | (1 << 10) | (1 << 0) };
|
||||
outputPitch >>= 2, inputPitch >>= 2;
|
||||
|
||||
#pragma omp parallel for
|
||||
|
@ -36,10 +37,10 @@ dllexport void filter_render(
|
|||
uint32_t E = *(in++ + nextline);
|
||||
|
||||
if(A != E && B != D) {
|
||||
*out0++ = (A == B ? C + A - ((C ^ A) & 0x010101) >> 1 : C);
|
||||
*out0++ = (A == D ? C + A - ((C ^ A) & 0x010101) >> 1 : C);
|
||||
*out1++ = (E == B ? C + E - ((C ^ E) & 0x010101) >> 1 : C);
|
||||
*out1++ = (E == D ? C + E - ((C ^ E) & 0x010101) >> 1 : C);
|
||||
*out0++ = (A == B ? C + A - ((C ^ A) & Mask) >> 1 : C);
|
||||
*out0++ = (A == D ? C + A - ((C ^ A) & Mask) >> 1 : C);
|
||||
*out1++ = (E == B ? C + E - ((C ^ E) & Mask) >> 1 : C);
|
||||
*out1++ = (E == D ? C + E - ((C ^ E) & Mask) >> 1 : C);
|
||||
} else {
|
||||
*out0++ = C;
|
||||
*out0++ = C;
|
||||
|
|
|
@ -2,12 +2,12 @@ include nall/Makefile
|
|||
|
||||
c := $(compiler) -std=gnu99
|
||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||
flags := -fPIC -O3 -I. -Iobj -fomit-frame-pointer
|
||||
flags := -fPIC -I. -Iobj -O3 -fomit-frame-pointer
|
||||
link := -s
|
||||
objects :=
|
||||
|
||||
ifeq ($(platform),x)
|
||||
flags += -fopenmp
|
||||
# flags += -fopenmp
|
||||
endif
|
||||
|
||||
objects += out/Pixellate2x.filter
|
||||
|
|
|
@ -17,6 +17,7 @@ dllexport void filter_render(
|
|||
const uint32_t *input, unsigned inputPitch,
|
||||
unsigned width, unsigned height
|
||||
) {
|
||||
enum : unsigned { Mask = (1022 << 20) + (1022 << 10) + (1022 << 0) };
|
||||
outputPitch >>= 2, inputPitch >>= 2;
|
||||
|
||||
#pragma omp parallel for
|
||||
|
@ -31,27 +32,27 @@ dllexport void filter_render(
|
|||
uint32_t B = *in;
|
||||
uint32_t C = (x == width - 1 ? 0 : *(in + 1));
|
||||
|
||||
uint8_t Ar = A >> 16, Ag = A >> 8, Ab = A >> 0;
|
||||
uint8_t Br = B >> 16, Bg = B >> 8, Bb = B >> 0;
|
||||
uint8_t Cr = C >> 16, Cg = C >> 8, Cb = C >> 0;
|
||||
unsigned Ar = (A >> 20) & 1023, Ag = (A >> 10) & 1023, Ab = (A >> 0) & 1023;
|
||||
unsigned Br = (B >> 20) & 1023, Bg = (B >> 10) & 1023, Bb = (B >> 0) & 1023;
|
||||
unsigned Cr = (C >> 20) & 1023, Cg = (C >> 10) & 1023, Cb = (C >> 0) & 1023;
|
||||
|
||||
A = ((Br >> 0) << 16) + ((Bg >> 1) << 8) + ((Ab >> 1) << 0);
|
||||
B = ((Br >> 1) << 16) + ((Bg >> 0) << 8) + ((Bb >> 1) << 0);
|
||||
C = ((Cr >> 1) << 16) + ((Bg >> 1) << 8) + ((Bb >> 0) << 0);
|
||||
A = ((Br >> 0) << 20) + ((Bg >> 1) << 10) + ((Ab >> 1) << 0);
|
||||
B = ((Br >> 1) << 20) + ((Bg >> 0) << 10) + ((Bb >> 1) << 0);
|
||||
C = ((Cr >> 1) << 20) + ((Bg >> 1) << 10) + ((Bb >> 0) << 0);
|
||||
|
||||
in++;
|
||||
|
||||
*out0++ = A;
|
||||
*out1++ = A;
|
||||
*out2++ = (A & 0xf8f8f8) >> 1;
|
||||
*out2++ = (A & Mask) >> 1;
|
||||
|
||||
*out0++ = B;
|
||||
*out1++ = B;
|
||||
*out2++ = (B & 0xf8f8f8) >> 1;
|
||||
*out2++ = (B & Mask) >> 1;
|
||||
|
||||
*out0++ = C;
|
||||
*out1++ = C;
|
||||
*out2++ = (C & 0xf8f8f8) >> 1;
|
||||
*out2++ = (C & Mask) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ dllexport void filter_render(
|
|||
const uint32_t *input, unsigned inputPitch,
|
||||
unsigned width, unsigned height
|
||||
) {
|
||||
enum : unsigned { Mask = (1022 << 20) | (1022 << 10) | (1022 << 0) };
|
||||
outputPitch >>= 2, inputPitch >>= 2;
|
||||
|
||||
#pragma omp parallel for
|
||||
|
@ -26,7 +27,7 @@ dllexport void filter_render(
|
|||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
*out0++ = *in;
|
||||
*out1++ = (*in++ & 0xf8f8f8) >> 1;
|
||||
*out1++ = (*in++ & Mask) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ ifeq ($(platform),)
|
|||
ifeq ($(uname),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring CYGWIN,$(uname)),)
|
||||
platform := win
|
||||
delete = del $(subst /,\,$1)
|
||||
else ifneq ($(findstring Darwin,$(uname)),)
|
||||
platform := osx
|
||||
delete = rm -f $1
|
||||
|
@ -32,9 +35,9 @@ ifeq ($(compiler),)
|
|||
ifeq ($(platform),win)
|
||||
compiler := gcc
|
||||
else ifeq ($(platform),osx)
|
||||
compiler := gcc-mp-4.5
|
||||
compiler := gcc-mp-4.6
|
||||
else
|
||||
compiler := gcc-4.5
|
||||
compiler := gcc-4.6
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
#define NALL_ARRAY_HPP
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <nall/algorithm.hpp>
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/concept.hpp>
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
@ -26,7 +25,7 @@ namespace nall {
|
|||
|
||||
void reset() {
|
||||
if(pool) free(pool);
|
||||
pool = 0;
|
||||
pool = nullptr;
|
||||
poolsize = 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
|
@ -54,21 +53,14 @@ namespace nall {
|
|||
operator[](buffersize) = data;
|
||||
}
|
||||
|
||||
void append(const T data[], unsigned length) {
|
||||
for(unsigned n = 0; n < length; n++) operator[](buffersize) = data[n];
|
||||
}
|
||||
|
||||
void remove() {
|
||||
if(size > 0) resize(size - 1); //remove last element only
|
||||
}
|
||||
|
||||
template<typename U> void insert(unsigned index, const U list) {
|
||||
unsigned listsize = container_size(list);
|
||||
resize(buffersize + listsize);
|
||||
memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T));
|
||||
foreach(item, list) pool[index++] = item;
|
||||
}
|
||||
|
||||
void insert(unsigned index, const T item) {
|
||||
insert(index, array<T>{ item });
|
||||
}
|
||||
|
||||
void remove(unsigned index, unsigned count = 1) {
|
||||
for(unsigned i = index; count + i < buffersize; i++) {
|
||||
pool[i] = pool[count + i];
|
||||
|
@ -86,10 +78,10 @@ namespace nall {
|
|||
memset(pool, 0, buffersize * sizeof(T));
|
||||
}
|
||||
|
||||
array() : pool(0), poolsize(0), buffersize(0) {
|
||||
array() : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
}
|
||||
|
||||
array(std::initializer_list<T> list) : pool(0), poolsize(0), buffersize(0) {
|
||||
array(std::initializer_list<T> list) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
||||
}
|
||||
|
||||
|
@ -107,7 +99,7 @@ namespace nall {
|
|||
return *this;
|
||||
}
|
||||
|
||||
array(const array &source) : pool(0), poolsize(0), buffersize(0) {
|
||||
array(const array &source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
operator=(source);
|
||||
}
|
||||
|
||||
|
@ -117,12 +109,12 @@ namespace nall {
|
|||
pool = source.pool;
|
||||
poolsize = source.poolsize;
|
||||
buffersize = source.buffersize;
|
||||
source.pool = 0;
|
||||
source.pool = nullptr;
|
||||
source.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
array(array &&source) : pool(0), poolsize(0), buffersize(0) {
|
||||
array(array &&source) : pool(nullptr), poolsize(0), buffersize(0) {
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
|
@ -144,8 +136,6 @@ namespace nall {
|
|||
const T* begin() const { return &pool[0]; }
|
||||
const T* end() const { return &pool[buffersize]; }
|
||||
};
|
||||
|
||||
template<typename T> struct has_size<array<T>> { enum { value = true }; };
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
#ifndef NALL_ATOI_HPP
|
||||
#define NALL_ATOI_HPP
|
||||
|
||||
namespace nall {
|
||||
|
||||
//note: this header is intended to form the base for user-defined literals;
|
||||
//once they are supported by GCC. eg:
|
||||
//unsigned operator "" b(const char *s) { return binary(s); }
|
||||
//-> signed data = 1001b;
|
||||
//(0b1001 is nicer, but is not part of the C++ standard)
|
||||
|
||||
constexpr inline uintmax_t binary_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s == '0' || *s == '1' ? binary_(s + 1, (sum << 1) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t octal_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= '0' && *s <= '7' ? octal_(s + 1, (sum << 3) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t decimal_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= '0' && *s <= '9' ? decimal_(s + 1, (sum * 10) + *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t hex_(const char *s, uintmax_t sum = 0) {
|
||||
return (
|
||||
*s >= 'A' && *s <= 'F' ? hex_(s + 1, (sum << 4) | *s - 'A' + 10) :
|
||||
*s >= 'a' && *s <= 'f' ? hex_(s + 1, (sum << 4) | *s - 'a' + 10) :
|
||||
*s >= '0' && *s <= '9' ? hex_(s + 1, (sum << 4) | *s - '0') :
|
||||
sum
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
constexpr inline uintmax_t binary(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'B' ? binary_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'b' ? binary_(s + 2) :
|
||||
*s == '%' ? binary_(s + 1) :
|
||||
binary_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t octal(const char *s) {
|
||||
return (
|
||||
octal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline intmax_t integer(const char *s) {
|
||||
return (
|
||||
*s == '+' ? +decimal_(s + 1) :
|
||||
*s == '-' ? -decimal_(s + 1) :
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t decimal(const char *s) {
|
||||
return (
|
||||
decimal_(s)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr inline uintmax_t hex(const char *s) {
|
||||
return (
|
||||
*s == '0' && *(s + 1) == 'X' ? hex_(s + 2) :
|
||||
*s == '0' && *(s + 1) == 'x' ? hex_(s + 2) :
|
||||
*s == '$' ? hex_(s + 1) :
|
||||
hex_(s)
|
||||
);
|
||||
}
|
||||
|
||||
inline double fp(const char *s) {
|
||||
return atof(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,8 +5,7 @@
|
|||
#include <nall/stdint.hpp>
|
||||
|
||||
namespace nall {
|
||||
class base64 {
|
||||
public:
|
||||
struct base64 {
|
||||
static bool encode(char *&output, const uint8_t* input, unsigned inlength) {
|
||||
output = new char[inlength * 8 / 6 + 6]();
|
||||
|
||||
|
|
|
@ -2,39 +2,39 @@
|
|||
#define NALL_BIT_HPP
|
||||
|
||||
namespace nall {
|
||||
template<int bits> inline unsigned uclamp(const unsigned x) {
|
||||
template<int bits> constexpr inline unsigned uclamp(const unsigned x) {
|
||||
enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||
return y + ((x - y) & -(x < y)); //min(x, y);
|
||||
}
|
||||
|
||||
template<int bits> inline unsigned uclip(const unsigned x) {
|
||||
template<int bits> constexpr inline unsigned uclip(const unsigned x) {
|
||||
enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) };
|
||||
return (x & m);
|
||||
}
|
||||
|
||||
template<int bits> inline signed sclamp(const signed x) {
|
||||
template<int bits> constexpr inline signed sclamp(const signed x) {
|
||||
enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 };
|
||||
return (x > m) ? m : (x < -b) ? -b : x;
|
||||
}
|
||||
|
||||
template<int bits> inline signed sclip(const signed x) {
|
||||
template<int bits> constexpr inline signed sclip(const signed x) {
|
||||
enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
|
||||
return ((x & m) ^ b) - b;
|
||||
}
|
||||
|
||||
namespace bit {
|
||||
//lowest(0b1110) == 0b0010
|
||||
template<typename T> inline T lowest(const T x) {
|
||||
template<typename T> constexpr inline T lowest(const T x) {
|
||||
return x & -x;
|
||||
}
|
||||
|
||||
//clear_lowest(0b1110) == 0b1100
|
||||
template<typename T> inline T clear_lowest(const T x) {
|
||||
template<typename T> constexpr inline T clear_lowest(const T x) {
|
||||
return x & (x - 1);
|
||||
}
|
||||
|
||||
//set_lowest(0b0101) == 0b0111
|
||||
template<typename T> inline T set_lowest(const T x) {
|
||||
template<typename T> constexpr inline T set_lowest(const T x) {
|
||||
return x | (x + 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ protected:
|
|||
struct Node {
|
||||
unsigned offset;
|
||||
Node *next;
|
||||
inline Node() : offset(0), next(0) {}
|
||||
inline Node() : offset(0), next(nullptr) {}
|
||||
inline ~Node() { if(next) delete next; }
|
||||
};
|
||||
|
||||
|
|
|
@ -1,18 +1,56 @@
|
|||
#ifndef NALL_COMPOSITOR_HPP
|
||||
#define NALL_COMPOSITOR_HPP
|
||||
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct compositor {
|
||||
inline static bool enabled();
|
||||
inline static bool enable(bool status);
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
enum class Compositor : unsigned { Unknown, Metacity, Xfwm4 };
|
||||
inline static Compositor detect();
|
||||
|
||||
inline static bool enabled_metacity();
|
||||
inline static bool enable_metacity(bool status);
|
||||
|
||||
inline static bool enabled_xfwm4();
|
||||
inline static bool enable_xfwm4(bool status);
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(PLATFORM_X)
|
||||
|
||||
bool compositor::enabled() {
|
||||
//Metacity
|
||||
|
||||
bool compositor::enabled_metacity() {
|
||||
FILE *fp = popen("gconftool-2 --get /apps/metacity/general/compositing_manager", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
char buffer[512];
|
||||
if(fgets(buffer, sizeof buffer, fp) == 0) return false;
|
||||
|
||||
if(!memcmp(buffer, "true", 4)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable_metacity(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager true", "r");
|
||||
} else {
|
||||
fp = popen("gconftool-2 --set --type bool /apps/metacity/general/compositing_manager false", "r");
|
||||
}
|
||||
if(fp == 0) return false;
|
||||
pclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Xfwm4
|
||||
|
||||
bool compositor::enabled_xfwm4() {
|
||||
FILE *fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing'", "r");
|
||||
if(fp == 0) return false;
|
||||
|
||||
|
@ -23,7 +61,7 @@ bool compositor::enabled() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
bool compositor::enable_xfwm4(bool status) {
|
||||
FILE *fp;
|
||||
if(status) {
|
||||
fp = popen("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'", "r");
|
||||
|
@ -35,7 +73,42 @@ bool compositor::enable(bool status) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_WIN)
|
||||
//General
|
||||
|
||||
compositor::Compositor compositor::detect() {
|
||||
Compositor result = Compositor::Unknown;
|
||||
|
||||
FILE *fp;
|
||||
char buffer[512];
|
||||
|
||||
fp = popen("pidof metacity", "r");
|
||||
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Metacity;
|
||||
pclose(fp);
|
||||
|
||||
fp = popen("pidof xfwm4", "r");
|
||||
if(fp && fgets(buffer, sizeof buffer, fp)) result = Compositor::Xfwm4;
|
||||
pclose(fp);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool compositor::enabled() {
|
||||
switch(detect()) {
|
||||
case Compositor::Metacity: return enabled_metacity();
|
||||
case Compositor::Xfwm4: return enabled_xfwm4();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool compositor::enable(bool status) {
|
||||
switch(detect()) {
|
||||
case Compositor::Metacity: return enable_metacity(status);
|
||||
case Compositor::Xfwm4: return enable_xfwm4(status);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
bool compositor::enabled() {
|
||||
HMODULE module = GetModuleHandleW(L"dwmapi");
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace nall {
|
|||
else list[n].type = unknown_t;
|
||||
}
|
||||
|
||||
virtual bool load(const char *filename) {
|
||||
virtual bool load(const string &filename) {
|
||||
string data;
|
||||
if(data.readfile(filename) == true) {
|
||||
data.replace("\r", "");
|
||||
|
@ -100,7 +100,7 @@ namespace nall {
|
|||
}
|
||||
}
|
||||
|
||||
virtual bool save(const char *filename) const {
|
||||
virtual bool save(const string &filename) const {
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write)) {
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#ifndef NALL_DIRECTORY_HPP
|
||||
#define NALL_DIRECTORY_HPP
|
||||
|
||||
#include <nall/foreach.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/sort.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
|
@ -22,7 +23,7 @@ struct directory {
|
|||
static lstring contents(const string &pathname, const string &pattern = "*");
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
inline bool directory::exists(const string &pathname) {
|
||||
DWORD result = GetFileAttributes(utf16_t(pathname));
|
||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||
|
@ -56,7 +57,7 @@ struct directory {
|
|||
FindClose(handle);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
foreach(name, list) name.append("/"); //must append after sorting
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -89,7 +90,7 @@ struct directory {
|
|||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#else
|
||||
|
@ -116,7 +117,7 @@ struct directory {
|
|||
closedir(dp);
|
||||
}
|
||||
if(list.size() > 0) sort(&list[0], list.size());
|
||||
foreach(name, list) name.append("/"); //must append after sorting
|
||||
for(auto &name : list) name.append("/"); //must append after sorting
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -142,7 +143,7 @@ struct directory {
|
|||
inline lstring directory::contents(const string &pathname, const string &pattern) {
|
||||
lstring folders = directory::folders(pathname); //pattern search of contents() should only filter files
|
||||
lstring files = directory::files(pathname, pattern);
|
||||
foreach(file, files) folders.append(file);
|
||||
for(auto &file : files) folders.append(file);
|
||||
return folders;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
//dynamic linking support
|
||||
|
||||
#include <nall/detect.hpp>
|
||||
#include <nall/intrinsics.hpp>
|
||||
#include <nall/stdint.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/utility.hpp>
|
||||
|
||||
#if defined(PLATFORM_X) || defined(PLATFORM_OSX)
|
||||
#include <dlfcn.h>
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <nall/windows/utf8.hpp>
|
||||
#endif
|
||||
|
@ -81,7 +81,7 @@ namespace nall {
|
|||
dlclose((void*)handle);
|
||||
handle = 0;
|
||||
}
|
||||
#elif defined(PLATFORM_WIN)
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
inline bool library::open(const char *name, const char *path) {
|
||||
if(handle) close();
|
||||
string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll");
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#ifndef NALL_DSP_HPP
|
||||
#define NALL_DSP_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#ifdef __SSE__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define NALL_DSP_INTERNAL_HPP
|
||||
#include <nall/dsp/core.hpp>
|
||||
#undef NALL_DSP_INTERNAL_HPP
|
||||
|
|
|
@ -5,24 +5,40 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
//precision: can be float, double or long double
|
||||
#define real float
|
||||
|
||||
struct DSP;
|
||||
|
||||
struct Resampler {
|
||||
DSP &dsp;
|
||||
real frequency;
|
||||
|
||||
virtual void setFrequency() = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual void sample() = 0;
|
||||
Resampler(DSP &dsp) : dsp(dsp) {}
|
||||
};
|
||||
|
||||
struct DSP {
|
||||
enum class Resampler : unsigned {
|
||||
Point,
|
||||
enum class ResampleEngine : unsigned {
|
||||
Nearest,
|
||||
Linear,
|
||||
Cosine,
|
||||
Cubic,
|
||||
Hermite,
|
||||
Average,
|
||||
Sinc,
|
||||
};
|
||||
|
||||
inline void setChannels(unsigned channels);
|
||||
inline void setPrecision(unsigned precision);
|
||||
inline void setFrequency(double frequency); //inputFrequency
|
||||
inline void setVolume(double volume);
|
||||
inline void setBalance(double balance);
|
||||
inline void setFrequency(real frequency); //inputFrequency
|
||||
inline void setVolume(real volume);
|
||||
inline void setBalance(real balance);
|
||||
|
||||
inline void setResampler(Resampler resampler);
|
||||
inline void setResamplerFrequency(double frequency); //outputFrequency
|
||||
inline void setResampler(ResampleEngine resamplingEngine);
|
||||
inline void setResamplerFrequency(real frequency); //outputFrequency
|
||||
|
||||
inline void sample(signed channel[]);
|
||||
inline bool pending();
|
||||
|
@ -33,33 +49,28 @@ struct DSP {
|
|||
inline ~DSP();
|
||||
|
||||
protected:
|
||||
friend class ResampleNearest;
|
||||
friend class ResampleLinear;
|
||||
friend class ResampleCosine;
|
||||
friend class ResampleCubic;
|
||||
friend class ResampleAverage;
|
||||
friend class ResampleHermite;
|
||||
friend class ResampleSinc;
|
||||
|
||||
struct Settings {
|
||||
unsigned channels;
|
||||
unsigned precision;
|
||||
double frequency;
|
||||
double volume;
|
||||
double balance;
|
||||
real frequency;
|
||||
real volume;
|
||||
real balance;
|
||||
|
||||
//internal
|
||||
double intensity;
|
||||
real intensity;
|
||||
real intensityInverse;
|
||||
} settings;
|
||||
|
||||
struct ResamplerSettings {
|
||||
Resampler engine;
|
||||
double frequency;
|
||||
//internal
|
||||
double fraction;
|
||||
double step;
|
||||
} resampler;
|
||||
|
||||
inline void resamplerRun();
|
||||
inline void resamplerWrite(double channel[]);
|
||||
|
||||
inline void resamplePoint();
|
||||
inline void resampleLinear();
|
||||
inline void resampleCosine();
|
||||
inline void resampleCubic();
|
||||
inline void resampleHermite();
|
||||
inline void resampleAverage();
|
||||
Resampler *resampler;
|
||||
inline void write(real channel[]);
|
||||
|
||||
#include "buffer.hpp"
|
||||
Buffer buffer;
|
||||
|
@ -70,14 +81,21 @@ protected:
|
|||
inline signed clamp(const unsigned bits, const signed x);
|
||||
};
|
||||
|
||||
#include "resample/nearest.hpp"
|
||||
#include "resample/linear.hpp"
|
||||
#include "resample/cosine.hpp"
|
||||
#include "resample/cubic.hpp"
|
||||
#include "resample/hermite.hpp"
|
||||
#include "resample/average.hpp"
|
||||
#include "resample/sinc.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
void DSP::sample(signed channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
buffer.write(c) = (double)channel[c] / settings.intensity;
|
||||
buffer.write(c) = (real)channel[c] * settings.intensityInverse;
|
||||
}
|
||||
buffer.wroffset++;
|
||||
resamplerRun();
|
||||
resampler->sample();
|
||||
}
|
||||
|
||||
bool DSP::pending() {
|
||||
|
@ -94,31 +112,13 @@ void DSP::read(signed channel[]) {
|
|||
output.rdoffset++;
|
||||
}
|
||||
|
||||
void DSP::resamplerRun() {
|
||||
switch(resampler.engine) {
|
||||
case Resampler::Point: return resamplePoint();
|
||||
case Resampler::Linear: return resampleLinear();
|
||||
case Resampler::Cosine: return resampleCosine();
|
||||
case Resampler::Cubic: return resampleCubic();
|
||||
case Resampler::Hermite: return resampleHermite();
|
||||
case Resampler::Average: return resampleAverage();
|
||||
}
|
||||
}
|
||||
|
||||
void DSP::resamplerWrite(double channel[]) {
|
||||
void DSP::write(real channel[]) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) = channel[c];
|
||||
}
|
||||
output.wroffset++;
|
||||
}
|
||||
|
||||
#include "resample/point.hpp"
|
||||
#include "resample/linear.hpp"
|
||||
#include "resample/cosine.hpp"
|
||||
#include "resample/cubic.hpp"
|
||||
#include "resample/hermite.hpp"
|
||||
#include "resample/average.hpp"
|
||||
|
||||
void DSP::adjustVolume() {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.read(c) *= settings.volume;
|
||||
|
@ -138,25 +138,30 @@ signed DSP::clamp(const unsigned bits, const signed x) {
|
|||
}
|
||||
|
||||
void DSP::clear() {
|
||||
resampler.fraction = 0.0;
|
||||
buffer.clear();
|
||||
output.clear();
|
||||
resampler->clear();
|
||||
}
|
||||
|
||||
DSP::DSP() {
|
||||
setResampler(ResampleEngine::Hermite);
|
||||
setResamplerFrequency(44100.0);
|
||||
|
||||
setChannels(2);
|
||||
setPrecision(16);
|
||||
setFrequency(44100.0);
|
||||
setVolume(1.0);
|
||||
setBalance(0.0);
|
||||
setResampler(Resampler::Hermite);
|
||||
setResamplerFrequency(44100.0);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
DSP::~DSP() {
|
||||
if(resampler) delete resampler;
|
||||
}
|
||||
|
||||
#undef real
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,31 +1,72 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::resampleAverage() {
|
||||
struct ResampleAverage : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
inline void sampleLinear();
|
||||
ResampleAverage(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleAverage::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleAverage::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleAverage::sample() {
|
||||
//can only average if input frequency >= output frequency
|
||||
if(resampler.step < 1.0) return resampleHermite();
|
||||
if(step < 1.0) return sampleLinear();
|
||||
|
||||
resampler.fraction += 1.0;
|
||||
fraction += 1.0;
|
||||
|
||||
double scalar = 1.0;
|
||||
if(resampler.fraction > resampler.step) scalar = 1.0 - (resampler.fraction - resampler.step);
|
||||
real scalar = 1.0;
|
||||
if(fraction > step) scalar = 1.0 - (fraction - step);
|
||||
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) += buffer.read(c) * scalar;
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) += dsp.buffer.read(c) * scalar;
|
||||
}
|
||||
|
||||
if(resampler.fraction >= resampler.step) {
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) /= resampler.step;
|
||||
if(fraction >= step) {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) /= step;
|
||||
}
|
||||
output.wroffset++;
|
||||
dsp.output.wroffset++;
|
||||
|
||||
resampler.fraction -= resampler.step;
|
||||
for(unsigned c = 0; c < settings.channels; c++) {
|
||||
output.write(c) = buffer.read(c) * resampler.fraction;
|
||||
fraction -= step;
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) = dsp.buffer.read(c) * fraction;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.rdoffset++;
|
||||
dsp.buffer.rdoffset++;
|
||||
}
|
||||
|
||||
void ResampleAverage::sampleLinear() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,25 +1,44 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::resampleCosine() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[settings.channels];
|
||||
struct ResampleCosine : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleCosine(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -1);
|
||||
double b = buffer.read(n, -0);
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
double mu = resampler.fraction;
|
||||
void ResampleCosine::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleCosine::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleCosine::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
mu = (1.0 - cos(mu * 3.14159265)) / 2.0;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
buffer.rdoffset++;
|
||||
resampler.fraction -= 1.0;
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,31 +1,50 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::resampleCubic() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[settings.channels];
|
||||
struct ResampleCubic : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleCubic(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -3);
|
||||
double b = buffer.read(n, -2);
|
||||
double c = buffer.read(n, -1);
|
||||
double d = buffer.read(n, -0);
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
double mu = resampler.fraction;
|
||||
void ResampleCubic::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
double A = d - c - a + b;
|
||||
double B = a - b - A;
|
||||
double C = c - a;
|
||||
double D = b;
|
||||
void ResampleCubic::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleCubic::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -3);
|
||||
real b = dsp.buffer.read(n, -2);
|
||||
real c = dsp.buffer.read(n, -1);
|
||||
real d = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
real A = d - c - a + b;
|
||||
real B = a - b - A;
|
||||
real C = c - a;
|
||||
real D = b;
|
||||
|
||||
channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D;
|
||||
}
|
||||
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
buffer.rdoffset++;
|
||||
resampler.fraction -= 1.0;
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,21 +1,40 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::resampleHermite() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[settings.channels];
|
||||
struct ResampleHermite : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleHermite(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -3);
|
||||
double b = buffer.read(n, -2);
|
||||
double c = buffer.read(n, -1);
|
||||
double d = buffer.read(n, -0);
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
const double tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||
const double bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||
void ResampleHermite::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
double mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
void ResampleHermite::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
mu1 = resampler.fraction;
|
||||
void ResampleHermite::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -3);
|
||||
real b = dsp.buffer.read(n, -2);
|
||||
real c = dsp.buffer.read(n, -1);
|
||||
real d = dsp.buffer.read(n, -0);
|
||||
|
||||
const real tension = 0.0; //-1 = low, 0 = normal, +1 = high
|
||||
const real bias = 0.0; //-1 = left, 0 = even, +1 = right
|
||||
|
||||
real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu1 = fraction;
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
|
@ -32,12 +51,12 @@ void DSP::resampleHermite() {
|
|||
channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
buffer.rdoffset++;
|
||||
resampler.fraction -= 1.0;
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,600 @@
|
|||
// If these types are changed to anything other than "float", you should comment out the SSE detection directives below
|
||||
// so that the SSE code is not used.
|
||||
|
||||
typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16
|
||||
typedef float resample_samp_t;
|
||||
|
||||
|
||||
// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE.
|
||||
#define RESAMPLE_SSEREGPARM
|
||||
|
||||
#if defined(__SSE__)
|
||||
#define SINCRESAMPLE_USE_SSE 1
|
||||
#ifndef __x86_64__
|
||||
#undef RESAMPLE_SSEREGPARM
|
||||
#define RESAMPLE_SSEREGPARM __attribute__((sseregparm))
|
||||
#endif
|
||||
#else
|
||||
// TODO: altivec here
|
||||
#endif
|
||||
|
||||
namespace ResampleUtility
|
||||
{
|
||||
inline void kaiser_window(double* io, int count, double beta);
|
||||
inline void gen_sinc(double* out, int size, double cutoff, double kaiser);
|
||||
inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser);
|
||||
inline void normalize(double* io, int size, double gain = 1.0);
|
||||
|
||||
inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2
|
||||
}
|
||||
|
||||
class SincResampleHR
|
||||
{
|
||||
private:
|
||||
|
||||
inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d);
|
||||
|
||||
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||
inline bool output_avail(void);
|
||||
|
||||
private:
|
||||
|
||||
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count);
|
||||
|
||||
unsigned ratio;
|
||||
unsigned num_convolutions;
|
||||
|
||||
resample_coeff_t *coeffs;
|
||||
std::vector<unsigned char> coeffs_mem;
|
||||
|
||||
// second half of ringbuffer should be copy of first half.
|
||||
resample_samp_t *rb;
|
||||
std::vector<unsigned char> rb_mem;
|
||||
|
||||
signed rb_readpos;
|
||||
signed rb_writepos;
|
||||
signed rb_in;
|
||||
signed rb_eff_size;
|
||||
|
||||
friend class SincResample;
|
||||
};
|
||||
|
||||
class SincResample
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
QUALITY_LOW = 0,
|
||||
QUALITY_MEDIUM = 2,
|
||||
QUALITY_HIGH = 4
|
||||
};
|
||||
|
||||
inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH);
|
||||
|
||||
inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM;
|
||||
inline resample_samp_t read(void) RESAMPLE_SSEREGPARM;
|
||||
inline bool output_avail(void);
|
||||
|
||||
private:
|
||||
|
||||
inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min);
|
||||
|
||||
inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM;
|
||||
|
||||
unsigned num_convolutions;
|
||||
unsigned num_phases;
|
||||
|
||||
unsigned step_int;
|
||||
double step_fract;
|
||||
|
||||
double input_pos_fract;
|
||||
|
||||
|
||||
std::vector<resample_coeff_t *> coeffs; // Pointers into coeff_mem.
|
||||
std::vector<unsigned char> coeff_mem;
|
||||
|
||||
|
||||
std::vector<resample_samp_t> rb; // second half should be copy of first half.
|
||||
signed rb_readpos;
|
||||
signed rb_writepos;
|
||||
signed rb_in;
|
||||
|
||||
bool hr_used;
|
||||
SincResampleHR hr;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Code:
|
||||
//
|
||||
//#include "resample.hpp"
|
||||
|
||||
#if 0
|
||||
namespace bit
|
||||
{
|
||||
inline unsigned round(unsigned x) {
|
||||
if((x & (x - 1)) == 0) return x;
|
||||
while(x & (x - 1)) x &= x - 1;
|
||||
return x << 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d)
|
||||
{
|
||||
const unsigned align_boundary = 16;
|
||||
std::vector<double> coeffs_tmp;
|
||||
double cutoff; // 1.0 = f/2
|
||||
|
||||
ratio = ratio_arg;
|
||||
|
||||
//num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even
|
||||
num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1);
|
||||
|
||||
cutoff = (1.0 / ratio) - (d / num_convolutions);
|
||||
|
||||
//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff);
|
||||
assert(num_convolutions > ratio);
|
||||
|
||||
|
||||
// Generate windowed sinc of POWER
|
||||
coeffs_tmp.resize(num_convolutions);
|
||||
//ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||
ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta);
|
||||
ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions);
|
||||
|
||||
// Copy from coeffs_tmp to coeffs~
|
||||
// We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really
|
||||
// exist are allocated, zero'd mem.
|
||||
|
||||
coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1));
|
||||
coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary);
|
||||
|
||||
|
||||
for(unsigned i = 0; i < num_convolutions; i++)
|
||||
coeffs[i] = coeffs_tmp[i];
|
||||
|
||||
rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1;
|
||||
rb_readpos = 0;
|
||||
rb_writepos = 0;
|
||||
rb_in = 0;
|
||||
|
||||
rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1));
|
||||
rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary);
|
||||
}
|
||||
|
||||
|
||||
inline bool SincResampleHR::output_avail(void)
|
||||
{
|
||||
return(rb_in >= (signed)num_convolutions);
|
||||
}
|
||||
|
||||
inline void SincResampleHR::write(resample_samp_t sample)
|
||||
{
|
||||
assert(!output_avail());
|
||||
|
||||
rb[rb_writepos] = sample;
|
||||
rb[rb_writepos + rb_eff_size] = sample;
|
||||
rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1);
|
||||
rb_in++;
|
||||
}
|
||||
|
||||
resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count)
|
||||
{
|
||||
#if SINCRESAMPLE_USE_SSE
|
||||
__m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
|
||||
resample_samp_t accum;
|
||||
|
||||
for(unsigned c = 0; c < count; c += 8)
|
||||
{
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
__m128 co[2];
|
||||
__m128 w[2];
|
||||
|
||||
co[i] = _mm_load_ps(&coeff[c + i * 4]);
|
||||
w[i] = _mm_load_ps(&wave[c + i * 4]);
|
||||
|
||||
w[i] = _mm_mul_ps(w[i], co[i]);
|
||||
|
||||
accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3]));
|
||||
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||
|
||||
_mm_store_ss(&accum, accum_vec);
|
||||
|
||||
return accum;
|
||||
#else
|
||||
resample_samp_t accum[4] = { 0, 0, 0, 0 };
|
||||
|
||||
for(unsigned c = 0; c < count; c+= 4)
|
||||
{
|
||||
accum[0] += wave[c + 0] * coeff[c + 0];
|
||||
accum[1] += wave[c + 1] * coeff[c + 1];
|
||||
accum[2] += wave[c + 2] * coeff[c + 2];
|
||||
accum[3] += wave[c + 3] * coeff[c + 3];
|
||||
}
|
||||
|
||||
return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may...
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
resample_samp_t SincResampleHR::read(void)
|
||||
{
|
||||
assert(output_avail());
|
||||
resample_samp_t ret;
|
||||
|
||||
ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions);
|
||||
|
||||
rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1);
|
||||
rb_in -= ratio;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality)
|
||||
{
|
||||
const struct
|
||||
{
|
||||
double beta;
|
||||
double d;
|
||||
unsigned pn_nume;
|
||||
unsigned phases_min;
|
||||
} qtab[5] =
|
||||
{
|
||||
{ 5.658, 3.62, 4096, 4 },
|
||||
{ 6.764, 4.32, 8192, 4 },
|
||||
{ 7.865, 5.0, 16384, 8 },
|
||||
{ 8.960, 5.7, 32768, 16 },
|
||||
{ 10.056, 6.4, 65536, 32 }
|
||||
};
|
||||
|
||||
// Sanity checks
|
||||
assert(ceil(input_rate) > 0);
|
||||
assert(ceil(output_rate) > 0);
|
||||
assert(ceil(input_rate / output_rate) <= 1024);
|
||||
assert(ceil(output_rate / input_rate) <= 1024);
|
||||
|
||||
// The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when
|
||||
// upsampling.
|
||||
assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96);
|
||||
assert(quality >= 0 && quality <= 4);
|
||||
|
||||
hr_used = false;
|
||||
|
||||
#if 1
|
||||
// Round down to the nearest multiple of 4(so wave buffer remains aligned)
|
||||
// It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2
|
||||
// aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately
|
||||
// to virtually eliminate over-attenuation.
|
||||
unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3;
|
||||
|
||||
if(ioratio_rd >= 8)
|
||||
{
|
||||
hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4);
|
||||
hr_used = true;
|
||||
|
||||
input_rate /= ioratio_rd;
|
||||
}
|
||||
#endif
|
||||
|
||||
Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min);
|
||||
}
|
||||
|
||||
void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min)
|
||||
{
|
||||
const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2.
|
||||
const unsigned max_mult_minus1 = (max_mult_atatime - 1);
|
||||
const unsigned conv_alignment_bytes = 16; // must be power of 2
|
||||
const double input_to_output_ratio = input_rate / output_rate;
|
||||
const double output_to_input_ratio = output_rate / input_rate;
|
||||
double cutoff; // 1.0 = input_rate / 2
|
||||
std::vector<double> coeff_init_buffer;
|
||||
|
||||
// Round up num_convolutions to be even.
|
||||
if(output_rate > input_rate)
|
||||
num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1;
|
||||
else
|
||||
num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1;
|
||||
|
||||
if(output_rate > input_rate) // Upsampling
|
||||
cutoff = desired_bandwidth;
|
||||
else // Downsampling
|
||||
cutoff = output_to_input_ratio * desired_bandwidth;
|
||||
|
||||
// Round up to be even.
|
||||
num_phases = (std::max<unsigned>(pn_nume / num_convolutions, phases_min) + 1) &~1;
|
||||
|
||||
// Adjust cutoff to account for the multiple phases.
|
||||
cutoff = cutoff / num_phases;
|
||||
|
||||
assert((num_convolutions & 1) == 0);
|
||||
assert((num_phases & 1) == 0);
|
||||
|
||||
// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases,
|
||||
// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes));
|
||||
|
||||
coeff_init_buffer.resize(num_phases * num_convolutions);
|
||||
|
||||
coeffs.resize(num_phases + 1 + 1);
|
||||
|
||||
coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes);
|
||||
|
||||
// Assign aligned pointers into coeff_mem
|
||||
{
|
||||
resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes);
|
||||
|
||||
for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++)
|
||||
{
|
||||
coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase);
|
||||
}
|
||||
}
|
||||
|
||||
ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta);
|
||||
ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases);
|
||||
|
||||
// Reorder coefficients to allow for more efficient convolution.
|
||||
for(int phase = -1; phase < ((int)num_phases + 1); phase++)
|
||||
{
|
||||
for(int conv = 0; conv < (int)num_convolutions; conv++)
|
||||
{
|
||||
double coeff;
|
||||
|
||||
if(phase == -1 && conv == 0)
|
||||
coeff = 0;
|
||||
else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1))
|
||||
coeff = 0;
|
||||
else
|
||||
coeff = coeff_init_buffer[conv * num_phases + phase];
|
||||
|
||||
coeffs[phase + 1][conv] = coeff;
|
||||
}
|
||||
}
|
||||
|
||||
// Free a bit of mem
|
||||
coeff_init_buffer.resize(0);
|
||||
|
||||
step_int = floor(input_to_output_ratio);
|
||||
step_fract = input_to_output_ratio - step_int;
|
||||
|
||||
input_pos_fract = 0;
|
||||
|
||||
// Do NOT use rb.size() later in the code, since it'll include the padding.
|
||||
// We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time
|
||||
// rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half,
|
||||
// since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to
|
||||
// prevent illegal memory accesses.
|
||||
rb.resize(num_convolutions * 2 + max_mult_minus1);
|
||||
|
||||
rb_readpos = 0;
|
||||
rb_writepos = 0;
|
||||
rb_in = 0;
|
||||
}
|
||||
|
||||
resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count)
|
||||
{
|
||||
resample_samp_t accum = 0;
|
||||
#if SINCRESAMPLE_USE_SSE
|
||||
__m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
__m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) };
|
||||
|
||||
for(unsigned c = 0; c < count; c += 8) //8) //4)
|
||||
{
|
||||
__m128 coeff_a[2];
|
||||
__m128 coeff_b[2];
|
||||
__m128 w[2];
|
||||
__m128 result_a[2], result_b[2];
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]);
|
||||
coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]);
|
||||
w[i] = _mm_loadu_ps(&wave[c + (i * 4)]);
|
||||
|
||||
result_a[i] = _mm_mul_ps(coeff_a[i], w[i]);
|
||||
result_b[i] = _mm_mul_ps(coeff_b[i], w[i]);
|
||||
|
||||
accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]);
|
||||
accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__m128 accum_vec, av_a, av_b;
|
||||
__m128 mult_a_vec = _mm_set1_ps(1.0 - ffract);
|
||||
__m128 mult_b_vec = _mm_set1_ps(ffract);
|
||||
|
||||
av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1]));
|
||||
av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1]));
|
||||
|
||||
accum_vec = _mm_add_ps(av_a, av_b);
|
||||
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
|
||||
accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6)));
|
||||
|
||||
_mm_store_ss(&accum, accum_vec);
|
||||
#else
|
||||
resample_coeff_t mult_a = 1.0 - ffract;
|
||||
resample_coeff_t mult_b = ffract;
|
||||
|
||||
for(unsigned c = 0; c < count; c += 4)
|
||||
{
|
||||
accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b);
|
||||
accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b);
|
||||
accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b);
|
||||
accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b);
|
||||
}
|
||||
#endif
|
||||
|
||||
return accum;
|
||||
}
|
||||
|
||||
inline bool SincResample::output_avail(void)
|
||||
{
|
||||
return(rb_in >= (int)num_convolutions);
|
||||
}
|
||||
|
||||
resample_samp_t SincResample::read(void)
|
||||
{
|
||||
assert(output_avail());
|
||||
double phase = input_pos_fract * num_phases - 0.5;
|
||||
signed phase_int = (signed)floor(phase);
|
||||
double phase_fract = phase - phase_int;
|
||||
unsigned phase_a = num_phases - 1 - phase_int;
|
||||
unsigned phase_b = phase_a - 1;
|
||||
resample_samp_t ret;
|
||||
|
||||
ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions);
|
||||
|
||||
unsigned int_increment = step_int;
|
||||
|
||||
input_pos_fract += step_fract;
|
||||
int_increment += floor(input_pos_fract);
|
||||
input_pos_fract -= floor(input_pos_fract);
|
||||
|
||||
rb_readpos = (rb_readpos + int_increment) % num_convolutions;
|
||||
rb_in -= int_increment;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void SincResample::write(resample_samp_t sample)
|
||||
{
|
||||
assert(!output_avail());
|
||||
|
||||
if(hr_used)
|
||||
{
|
||||
hr.write(sample);
|
||||
|
||||
if(hr.output_avail())
|
||||
{
|
||||
sample = hr.read();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rb[rb_writepos + 0 * num_convolutions] = sample;
|
||||
rb[rb_writepos + 1 * num_convolutions] = sample;
|
||||
rb_writepos = (rb_writepos + 1) % num_convolutions;
|
||||
rb_in++;
|
||||
}
|
||||
|
||||
void ResampleUtility::kaiser_window( double* io, int count, double beta)
|
||||
{
|
||||
int const accuracy = 24; //16; //12;
|
||||
|
||||
double* end = io + count;
|
||||
|
||||
double beta2 = beta * beta * (double) -0.25;
|
||||
double to_fract = beta2 / ((double) count * count);
|
||||
double i = 0;
|
||||
double rescale = 0; // Doesn't need an initializer, to shut up gcc
|
||||
|
||||
for ( ; io < end; ++io, i += 1 )
|
||||
{
|
||||
double x = i * i * to_fract - beta2;
|
||||
double u = x;
|
||||
double k = x + 1;
|
||||
|
||||
double n = 2;
|
||||
do
|
||||
{
|
||||
u *= x / (n * n);
|
||||
n += 1;
|
||||
k += u;
|
||||
}
|
||||
while ( k <= u * (1 << accuracy) );
|
||||
|
||||
if ( !i )
|
||||
rescale = 1 / k; // otherwise values get large
|
||||
|
||||
*io *= k * rescale;
|
||||
}
|
||||
}
|
||||
|
||||
void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser)
|
||||
{
|
||||
assert( size % 2 == 0 ); // size must be even
|
||||
|
||||
int const half_size = size / 2;
|
||||
double* const mid = &out [half_size];
|
||||
|
||||
// Generate right half of sinc
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
{
|
||||
double angle = (i * 2 + 1) * (M_PI / 2);
|
||||
mid [i] = sin( angle * cutoff ) / angle;
|
||||
}
|
||||
|
||||
kaiser_window( mid, half_size, kaiser );
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < half_size; i++ )
|
||||
out [i] = mid [half_size - 1 - i];
|
||||
}
|
||||
|
||||
void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser)
|
||||
{
|
||||
assert( size % 2 == 1); // size must be odd
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
if(i == (size / 2))
|
||||
out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||
else
|
||||
out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2));
|
||||
|
||||
// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1));
|
||||
//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1));
|
||||
|
||||
// printf("%d %f\n", i, out[i]);
|
||||
}
|
||||
|
||||
kaiser_window(&out[size / 2], size / 2 + 1, kaiser);
|
||||
|
||||
// Mirror for left half
|
||||
for ( int i = 0; i < size / 2; i++ )
|
||||
out [i] = out [size - 1 - i];
|
||||
|
||||
}
|
||||
|
||||
void ResampleUtility::normalize(double* io, int size, double gain)
|
||||
{
|
||||
double sum = 0;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
sum += io [i];
|
||||
|
||||
double scale = gain / sum;
|
||||
for ( int i = 0; i < size; i++ )
|
||||
io [i] *= scale;
|
||||
}
|
||||
|
||||
void* ResampleUtility::make_aligned(void* ptr, unsigned boundary)
|
||||
{
|
||||
unsigned char* null_ptr = (unsigned char *)NULL;
|
||||
unsigned char* uc_ptr = (unsigned char *)ptr;
|
||||
|
||||
uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1);
|
||||
|
||||
//while((uc_ptr - null_ptr) & (boundary - 1))
|
||||
// uc_ptr++;
|
||||
|
||||
//printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr);
|
||||
|
||||
assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr));
|
||||
|
||||
return uc_ptr;
|
||||
}
|
|
@ -1,24 +1,43 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
void DSP::resampleLinear() {
|
||||
while(resampler.fraction <= 1.0) {
|
||||
double channel[settings.channels];
|
||||
struct ResampleLinear : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleLinear(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
for(unsigned n = 0; n < settings.channels; n++) {
|
||||
double a = buffer.read(n, -1);
|
||||
double b = buffer.read(n, -0);
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
double mu = resampler.fraction;
|
||||
void ResampleLinear::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleLinear::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleLinear::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = a * (1.0 - mu) + b * mu;
|
||||
}
|
||||
|
||||
resamplerWrite(channel);
|
||||
resampler.fraction += resampler.step;
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
buffer.rdoffset++;
|
||||
resampler.fraction -= 1.0;
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
struct ResampleNearest : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
ResampleNearest(DSP &dsp) : Resampler(dsp) {}
|
||||
|
||||
real fraction;
|
||||
real step;
|
||||
};
|
||||
|
||||
void ResampleNearest::setFrequency() {
|
||||
fraction = 0.0;
|
||||
step = dsp.settings.frequency / frequency;
|
||||
}
|
||||
|
||||
void ResampleNearest::clear() {
|
||||
fraction = 0.0;
|
||||
}
|
||||
|
||||
void ResampleNearest::sample() {
|
||||
while(fraction <= 1.0) {
|
||||
real channel[dsp.settings.channels];
|
||||
|
||||
for(unsigned n = 0; n < dsp.settings.channels; n++) {
|
||||
real a = dsp.buffer.read(n, -1);
|
||||
real b = dsp.buffer.read(n, -0);
|
||||
|
||||
real mu = fraction;
|
||||
|
||||
channel[n] = mu < 0.5 ? a : b;
|
||||
}
|
||||
|
||||
dsp.write(channel);
|
||||
fraction += step;
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
fraction -= 1.0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
#ifdef NALL_DSP_INTERNAL_HPP
|
||||
|
||||
#include "lib/sinc.hpp"
|
||||
|
||||
struct ResampleSinc : Resampler {
|
||||
inline void setFrequency();
|
||||
inline void clear();
|
||||
inline void sample();
|
||||
inline ResampleSinc(DSP &dsp);
|
||||
|
||||
private:
|
||||
inline void remakeSinc();
|
||||
SincResample *sinc_resampler[8];
|
||||
};
|
||||
|
||||
void ResampleSinc::setFrequency() {
|
||||
remakeSinc();
|
||||
}
|
||||
|
||||
void ResampleSinc::clear() {
|
||||
remakeSinc();
|
||||
}
|
||||
|
||||
void ResampleSinc::sample() {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
sinc_resampler[c]->write(dsp.buffer.read(c));
|
||||
}
|
||||
|
||||
if(sinc_resampler[0]->output_avail()) {
|
||||
do {
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
dsp.output.write(c) = sinc_resampler[c]->read();
|
||||
}
|
||||
dsp.output.wroffset++;
|
||||
} while(sinc_resampler[0]->output_avail());
|
||||
}
|
||||
|
||||
dsp.buffer.rdoffset++;
|
||||
}
|
||||
|
||||
ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) {
|
||||
for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0;
|
||||
}
|
||||
|
||||
void ResampleSinc::remakeSinc() {
|
||||
assert(dsp.settings.channels < 8);
|
||||
|
||||
for(unsigned c = 0; c < dsp.settings.channels; c++) {
|
||||
if(sinc_resampler[c]) delete sinc_resampler[c];
|
||||
sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,30 +10,41 @@ void DSP::setChannels(unsigned channels) {
|
|||
void DSP::setPrecision(unsigned precision) {
|
||||
settings.precision = precision;
|
||||
settings.intensity = 1 << (settings.precision - 1);
|
||||
settings.intensityInverse = 1.0 / settings.intensity;
|
||||
}
|
||||
|
||||
void DSP::setFrequency(double frequency) {
|
||||
void DSP::setFrequency(real frequency) {
|
||||
settings.frequency = frequency;
|
||||
resampler.fraction = 0;
|
||||
resampler.step = settings.frequency / resampler.frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
void DSP::setVolume(double volume) {
|
||||
void DSP::setVolume(real volume) {
|
||||
settings.volume = volume;
|
||||
}
|
||||
|
||||
void DSP::setBalance(double balance) {
|
||||
void DSP::setBalance(real balance) {
|
||||
settings.balance = balance;
|
||||
}
|
||||
|
||||
void DSP::setResampler(Resampler engine) {
|
||||
resampler.engine = engine;
|
||||
void DSP::setResampler(ResampleEngine engine) {
|
||||
if(resampler) delete resampler;
|
||||
|
||||
switch(engine) {
|
||||
case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return;
|
||||
case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return;
|
||||
case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return;
|
||||
case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return;
|
||||
case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return;
|
||||
case ResampleEngine::Average: resampler = new ResampleAverage(*this); return;
|
||||
case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void DSP::setResamplerFrequency(double frequency) {
|
||||
resampler.frequency = frequency;
|
||||
resampler.fraction = 0;
|
||||
resampler.step = settings.frequency / resampler.frequency;
|
||||
void DSP::setResamplerFrequency(real frequency) {
|
||||
resampler->frequency = frequency;
|
||||
resampler->setFrequency();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef NALL_ENDIAN_HPP
|
||||
#define NALL_ENDIAN_HPP
|
||||
|
||||
#if !defined(ARCH_MSB)
|
||||
#include <nall/intrinsics.hpp>
|
||||
|
||||
#if defined(ENDIAN_LSB)
|
||||
//little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201
|
||||
#define order_lsb2(a,b) a,b
|
||||
#define order_lsb3(a,b,c) a,b,c
|
||||
|
@ -17,7 +19,7 @@
|
|||
#define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a
|
||||
#define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a
|
||||
#else
|
||||
#elif defined(ENDIAN_MSB)
|
||||
//big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304
|
||||
#define order_lsb2(a,b) b,a
|
||||
#define order_lsb3(a,b,c) c,b,a
|
||||
|
@ -33,6 +35,8 @@
|
|||
#define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f
|
||||
#define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g
|
||||
#define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h
|
||||
#else
|
||||
#error "Unknown endian. Please specify in nall/intrinsics.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,19 +36,19 @@ namespace nall {
|
|||
public:
|
||||
operator bool() const { return callback; }
|
||||
R operator()(P... p) const { return (*callback)(std::forward<P>(p)...); }
|
||||
void reset() { if(callback) { delete callback; callback = 0; } }
|
||||
void reset() { if(callback) { delete callback; callback = nullptr; } }
|
||||
|
||||
function& operator=(const function &source) {
|
||||
if(this != &source) {
|
||||
if(callback) { delete callback; callback = 0; }
|
||||
if(callback) { delete callback; callback = nullptr; }
|
||||
if(source.callback) callback = source.callback->copy();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
function(const function &source) : callback(0) { operator=(source); }
|
||||
function() : callback(0) {}
|
||||
function(void *function) : callback(0) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(const function &source) : callback(nullptr) { operator=(source); }
|
||||
function() : callback(nullptr) {}
|
||||
function(void *function) : callback(nullptr) { if(function) callback = new global((R (*)(P...))function); }
|
||||
function(R (*function)(P...)) { callback = new global(function); }
|
||||
template<typename C> function(R (C::*function)(P...), C *object) { callback = new member<C>(function, object); }
|
||||
template<typename C> function(R (C::*function)(P...) const, C *object) { callback = new member<C>((R (C::*)(P...))function, object); }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue