Compare commits

...
62 Commits
Author SHA1 Message Date
Themaister 2166db91e2 Fix awkward compiler warning. 2011-05-19 22:51:23 +02:00
Themaister 4f50b6a749 Clarify default path in $HOME. 2011-05-19 15:51:30 +02:00
Themaister 676f659306 Fix Makefile to work when mkdir -p is being stupid. 2011-05-19 15:40:39 +02:00
Themaister 2f858b7948 Clean up ugly inline stuff. :\ 2011-05-18 22:40:42 +02:00
Themaister 36d9adbc0f *Shrug* windows. Works at least ... :D 2011-05-18 22:11:34 +02:00
Themaister 9473b392df TGA loader (nolib). 2011-05-18 21:49:23 +02:00
Themaister 3bf050d5aa Use MultiTexCoord1 for image coords. 2011-05-18 20:38:04 +02:00
Themaister 8a0ccc99df Make sure we don't load too many pictures. 2011-05-18 20:27:27 +02:00
Themaister c938f3d8de Add lookup texture support. :D yay. 2011-05-18 20:22:27 +02:00
Themaister d1c7b774f0 Remove default buttons. 2011-05-18 14:07:31 +02:00
Themaister 6b6877afc1 DSP config option to ssnes-joyconfig. 2011-05-17 19:41:57 +02:00
Themaister c370cba5b5 Update config. 2011-05-17 19:37:05 +02:00
Themaister 05d62a93c9 Update gitignore. 2011-05-17 19:34:57 +02:00
Themaister 24cb2e6844 Update filter a bit. Add a Makefile. 2011-05-17 19:34:24 +02:00
Themaister 7f1cd62c8a Update DSP API for config callback.
Clean up API a bit to have separate
SSNES_*_API_VERSION defines.
2011-05-17 19:20:41 +02:00
Themaister 5f5013871a Should fix tilde/grave key. 2011-05-17 18:52:00 +02:00
Themaister aebe6bcd23 Add example SSE3-optimized IIR filter. 2011-05-17 02:45:10 +02:00
Themaister ac21fb81e2 Prepare for 0.5 release. 2011-05-15 22:18:36 +02:00
Themaister 74afe7f95b Win32 doesn't like %g in strftime ... 2011-05-15 22:01:24 +02:00
Themaister c8161a31d6 Everything implemented. 2011-05-15 21:57:16 +02:00
Themaister 628217c6ae Fix up screenshot formatting. 2011-05-15 17:30:00 +02:00
Themaister efed98d5b6 Yay, works :) 2011-05-15 17:16:29 +02:00
Themaister 29addd605e Start adding screenshot support. 2011-05-15 16:54:43 +02:00
Themaister 0c121571b3 Update TODO. 2011-05-15 16:17:57 +02:00
Themaister 6244671b04 Small API cleanup. 2011-05-15 15:57:47 +02:00
Themaister 21b046cdd1 Add more binds to X11 input. 2011-05-15 15:54:46 +02:00
Themaister b32224d406 Check for API mismatch. 2011-05-15 01:49:37 +02:00
Themaister 5d5865cfd8 Add external API for audio driver. 2011-05-15 01:46:11 +02:00
Themaister 4a78f2fef1 Add a clarifying comment. 2011-05-14 20:53:38 +02:00
Themaister b521becd3c Fix up external video lib a bit. 2011-05-14 20:52:35 +02:00
Themaister f10ed12b74 Fix doc bug. 2011-05-13 22:05:16 +02:00
Themaister 8d051ab413 Fix up case where plugin resamples. 2011-05-13 21:43:32 +02:00
Themaister 4cc17a74f7 Clean up error handling a bit. 2011-05-13 21:22:37 +02:00
Themaister 5ae4235545 Some fixes. 2011-05-13 21:19:05 +02:00
Themaister 8ceb8225ad DSP plugin interface, weeee :D 2011-05-13 21:05:28 +02:00
Themaister 20a3087d1c Make input_scale more clear. 2011-05-13 19:42:37 +02:00
Themaister 1f45dc512b 80-cols comments. 2011-05-12 01:30:30 +02:00
Themaister 13dee59fc8 Fix some more ... :v 2011-05-12 00:35:29 +02:00
Themaister 5f78063449 Fix up call conventions, etc. 2011-05-12 00:34:08 +02:00
Themaister ea6bd8c5c5 Fix up Win32 build a bit, and a crash. :D 2011-05-11 22:53:54 +02:00
Themaister bdc85f2def Fix up Win32 build. 2011-05-11 21:37:50 +02:00
Themaister efa731fc6d Fix inconsistency with ext input. 2011-05-11 21:12:14 +02:00
Themaister 80d50b046d Fix crash. 2011-05-11 18:47:36 +02:00
Themaister 1855a7ea0d Add driver stuff. 2011-05-11 18:40:03 +02:00
Themaister f5c11071be Start hooking up input driver. 2011-05-11 18:26:00 +02:00
Themaister 4b9e46ff06 Update config. 2011-05-11 17:57:31 +02:00
Themaister 75d618140b Some more work on external API. 2011-05-11 17:52:16 +02:00
Themaister 59c6de69b0 Frame counter for shaders. Not in spec currently. 2011-05-11 13:24:59 +02:00
Themaister dc20c58189 Update path. 2011-05-11 12:19:33 +02:00
Themaister c6eecf706a Add Win64 Makefile. 2011-05-11 12:13:55 +02:00
Themaister f2ee22d259 Start working on video interface. 2011-05-05 20:23:08 +02:00
Themaister b48322222a Add overscan cropping. 2011-05-05 14:13:12 +02:00
Themaister bbf5a1ee72 Should fix aspect ratio calculation. 2011-05-05 11:38:57 +02:00
Themaister c0851877bf Update TODO. 2011-05-05 00:13:51 +02:00
Themaister 51d96676af Update TODO list. 2011-05-04 22:26:37 +02:00
Themaister 9f3b86e893 Update joyconfig. 2011-05-04 22:25:27 +02:00
Themaister ac85037fc1 And some more ... 2011-05-04 22:16:25 +02:00
Themaister fa8e948e3e Some additional keybinds. 2011-05-04 22:10:17 +02:00
Themaister 252cce07a6 Update TODO again. 2011-05-01 23:41:39 +02:00
Themaister 17581106ba Update TODO. 2011-05-01 17:10:26 +02:00
Themaister 1bd8424fb4 A TODO list to keep track of things. 2011-05-01 17:05:49 +02:00
Themaister 23db525296 Fix nasty cheat handling bug. 2011-04-29 12:36:28 +02:00
45 changed files with 2377 additions and 194 deletions

No files matched your search

+2
View File
@@ -1,4 +1,6 @@
*.o
*.so
*.dll
ssnes
/config.h
/config.mk
+17 -10
View File
@@ -2,7 +2,7 @@ include config.mk
TARGET = ssnes tools/ssnes-joyconfig
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o
HEADERS = $(wildcard */*.h) $(wildcard *.h)
@@ -92,12 +92,13 @@ ifeq ($(HAVE_CG), 1)
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o sha256.o cheats.o
OBJ += gfx/shader_glsl.o gfx/image.o sha256.o cheats.o
LIBS += $(XML_LIBS)
DEFINES += $(XML_CFLAGS)
endif
ifeq ($(HAVE_FILTER), 1)
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
LIBS += -ldl
endif
@@ -107,6 +108,11 @@ ifeq ($(HAVE_FREETYPE), 1)
DEFINES += $(FREETYPE_CFLAGS)
endif
ifeq ($(HAVE_IMLIB), 1)
LIBS += $(IMLIB_LIBS)
DEFINES += $(IMLIB_CFLAGS)
endif
ifeq ($(HAVE_FFMPEG), 1)
OBJ += record/ffemu.o
LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
@@ -153,9 +159,9 @@ tools/ssnes-joyconfig: $(JOYCONFIG_OBJ)
@$(if $(Q), $(shell echo echo CC $<),)
install: $(TARGET)
mkdir -p $(DESTDIR)$(PREFIX)/bin
mkdir -p $(DESTDIR)/etc
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
mkdir -p $(DESTDIR)$(PREFIX)/bin 2>/dev/null || /bin/true
mkdir -p $(DESTDIR)/etc 2>/dev/null || /bin/true
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 2>/dev/null || /bin/true
install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin
install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg
install -m644 docs/ssnes.1 $(DESTDIR)$(PREFIX)/share/man/man1
@@ -163,9 +169,12 @@ install: $(TARGET)
install -m755 ssnes-zip $(DESTDIR)$(PREFIX)/bin
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/{ssnes,ssnes-joyconfig,ssnes-zip}
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes-joyconfig
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes-zip
rm -f $(DESTDIR)/etc/ssnes.cfg
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/{ssnes,ssnes-joyconfig}.1
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ssnes.1
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ssnes-joyconfig.1
clean:
rm -f *.o
@@ -173,8 +182,6 @@ clean:
rm -f conf/*.o
rm -f gfx/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f input/*.o
rm -f tools/*.o
rm -f $(TARGET)
+17 -6
View File
@@ -1,6 +1,6 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
@@ -12,9 +12,10 @@ HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 1
HAVE_FILTER = 1
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
libsnes ?= -lsnes
LIBS = -lm
@@ -38,6 +39,12 @@ ifeq ($(HAVE_SDL), 1)
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
@@ -50,13 +57,13 @@ ifeq ($(HAVE_RSOUND), 1)
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o sha256.o cheats.o
OBJ += gfx/shader_glsl.o gfx/image.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML
LIBS += -lxml2
endif
ifeq ($(HAVE_FILTER), 1)
DEFINES += -DHAVE_FILTER
ifeq ($(HAVE_DYLIB), 1)
DEFINES += -DHAVE_DYLIB
endif
ifeq ($(HAVE_NETPLAY), 1)
@@ -81,6 +88,10 @@ ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifneq ($(V),1)
Q := @
endif
@@ -116,7 +127,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.4.1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
zip -r ssnes-win32-0.5.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+137
View File
@@ -0,0 +1,137 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 0
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
libsnes ?= -lsnes
LIBS = -lm
DEFINES = -I. -DHAVE_CONFIGFILE
LDFLAGS = -L. -static-libgcc -s
SRC_LIBS = -lsamplerate-0
SDL_LIBS = -lmingw32 -lSDL -lSDLmain -lwinmm -mwindows -lm -luser32 -lgdi32 -lwinmm -ldxguid
SDL_CFLAGS = -ISDL
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS) -DHAVE_SRC
else
OBJ += audio/hermite.o
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
LIBS += -lrsound
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o gfx/image.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML -DLIBXML_STATIC
LIBS += -lxml2 -lz -lws2_32
endif
ifeq ($(HAVE_DYLIB), 1)
DEFINES += -DHAVE_DYLIB
endif
ifeq ($(HAVE_NETPLAY), 1)
DEFINES += -DHAVE_NETPLAY
OBJ += netplay.o
LIBS += -lws2_32
endif
ifeq ($(HAVE_FREETYPE), 1)
OBJ += gfx/fonts.o
DEFINES += -DHAVE_FREETYPE -Ifreetype2
LIBS += -lz -lfreetype6
endif
ifeq ($(DYNAMIC), 1)
DEFINES += -DHAVE_DYNAMIC
else
LIBS += $(libsnes)
endif
ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifneq ($(V),1)
Q := @
endif
CFLAGS = -Wall -O3 -std=gnu99 -I.
all: $(TARGET) $(JTARGET)
$(TARGET): $(OBJ)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
%.o: %.c
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CC $<),)
$(JTARGET): $(JOBJ)
$(Q)$(CC) -o $@ $(JOBJ) $(SDL_LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
clean:
rm -f *.o
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f $(TARGET)
rm -f ssnes-joyconfig.exe
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.5.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate
unzip ssnes-win64-libs.zip
.PHONY: all install uninstall clean dist libs
+1 -1
View File
@@ -49,7 +49,7 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
return false;
}
static void* __alsa_init(const char* device, int rate, int latency)
static void* __alsa_init(const char* device, unsigned rate, unsigned latency)
{
alsa_t *alsa = calloc(1, sizeof(alsa_t));
if ( alsa == NULL )
+157
View File
@@ -0,0 +1,157 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "ext/ssnes_audio.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "driver.h"
#include "dynamic.h"
#include "general.h"
#include <sys/types.h>
typedef struct audio_ext
{
dylib_t lib;
const ssnes_audio_driver_t *driver;
void *handle;
bool is_float;
} audio_ext_t;
static void audio_ext_free(void *data)
{
audio_ext_t *ext = data;
if (ext)
{
if (ext->driver && ext->handle)
ext->driver->free(ext->handle);
if (ext->lib)
dylib_close(ext->lib);
free(ext);
}
}
static void* audio_ext_init(const char *device, unsigned rate, unsigned latency)
{
if (!(*g_settings.audio.external_driver))
{
SSNES_ERR("Please define an external audio driver.\n");
return NULL;
}
audio_ext_t *ext = calloc(1, sizeof(*ext));
if (!ext)
return NULL;
ext->lib = dylib_load(g_settings.audio.external_driver);
if (!ext->lib)
{
SSNES_ERR("Failed to load external library \"%s\"\n", g_settings.audio.external_driver);
goto error;
}
const ssnes_audio_driver_t* (*plugin_load)(void) = dylib_proc(ext->lib, "ssnes_audio_driver_init");
if (!plugin_load)
{
SSNES_ERR("Failed to find symbol \"ssnes_audio_driver_init\" in plugin.\n");
goto error;
}
ext->driver = plugin_load();
if (!ext->driver)
{
SSNES_ERR("Received invalid driver from plugin.\n");
goto error;
}
SSNES_LOG("Loaded external audio driver: \"%s\"\n", ext->driver->ident ? ext->driver->ident : "Unknown");
if (ext->driver->api_version != SSNES_AUDIO_API_VERSION)
{
SSNES_ERR("API mismatch in external video plugin. SSNES: %d, Plugin: %d ...\n", SSNES_AUDIO_API_VERSION, ext->driver->api_version);
goto error;
}
ssnes_audio_driver_info_t info = {
.device = device,
.sample_rate = rate,
.latency = latency
};
ext->handle = ext->driver->init(&info);
if (!ext->handle)
{
SSNES_ERR("Failed to init audio driver.\n");
goto error;
}
return ext;
error:
audio_ext_free(ext);
return NULL;
}
static ssize_t audio_ext_write(void *data, const void *buf, size_t size)
{
audio_ext_t *ext = data;
unsigned frame_size = ext->is_float ? (2 * sizeof(float)) : (2 * sizeof(int16_t));
size /= frame_size;
int ret = ext->driver->write(ext->handle, buf, size);
if (ret < 0)
return -1;
return ret * frame_size;
}
static bool audio_ext_start(void *data)
{
audio_ext_t *ext = data;
return ext->driver->start(ext->handle);
}
static bool audio_ext_stop(void *data)
{
audio_ext_t *ext = data;
return ext->driver->stop(ext->handle);
}
static void audio_ext_set_nonblock_state(void *data, bool toggle)
{
audio_ext_t *ext = data;
ext->driver->set_nonblock_state(ext->handle, toggle);
}
static bool audio_ext_use_float(void *data)
{
audio_ext_t *ext = data;
ext->is_float = ext->driver->use_float(ext->handle);
return ext->is_float;
}
const audio_driver_t audio_ext = {
.init = audio_ext_init,
.write = audio_ext_write,
.stop = audio_ext_stop,
.start = audio_ext_start,
.set_nonblock_state = audio_ext_set_nonblock_state,
.use_float = audio_ext_use_float,
.free = audio_ext_free,
.ident = "ext"
};
+16
View File
@@ -0,0 +1,16 @@
TARGET = ssnes_iir_filter.so
OBJ = ssnes_iir_filter.o
all: $(TARGET)
$(TARGET): $(OBJ)
$(CC) -o $@ -shared $(OBJ)
%.o: %.c
$(CC) -c -o $@ $< -std=c99 -fPIC -O3 -march=native $(CFLAGS)
clean:
rm -f $(TARGET)
rm -f $(OBJ)
.PHONY: clean
+121
View File
@@ -0,0 +1,121 @@
/////
// API header for external SSNES audio driver plugins.
//
//
#ifndef __SSNES_AUDIO_DRIVER_PLUGIN_H
#define __SSNES_AUDIO_DRIVER_PLUGIN_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#ifdef SSNES_DLL_IMPORT
#define SSNES_API_EXPORT __declspec(dllimport)
#else
#define SSNES_API_EXPORT __declspec(dllexport)
#endif
#define SSNES_API_CALLTYPE __cdecl
#else
#define SSNES_API_EXPORT
#define SSNES_API_CALLTYPE
#endif
#ifndef SSNES_TRUE
#define SSNES_TRUE 1
#endif
#ifndef SSNES_FALSE
#define SSNES_FALSE 0
#endif
#ifndef SSNES_OK
#define SSNES_OK 1
#endif
#ifndef SSNES_ERROR
#define SSNES_ERROR 0
#endif
#define SSNES_AUDIO_API_VERSION 1
typedef struct ssnes_audio_driver_info
{
// A hint for a subdevice of the audio driver.
// This is driver independent, and not relevant for all
// audio drivers. I.e. ALSA driver might use "hw:0",
// OSS "/dev/audio", etc.
const char *device;
// Audio sample rate.
unsigned sample_rate;
// Maximum audio latency requested for output,
// measured in milliseconds.
// If driver is not able to provide this latency, it can
// be disregarded.
unsigned latency;
} ssnes_audio_driver_info_t;
typedef struct ssnes_audio_driver
{
// Initializes the device.
void* (*init)(const ssnes_audio_driver_info_t *info);
// Write data in buffer to audio driver.
// A frame here is defined as one combined sample of left and right
// channels. (I.e. 44.1kHz, 16-bit stereo has 88.2k samples/s, and
// 44.1k frames/s.)
//
// Samples are interleaved in format LRLRLRLRLR ...
// If the driver returns true in use_float(), a floating point
// format will be used, with range [-1.0, 1.0].
// If not, signed 16-bit samples in native byte ordering will be used.
//
// This function returns the number of frames successfully written.
// If an error occurs, -1 should be returned.
// Note that non-blocking behavior that cannot write at this time
// should return 0 as returning -1 will terminate the driver.
//
// Unless said otherwise with set_nonblock_state(), all writes
// are blocking, and it should block till it has written all frames.
int (*write)(void *data, const void *buffer, unsigned frames);
// Temporarily pauses the audio driver.
int (*stop)(void *data);
// Resumes audio driver from the paused state.
int (*start)(void *data);
// If state is true, nonblocking operation is assumed.
// This is typically used for fast-forwarding. If driver cannot
// implement nonblocking writes, this can be disregarded, but should
// log a message to stderr.
void (*set_nonblock_state)(void *data, int state);
// Stops and frees the audio driver.
void (*free)(void *data);
// If true is returned, the audio driver is capable of using
// floating point data. This will likely increase performance as the
// resampler unit uses floating point. The sample range is
// [-1.0, 1.0].
int (*use_float)(void *data);
// Human readable identification string for the driver.
const char *ident;
// Must be set to SSNES_AUDIO_API_VERSION.
// Used for detecting API mismatch.
int api_version;
} ssnes_audio_driver_t;
SSNES_API_EXPORT const ssnes_audio_driver_t* SSNES_API_CALLTYPE
ssnes_audio_driver_init(void);
#ifdef __cplusplus
}
#endif
#endif
+124
View File
@@ -0,0 +1,124 @@
/////
// API header for external SSNES DSP plugins.
//
//
#ifndef __SSNES_DSP_PLUGIN_H
#define __SSNES_DSP_PLUGIN_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#ifdef SSNES_DLL_IMPORT
#define SSNES_API_EXPORT __declspec(dllimport)
#else
#define SSNES_API_EXPORT __declspec(dllexport)
#endif
#define SSNES_API_CALLTYPE __cdecl
#else
#define SSNES_API_EXPORT
#define SSNES_API_CALLTYPE
#endif
#ifndef SSNES_FALSE
#define SSNES_FALSE 0
#endif
#ifndef SSNES_TRUE
#define SSNES_TRUE 1
#endif
#define SSNES_DSP_API_VERSION 2
typedef struct ssnes_dsp_info
{
// Input sample rate that the DSP plugin receives. This is generally ~32kHz.
// Some small variance is allowed due to syncing behavior.
float input_rate;
// SSNES requests that the DSP plugin resamples the
// input to a certain frequency.
//
// However, the plugin might ignore this
// using the resample field in ssnes_dsp_output_t (see below).
float output_rate;
} ssnes_dsp_info_t;
typedef struct ssnes_dsp_output
{
// The DSP plugin has to provide the buffering for the output samples.
// This is for performance reasons to avoid redundant copying of data.
// The samples are laid out in interleaving order: LRLRLRLR
// The range of the samples are [-1.0, 1.0].
// This range cannot be exceeded without horrible audio glitches.
const float *samples;
// Frames which the DSP plugin outputted for the current process.
// One frame is here defined as a combined sample of
// left and right channels.
// (I.e. 44.1kHz, 16bit stereo will have
// 88.2k samples/sec and 44.1k frames/sec.)
unsigned frames;
// If true, the DSP plugin did not resample the input audio,
// and requests resampling to the proper frequency to be
// performed outside the plugin.
// If false,
// it is assumed that the output has the same sample rate as given
// in output_rate.
int should_resample;
} ssnes_dsp_output_t;
typedef struct ssnes_dsp_input
{
// Input data for the DSP. The samples are interleaved in order: LRLRLRLR
const float *samples;
// Number of frames for input data.
// One frame is here defined as a combined sample of
// left and right channels.
// (I.e. 44.1kHz, 16bit stereo will have
// 88.2k samples/sec and 44.1k frames/sec.)
unsigned frames;
} ssnes_dsp_input_t;
typedef struct ssnes_dsp_plugin
{
// Creates a handle of the plugin. Returns NULL if failed.
void* (*init)(const ssnes_dsp_info_t *info);
// Processes input data.
// The plugin is allowed to return variable sizes for output data.
void (*process)(void *data, ssnes_dsp_output_t *output,
const ssnes_dsp_input_t *input);
// Frees the handle.
void (*free)(void *data);
// API version used to compile the plugin.
// Used to detect mismatches in API.
// Must be set to SSNES_DSP_API_VERSION on compile.
int api_version;
// Signal plugin that it may open a configuring window or
// something similiar. The behavior of this function
// is thus plugin dependent. Implementing this is optional,
// and can be set to NULL.
void (*config)(void *data);
// Human readable identification string.
const char *ident;
} ssnes_dsp_plugin_t;
// Called by SSNES at startup to get the callback struct.
// This is NOT dynamically allocated!
SSNES_API_EXPORT const ssnes_dsp_plugin_t* SSNES_API_CALLTYPE
ssnes_dsp_plugin_init(void);
#ifdef __cplusplus
}
#endif
#endif
+253
View File
@@ -0,0 +1,253 @@
#include "ssnes_dsp.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
// Simple IIR filter implementation, optimized for SSE3.
#ifdef __SSE3__ // Build with -march=native or -msse3 to let this be detected! D:
#define USE_SSE3
#endif
#ifdef USE_SSE3
#include <pmmintrin.h>
#endif
// Make a test build with standalone main()
//#define TEST
#ifdef TEST
#include <stdio.h>
#include <assert.h>
#endif
#define min(x, y) ((x) < (y) ? (x) : (y))
// Taps needs to be power-of-two.
#define TAPS (128)
// FIR coefficients, up to TAPS taps.
// Basic passthrough.
static const float fir_coeff[] = {
1.0
};
// IIR coefficients, up to TAPS taps.
static const float iir_coeff[] = {
};
#ifdef USE_SSE3
typedef union
{
__m128 vec[TAPS / 4 + 1]; // Wraparound for unaligned reads.
float f[TAPS + 4];
} vec_filt;
#else
typedef float vec_filt[TAPS + 4];
#endif
typedef struct
{
vec_filt buffer[2];
vec_filt iir_buffer[2];
vec_filt fir_coeff;
vec_filt iir_coeff;
float out_buffer[4096];
unsigned buf_ptr;
} dsp_state;
static void* dsp_init(const ssnes_dsp_info_t *info)
{
(void)info;
dsp_state *state = calloc(1, sizeof(*state));
if (!state)
return NULL;
memcpy(&state->fir_coeff, fir_coeff, min(sizeof(fir_coeff), TAPS * sizeof(float)));
memcpy(&state->iir_coeff, iir_coeff, min(sizeof(iir_coeff), TAPS * sizeof(float)));
#ifdef USE_SSE3
memcpy(state->fir_coeff.f + TAPS, fir_coeff, min(sizeof(fir_coeff), 4 * sizeof(float)));
memcpy(state->iir_coeff.f + TAPS, iir_coeff, min(sizeof(iir_coeff), 4 * sizeof(float)));
#endif
return state;
}
#ifdef USE_SSE3
static void calculate_iir(float *out, dsp_state *dsp)
{
unsigned buf_ptr = (dsp->buf_ptr - 1) & (TAPS - 1);
unsigned iir_ptr = (TAPS - dsp->buf_ptr) & (TAPS - 1);
const float * restrict samples_left = dsp->buffer[0].f;
const float * restrict samples_right = dsp->buffer[1].f;
const float * restrict iir_left = dsp->iir_buffer[0].f;
const float * restrict iir_right = dsp->iir_buffer[1].f;
__m128 sum_left = _mm_setzero_ps();
__m128 sum_right = _mm_setzero_ps();
for (unsigned i = 0; i < TAPS; i += 4)
{
__m128 left = _mm_load_ps(samples_left + i);
__m128 right = _mm_load_ps(samples_right + i);
const __m128 ileft = _mm_load_ps(iir_left + i);
const __m128 iright = _mm_load_ps(iir_right + i);
// Need unaligned reads here.
const __m128 fir = _mm_loadu_ps(dsp->fir_coeff.f + iir_ptr);
const __m128 iir = _mm_loadu_ps(dsp->iir_coeff.f + iir_ptr);
const __m128 fir_res_left = _mm_mul_ps(fir, left);
const __m128 fir_res_right = _mm_mul_ps(fir, right);
const __m128 iir_res_left = _mm_mul_ps(iir, ileft);
const __m128 iir_res_right = _mm_mul_ps(iir, iright);
left = _mm_add_ps(fir_res_left, iir_res_left);
right = _mm_add_ps(fir_res_right, iir_res_right);
sum_left = _mm_add_ps(sum_left, left);
sum_right = _mm_add_ps(sum_right, right);
iir_ptr = (iir_ptr + 4) & (TAPS - 1);
}
__m128 res = _mm_hadd_ps(sum_left, sum_right);
res = _mm_hadd_ps(res, res);
union
{
__m128 vec;
float f[4];
} u;
u.vec = res;
out[0] = u.f[0];
out[1] = u.f[1];
dsp->iir_buffer[0].f[buf_ptr] = u.f[0];
dsp->iir_buffer[1].f[buf_ptr] = u.f[1];
}
#else
static void calculate_iir(float *out, dsp_state *dsp)
{
const float * restrict samples_left = dsp->buffer[0];
const float * restrict samples_right = dsp->buffer[1];
const float * restrict iir_left = dsp->iir_buffer[0];
const float * restrict iir_right = dsp->iir_buffer[1];
const float * restrict fir = dsp->fir_coeff;
const float * restrict iir = dsp->iir_coeff;
unsigned iir_ptr = 0;
unsigned sample_ptr = dsp->buf_ptr;
float sum[2] = { 0.0f, 0.0f };
for (unsigned i = 0; i < TAPS; i++)
{
sum[0] += fir[iir_ptr] * samples_left[sample_ptr] + iir[iir_ptr] * iir_left[sample_ptr];
sum[1] += fir[iir_ptr] * samples_right[sample_ptr] + iir[iir_ptr] * iir_right[sample_ptr];
iir_ptr++;
sample_ptr = (sample_ptr + 1) & (TAPS - 1);
}
// Stick our output value in the IIR buffer.
for (unsigned i = 0; i < 2; i++)
{
out[i] = sum[i];
dsp->iir_buffer[i][(dsp->buf_ptr - 1) & (TAPS - 1)] = sum[i];
}
}
#endif
static void dsp_process(void *data, ssnes_dsp_output_t *output, const ssnes_dsp_input_t *input)
{
dsp_state *dsp = data;
output->should_resample = SSNES_TRUE;
output->frames = input->frames;
output->samples = dsp->out_buffer;
for (unsigned i = 0; i < input->frames; i++)
{
#ifdef USE_SSE3
dsp->buffer[0].f[dsp->buf_ptr] = input->samples[(i << 1) + 0];
dsp->buffer[1].f[dsp->buf_ptr] = input->samples[(i << 1) + 1];
#else
dsp->buffer[0][dsp->buf_ptr] = input->samples[(i << 1) + 0];
dsp->buffer[1][dsp->buf_ptr] = input->samples[(i << 1) + 1];
#endif
calculate_iir(&dsp->out_buffer[i << 1], dsp);
dsp->buf_ptr = (dsp->buf_ptr - 1) & (TAPS - 1);
}
}
static void dsp_config(void *data)
{
(void)data;
// Normally we unhide a GUI window or something,
// but we're just going to print to the log instead.
fprintf(stderr, "DSP_CONFIG\n");
}
static void dsp_free(void *data)
{
free(data);
}
const ssnes_dsp_plugin_t dsp_plug = {
.init = dsp_init,
.process = dsp_process,
.free = dsp_free,
.config = dsp_config,
.api_version = SSNES_DSP_API_VERSION,
.ident = "IIR filter"
};
SSNES_API_EXPORT const ssnes_dsp_plugin_t*
SSNES_API_CALLTYPE ssnes_dsp_plugin_init(void)
{
return &dsp_plug;
}
#ifdef TEST
int main(void)
{
const ssnes_dsp_plugin_t *plug = ssnes_dsp_plugin_init();
assert(plug);
ssnes_dsp_info_t info;
void *handle = plug->init(&info); // Info isn't used.
assert(handle);
int16_t buf[64];
float fbuf[64];
for (;;)
{
size_t rd = fread(buf, sizeof(int16_t), 64, stdin);
for (unsigned i = 0; i < rd; i++)
fbuf[i] = (float)buf[i];
ssnes_dsp_input_t input = {
.samples = fbuf,
.frames = rd >> 1
};
ssnes_dsp_output_t output;
plug->process(handle, &output, &input);
for (unsigned i = 0; i < output.frames << 1; i++)
{
int32_t sample = (int32_t)output.samples[i];
buf[i] = (sample > 0x7fff) ? 0x7fff : ((sample < -0x8000) ? -0x8000 : (int16_t)sample);
}
fwrite(buf, sizeof(int16_t), output.frames << 1, stdout);
if (rd < 64)
break;
}
plug->free(handle);
}
#endif
+1 -1
View File
@@ -124,7 +124,7 @@ static size_t find_buffersize(jack_t *jd, int latency)
return buffer_frames * sizeof(jack_default_audio_sample_t);
}
static void* __jack_init(const char* device, int rate, int latency)
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
{
jack_t *jd = calloc(1, sizeof(jack_t));
if ( jd == NULL )
+1 -1
View File
@@ -56,7 +56,7 @@ typedef struct al
} al_t;
static void* __al_init(const char* device, int rate, int latency)
static void* __al_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
al_t *al = calloc(1, sizeof(al_t));
+1 -1
View File
@@ -25,7 +25,7 @@
#include <errno.h>
#include <stdio.h>
static void* __oss_init(const char* device, int rate, int latency)
static void* __oss_init(const char* device, unsigned rate, unsigned latency)
{
int *fd = calloc(1, sizeof(int));
if ( fd == NULL )
+1 -1
View File
@@ -115,7 +115,7 @@ static void stream_latency_update_cb(pa_stream *s, void *data)
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
static void* __pulse_init(const char* device, int rate, int latency)
static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
{
pa_t *pa = calloc(1, sizeof(*pa));
if (!pa)
+1 -1
View File
@@ -30,7 +30,7 @@ typedef struct
bool nonblocking;
} roar_t;
static void* __roar_init(const char* device, int rate, int latency)
static void* __roar_init(const char* device, unsigned rate, unsigned latency)
{
int err;
roar_t *roar = calloc(1, sizeof(roar_t));
+1 -1
View File
@@ -54,7 +54,7 @@ static void err_cb(void *userdata)
SDL_CondSignal(rsd->cond);
}
static void* __rsd_init(const char* device, int rate, int latency)
static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
{
rsd_t *rsd = calloc(1, sizeof(rsd_t));
if ( rsd == NULL )
+1 -1
View File
@@ -58,7 +58,7 @@ static inline int find_num_frames(int rate, int latency)
return next_pow2(frames);
}
static void* sdl_audio_init(const char* device, int rate, int latency)
static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
+1 -1
View File
@@ -67,7 +67,7 @@ static bool init_lib(void)
return true;
}
static void* __xa_init(const char* device, int rate, int latency)
static void* __xa_init(const char* device, unsigned rate, unsigned latency)
{
if (!init_lib())
return NULL;
+1 -1
View File
@@ -121,7 +121,7 @@ cheat_manager_t* cheat_manager_new(const char *path)
xmlParserCtxtPtr ctx = NULL;
xmlDocPtr doc = NULL;
cheat_manager_t *handle = calloc(1, sizeof(handle));
cheat_manager_t *handle = calloc(1, sizeof(struct cheat_manager));
if (!handle)
return NULL;
+46 -83
View File
@@ -33,7 +33,7 @@
#ifdef HAVE_SDL
#include "SDL.h"
#else
#error HAVE_SDL is not defined!
#error "HAVE_SDL is not defined!"
#endif
#ifdef HAVE_SRC
@@ -45,6 +45,7 @@
#define VIDEO_GL 0
#define VIDEO_XVIDEO 11
#define VIDEO_SDL 13
#define VIDEO_EXT 14
////////////////////////
#define AUDIO_RSOUND 1
#define AUDIO_OSS 2
@@ -55,6 +56,7 @@
#define AUDIO_SDL 8
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
#define AUDIO_EXT 15
////////////////////////
#define INPUT_SDL 7
#define INPUT_X 12
@@ -64,6 +66,12 @@
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
#elif defined(HAVE_XVIDEO)
#define VIDEO_DEFAULT_DRIVER VIDEO_XVIDEO
#elif defined(HAVE_SDL)
#define VIDEO_DEFAULT_DRIVER VIDEO_SDL
#elif defined(HAVE_DYLIB)
#define VIDEO_DEFAULT_DRIVER VIDEO_EXT
#else
#error "Need at least one video driver!"
#endif
#if defined(HAVE_ALSA)
@@ -84,14 +92,18 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
#elif defined(HAVE_ROAR)
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
#elif defined(HAVE_DYLIB)
#define AUDIO_DEFAULT_DRIVER AUDIO_EXT
#else
#error Need at least one audio driver!
#error "Need at least one audio driver!"
#endif
#if defined(HAVE_SDL)
#define INPUT_DEFAULT_DRIVER INPUT_SDL
#elif defined(HAVE_XVIDEO)
#define INPUT_DEFAULT_DRIVER INPUT_X
#else
#error "Need at least one input driver!"
#endif
@@ -120,6 +132,9 @@ static const bool video_smooth = true;
// On resize and fullscreen, rendering area will stay 4:3
static const bool force_aspect = true;
// Crop overscanned frames (7/8 or 15/15 for interlaced frames).
static const bool crop_overscan = false;
// Font size for on-screen messages.
static const unsigned font_size = 48;
@@ -200,19 +215,19 @@ static const bool netplay_client_swap_input = false;
// Player 1
static const struct snes_keybind snes_keybinds_1[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_x, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_z, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_s, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_q, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_w, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, 7, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, 6, AXIS_NONE },
{ SSNES_FAST_FORWARD_KEY, SDLK_SPACE, 10, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_x, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_z, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_s, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_q, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_w, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, NO_BTN, AXIS_NONE },
{ SSNES_FAST_FORWARD_KEY, SDLK_SPACE, NO_BTN, AXIS_NONE },
{ SSNES_SAVE_STATE_KEY, SDLK_F2, NO_BTN, AXIS_NONE },
{ SSNES_LOAD_STATE_KEY, SDLK_F4, NO_BTN, AXIS_NONE },
{ SSNES_FULLSCREEN_TOGGLE_KEY, SDLK_f, NO_BTN, AXIS_NONE },
@@ -230,78 +245,26 @@ static const struct snes_keybind snes_keybinds_1[] = {
{ SSNES_CHEAT_INDEX_PLUS, SDLK_y, NO_BTN, AXIS_NONE },
{ SSNES_CHEAT_INDEX_MINUS, SDLK_t, NO_BTN, AXIS_NONE },
{ SSNES_CHEAT_TOGGLE, SDLK_u, NO_BTN, AXIS_NONE },
{ SSNES_SCREENSHOT, SDLK_PRINT, NO_BTN, AXIS_NONE },
{ SSNES_DSP_CONFIG, SDLK_c, NO_BTN, AXIS_NONE },
{ -1 }
};
// Player 2
static const struct snes_keybind snes_keybinds_2[] = {
// Player 2-5
static const struct snes_keybind snes_keybinds_rest[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ -1 }
};
// Player 3
static const struct snes_keybind snes_keybinds_3[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ -1 }
};
// Player 4
static const struct snes_keybind snes_keybinds_4[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ -1 }
};
// Player 5
static const struct snes_keybind snes_keybinds_5[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, NO_BTN, AXIS_NONE },
{ -1 }
};
+9 -3
View File
@@ -62,10 +62,10 @@ static const bool _xaudio_supp = true;
static const bool _xaudio_supp = false;
#endif
#ifdef HAVE_FILTER
static const bool _filter_supp = true;
#ifdef HAVE_DYLIB
static const bool _dylib_supp = true;
#else
static const bool _filter_supp = false;
static const bool _dylib_supp = false;
#endif
#ifdef HAVE_CG
@@ -80,6 +80,12 @@ static const bool _xml_supp = true;
static const bool _xml_supp = false;
#endif
#ifdef HAVE_IMLIB
static const bool _imlib_supp = true;
#else
static const bool _imlib_supp = false;
#endif
#ifdef HAVE_FBO
static const bool _fbo_supp = true;
#else
+102 -7
View File
@@ -56,6 +56,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_PULSE
&audio_pulse,
#endif
#ifdef HAVE_DYLIB
&audio_ext,
#endif
};
static const video_driver_t *video_drivers[] = {
@@ -66,15 +69,18 @@ static const video_driver_t *video_drivers[] = {
#ifdef HAVE_XVIDEO
&video_xvideo,
#endif
#ifdef HAVE_DYLIB
&video_ext,
#endif
};
static const input_driver_t *input_drivers[] = {
#ifdef HAVE_SDL
&input_sdl,
#endif
//#ifdef HAVE_XVIDEO
// &input_x,
//#endif
#ifdef HAVE_XVIDEO
&input_x,
#endif
};
static void find_audio_driver(void)
@@ -143,6 +149,70 @@ void uninit_drivers(void)
uninit_audio();
}
static void init_dsp_plugin(void)
{
if (!(*g_settings.audio.dsp_plugin))
return;
g_extern.audio_data.dsp_lib = dylib_load(g_settings.audio.dsp_plugin);
if (!g_extern.audio_data.dsp_lib)
{
SSNES_ERR("Failed to open DSP plugin: \"%s\" ...\n", g_settings.audio.dsp_plugin);
return;
}
const ssnes_dsp_plugin_t* (*SSNES_API_CALLTYPE plugin_init)(void) = dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
if (!plugin_init)
{
SSNES_ERR("Failed to find symbol \"ssnes_dsp_plugin_init\" in DSP plugin.\n");
goto error;
}
g_extern.audio_data.dsp_plugin = plugin_init();
if (!g_extern.audio_data.dsp_plugin)
{
SSNES_ERR("Failed to get a valid DSP plugin.\n");
goto error;
}
if (g_extern.audio_data.dsp_plugin->api_version != SSNES_DSP_API_VERSION)
{
SSNES_ERR("DSP plugin API mismatch! SSNES: %d, Plugin: %d\n", SSNES_DSP_API_VERSION, g_extern.audio_data.dsp_plugin->api_version);
goto error;
}
SSNES_LOG("Loaded DSP plugin: \"%s\"\n", g_extern.audio_data.dsp_plugin->ident ? g_extern.audio_data.dsp_plugin->ident : "Unknown");
ssnes_dsp_info_t info = {
.input_rate = g_settings.audio.in_rate,
.output_rate = g_settings.audio.out_rate
};
g_extern.audio_data.dsp_handle = g_extern.audio_data.dsp_plugin->init(&info);
if (!g_extern.audio_data.dsp_handle)
{
SSNES_ERR("Failed to init DSP plugin.\n");
goto error;
}
return;
error:
if (g_extern.audio_data.dsp_lib)
dylib_close(g_extern.audio_data.dsp_lib);
g_extern.audio_data.dsp_plugin = NULL;
g_extern.audio_data.dsp_lib = NULL;
}
static void deinit_dsp_plugin(void)
{
if (g_extern.audio_data.dsp_lib && g_extern.audio_data.dsp_plugin)
{
g_extern.audio_data.dsp_plugin->free(g_extern.audio_data.dsp_handle);
dylib_close(g_extern.audio_data.dsp_lib);
}
}
#define AUDIO_CHUNK_SIZE_BLOCKING 64
#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
#define AUDIO_MAX_RATIO 16
@@ -193,6 +263,8 @@ void init_audio(void)
g_extern.audio_data.data_ptr = 0;
assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO)));
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
init_dsp_plugin();
}
void uninit_audio(void)
@@ -218,9 +290,11 @@ void uninit_audio(void)
free(g_extern.audio_data.data); g_extern.audio_data.data = NULL;
free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL;
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
deinit_dsp_plugin();
}
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
static void init_filter(void)
{
if (g_extern.filter.active)
@@ -319,7 +393,7 @@ static void deinit_shader_dir(void)
void init_video_input(void)
{
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
init_filter();
#endif
@@ -336,9 +410,30 @@ void init_video_input(void)
if (g_extern.filter.active)
scale = g_extern.filter.scale;
unsigned width;
unsigned height;
if (g_settings.video.fullscreen)
{
width = g_settings.video.fullscreen_x;
height = g_settings.video.fullscreen_y;
}
else
{
if (g_settings.video.force_aspect)
{
width = 224 * g_settings.video.xscale * g_settings.video.aspect_ratio;
height = 224 * g_settings.video.yscale;
}
else
{
width = 256 * g_settings.video.xscale;
height = 224 * g_settings.video.yscale;
}
}
video_info_t video = {
.width = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_x : (224 * g_settings.video.xscale * g_settings.video.aspect_ratio),
.height = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_y : (224 * g_settings.video.yscale),
.width = width,
.height = height,
.fullscreen = g_settings.video.fullscreen,
.vsync = g_settings.video.vsync,
.force_aspect = g_settings.video.force_aspect,
+8 -4
View File
@@ -44,7 +44,9 @@ enum
SSNES_SHADER_PREV,
SSNES_CHEAT_INDEX_PLUS,
SSNES_CHEAT_INDEX_MINUS,
SSNES_CHEAT_TOGGLE
SSNES_CHEAT_TOGGLE,
SSNES_SCREENSHOT,
SSNES_DSP_CONFIG
};
@@ -58,8 +60,8 @@ struct snes_keybind
typedef struct video_info
{
int width;
int height;
unsigned width;
unsigned height;
bool fullscreen;
bool vsync;
bool force_aspect;
@@ -70,7 +72,7 @@ typedef struct video_info
typedef struct audio_driver
{
void* (*init)(const char* device, int rate, int latency);
void* (*init)(const char* device, unsigned rate, unsigned latency);
ssize_t (*write)(void* data, const void* buf, size_t size);
bool (*stop)(void* data);
bool (*start)(void* data);
@@ -154,9 +156,11 @@ extern const audio_driver_t audio_jack;
extern const audio_driver_t audio_sdl;
extern const audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const audio_driver_t audio_ext;
extern const video_driver_t video_gl;
extern const video_driver_t video_xvideo;
extern const video_driver_t video_sdl;
extern const video_driver_t video_ext;
extern const input_driver_t input_sdl;
extern const input_driver_t input_x;
////////////////////////////////////////////////
+1 -1
View File
@@ -40,7 +40,7 @@
#endif
// Generic file loader.
static ssize_t read_file(const char *path, void **buf)
ssize_t read_file(const char *path, void **buf)
{
void *rom_buf = NULL;
FILE *file = fopen(path, "rb");
+2
View File
@@ -26,6 +26,8 @@
#include <sys/types.h>
#include "general.h"
ssize_t read_file(const char *path, void **buf);
bool load_state(const char* path);
bool save_state(const char* path);
+17 -2
View File
@@ -30,6 +30,7 @@
#include "netplay.h"
#include "dynamic.h"
#include "cheats.h"
#include "audio/ext/ssnes_dsp.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -43,7 +44,7 @@
#define MAX_PLAYERS 5
#define MAX_BINDS 31 // Needs to be increased every time there are new binds added.
#define MAX_BINDS 33 // Needs to be increased every time there are new binds added.
#define SSNES_NO_JOYPAD 0xFFFF
enum ssnes_shader_type
@@ -68,6 +69,7 @@ struct settings
bool vsync;
bool smooth;
bool force_aspect;
bool crop_overscan;
float aspect_ratio;
char cg_shader_path[256];
char bsnes_shader_path[256];
@@ -87,6 +89,8 @@ struct settings
float msg_pos_y;
bool force_16bit;
char external_driver[256];
} video;
struct
@@ -100,6 +104,9 @@ struct settings
unsigned latency;
bool sync;
int src_quality;
char dsp_plugin[256];
char external_driver[256];
} audio;
struct
@@ -114,6 +121,8 @@ struct settings
char libsnes[256];
char cheat_database[256];
char screenshot_directory[256];
bool rewind_enable;
unsigned rewind_buffer_size;
unsigned rewind_granularity;
@@ -188,6 +197,10 @@ struct global
float *outsamples;
int16_t *conv_outsamples;
dylib_t dsp_lib;
const ssnes_dsp_plugin_t *dsp_plugin;
void *dsp_handle;
} audio_data;
struct
@@ -247,7 +260,9 @@ struct global
size_t ptr;
} shader_dir;
char sha256[65];
char sha256[64 + 1];
bool do_screenshot;
#ifdef HAVE_XML
cheat_manager_t *cheat;
+313
View File
@@ -0,0 +1,313 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
// Loader for external API plugins.
#define SSNES_DLL_IMPORT
#include "ext/ssnes_video.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "dynamic.h"
#include "general.h"
static bool g_input_dead = true;
static bool g_video_dead = true;
static dylib_t g_lib = NULL;
/////////// Input hook
typedef struct
{
const ssnes_input_driver_t *driver;
void *handle;
} input_ext_t;
static void* input_ext_init(void)
{
g_input_dead = false;
return calloc(1, sizeof(input_ext_t));
}
static void input_ext_poll(void *data)
{
input_ext_t *ext = data;
ext->driver->poll(ext->handle);
}
static int16_t input_ext_input_state(void *data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id)
{
input_ext_t *ext = data;
unsigned player = 0;
if (device == SNES_DEVICE_MULTITAP)
player = (port == SNES_PORT_1) ? 1 : index + 2;
else
player = (port == SNES_PORT_1) ? 1 : 2;
const struct snes_keybind *ssnes_bind = NULL;
for (unsigned i = 0; g_settings.input.binds[player - 1][i].id != -1; i++)
{
if (g_settings.input.binds[player - 1][i].id == id)
{
ssnes_bind = &g_settings.input.binds[player - 1][i];
break;
}
}
if (ssnes_bind)
{
struct ssnes_keybind bind = {
.key = ssnes_bind->key,
.joykey = ssnes_bind->joykey,
.joyaxis = ssnes_bind->joyaxis
};
return ext->driver->input_state(ext->handle, &bind, player);
}
else
return 0;
}
static bool input_ext_key_pressed(void *data, int key)
{
input_ext_t *ext = data;
const struct snes_keybind *ssnes_bind = NULL;
for (unsigned i = 0; g_settings.input.binds[0][i].id != -1; i++)
{
if (g_settings.input.binds[0][i].id == key)
{
ssnes_bind = &g_settings.input.binds[0][i];
break;
}
}
if (ssnes_bind)
{
struct ssnes_keybind bind = {
.key = ssnes_bind->key,
.joykey = ssnes_bind->joykey,
.joyaxis = ssnes_bind->joyaxis
};
return ext->driver->input_state(ext->handle, &bind, 1);
}
else
return false;
}
static void input_ext_free(void *data)
{
input_ext_t *ext = data;
if (ext)
{
if (ext->driver && ext->handle)
ext->driver->free(ext->handle);
if (g_video_dead)
{
dylib_close(g_lib);
g_lib = NULL;
}
g_input_dead = true;
free(ext);
}
}
static const input_driver_t input_ext = {
.init = input_ext_init,
.poll = input_ext_poll,
.input_state = input_ext_input_state,
.key_pressed = input_ext_key_pressed,
.free = input_ext_free,
.ident = "ext"
};
//////////// Video hook
typedef struct
{
const ssnes_video_driver_t *driver;
void *handle;
} ext_t;
static void video_ext_free(void *data)
{
ext_t *ext = data;
if (ext)
{
if (ext->driver && ext->handle)
ext->driver->free(ext->handle);
if (g_input_dead)
{
dylib_close(g_lib);
g_lib = NULL;
}
g_video_dead = true;
free(ext);
}
}
static bool video_ext_focus(void *data)
{
ext_t *ext = data;
return ext->driver->focus(ext->handle);
}
static bool video_ext_alive(void *data)
{
ext_t *ext = data;
return ext->driver->alive(ext->handle);
}
static void video_ext_set_nonblock_state(void *data, bool state)
{
ext_t *ext = data;
ext->driver->set_nonblock_state(ext->handle, state);
}
static bool video_ext_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{
ext_t *ext = data;
return ext->driver->frame(ext->handle, frame, width, height, pitch, msg);
}
static void* setup_input(ext_t *ext, const ssnes_input_driver_t *driver)
{
int joypad_index[5];
for (unsigned i = 0; i < 5; i++)
joypad_index[i] = g_settings.input.joypad_map[i] == SSNES_NO_JOYPAD ? -1 : g_settings.input.joypad_map[i];
void *handle = driver->init(joypad_index, g_settings.input.axis_threshold);
if (!handle)
return NULL;
input_ext_t *wrap_handle = input_ext.init();
if (!wrap_handle)
return NULL;
wrap_handle->handle = handle;
wrap_handle->driver = driver;
return wrap_handle;
}
static bool setup_video(ext_t *ext, const video_info_t *video, const input_driver_t **input, void **input_data)
{
SSNES_LOG("Loaded driver: \"%s\"\n", ext->driver->ident ? ext->driver->ident : "Unknown");
if (SSNES_GRAPHICS_API_VERSION != ext->driver->api_version)
{
SSNES_ERR("API version mismatch detected!\n");
SSNES_ERR("Required API version: %d, Library version: %d\n", SSNES_GRAPHICS_API_VERSION, ext->driver->api_version);
return false;
}
ssnes_video_info_t info = {
.width = video->width,
.height = video->height,
.fullscreen = video->fullscreen,
.vsync = video->vsync,
.force_aspect = video->force_aspect,
.aspect_ratio = g_settings.video.aspect_ratio,
.smooth = video->smooth,
.input_scale = video->input_scale,
.color_format = video->rgb32 ? SSNES_COLOR_FORMAT_ARGB8888 : SSNES_COLOR_FORMAT_XRGB1555,
.xml_shader = g_settings.video.bsnes_shader_path,
.cg_shader = g_settings.video.cg_shader_path,
.ttf_font = *g_settings.video.font_path ? g_settings.video.font_path : NULL,
.ttf_font_size = g_settings.video.font_size
};
const ssnes_input_driver_t *input_driver = NULL;
ext->handle = ext->driver->init(&info, &input_driver);
if (!ext->handle)
return false;
*input = input_driver ? &input_ext : NULL;
if (input_driver)
*input_data = setup_input(ext, input_driver);
else
*input_data = NULL;
return true;
}
static void* video_ext_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
ext_t *ext = calloc(1, sizeof(*ext));
if (!ext)
return NULL;
if (!(*g_settings.video.external_driver))
{
SSNES_ERR("External driver needs video_external_driver path to be set!\n");
goto error;
}
g_lib = dylib_load(g_settings.video.external_driver);
if (!g_lib)
{
SSNES_ERR("Failed to open library: \"%s\"\n", g_settings.video.external_driver);
goto error;
}
const ssnes_video_driver_t* (*video_init)(void) = dylib_proc(g_lib, "ssnes_video_init");
if (!video_init)
{
SSNES_ERR("Couldn't find function ssnes_video_init in library ...\n");
goto error;
}
ext->driver = video_init();
if (!ext->driver)
{
SSNES_ERR("External driver returned invalid driver handle.\n");
goto error;
}
if (!setup_video(ext, video, input, input_data))
{
SSNES_ERR("Failed to start driver.\n");
goto error;
}
g_video_dead = false;
return ext;
error:
video_ext_free(ext);
return NULL;
}
const video_driver_t video_ext = {
.init = video_ext_init,
.frame = video_ext_frame,
.alive = video_ext_alive,
.set_nonblock_state = video_ext_set_nonblock_state,
.focus = video_ext_focus,
.free = video_ext_free,
.ident = "ext"
};
+251
View File
@@ -0,0 +1,251 @@
/////
// API header for external SSNES video and input plugins.
//
//
#ifndef __SSNES_VIDEO_DRIVER_H
#define __SSNES_VIDEO_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#ifdef SSNES_DLL_IMPORT
#define SSNES_API_EXPORT __declspec(dllimport)
#else
#define SSNES_API_EXPORT __declspec(dllexport)
#endif
#define SSNES_API_CALLTYPE __cdecl
#else
#define SSNES_API_EXPORT
#define SSNES_API_CALLTYPE
#endif
#define SSNES_GRAPHICS_API_VERSION 1
// Since we don't want to rely on C++ or C99 for a proper boolean type,
// and make sure return semantics are perfectly clear ... ;)
#ifndef SSNES_OK
#define SSNES_OK 1
#endif
#ifndef SSNES_ERROR
#define SSNES_ERROR 0
#endif
#ifndef SSNES_TRUE
#define SSNES_TRUE 1
#endif
#ifndef SSNES_FALSE
#define SSNES_FALSE 0
#endif
#define SSNES_COLOR_FORMAT_XRGB1555 0
#define SSNES_COLOR_FORMAT_ARGB8888 1
typedef struct ssnes_video_info
{
// Width of window.
// If fullscreen mode is requested,
// a width of 0 means the resolution of the desktop should be used.
unsigned width;
// Height of window.
// If fullscreen mode is requested,
// a height of 0 means the resolutiof the desktop should be used.
unsigned height;
// If true, start the window in fullscreen mode.
int fullscreen;
// If true, VSync should be enabled.
int vsync;
// If true, the output image should have the aspect ratio
// as set in aspect_ratio.
int force_aspect;
// Aspect ratio. Only takes effect if force_aspect is enabled.
float aspect_ratio;
// Requests that the image is smoothed,
// using bilinear filtering or otherwise.
// If this cannot be implemented efficiently, this can be disregarded.
// If smooth is false, nearest-neighbor scaling is requested.
int smooth;
// input_scale defines the maximum size of the picture that will
// ever be used with the frame callback.
// The maximum resolution is a multiple of 256x256 size,
// so an input scale of 2
// means you should allocate a texture or of 512x512.
// It is normally set to 2 due to the possibility of
// pseudo-hires on the SNES.
// CPU filters or otherwise might increase this value.
unsigned input_scale;
// Defines the coloring format used of the input frame.
// XRGB1555 format is 16-bit and has byte ordering: 0RRRRRGGGGGBBBBB,
// in native endian.
// ARGB8888 is AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB, native endian.
// Alpha channel should be disregarded.
int color_format;
// If non-NULL, requests the use of an XML shader.
// Can be disregarded.
const char *xml_shader;
// If non-NULL, requests the use of a Cg shader.
// Can be disregarded.
// If both are non-NULL,
// Cg or XML could be used at the discretion of the plugin.
const char *cg_shader;
// Requestes that a certain
// TTF font is used for rendering messages to the screen.
// Can be disregarded.
const char *ttf_font;
unsigned ttf_font_size;
} ssnes_video_info_t;
// Some convenience macros.
// Extract which axes to test for in negative or positive direction.
// May be equal to SSNES_NO_AXIS, which means testing should not occur.
#define SSNES_AXIS_NEG_GET(x) (((unsigned)(x) >> 16) & 0xFFFFU)
#define SSNES_AXIS_POS_GET(x) ((unsigned)(x) & 0xFFFFU)
// I hope no joypad will ever have this many buttons or axes ... ;)
// If joykey is this value, do not check that button.
#define SSNES_NO_AXIS ((unsigned short)0xFFFFU)
#define SSNES_NO_BTN ((unsigned short)0xFFFFU)
// Masks to test on joykey which hat direction is to be tested for.
#define SSNES_HAT_UP_MASK (1 << 15)
#define SSNES_HAT_DOWN_MASK (1 << 14)
#define SSNES_HAT_LEFT_MASK (1 << 13)
#define SSNES_HAT_RIGHT_MASK (1 << 12)
#define SSNES_HAT_MAP(x, hat) ((x & ((1 << 12) - 1)) | hat)
#define SSNES_HAT_MASK (HAT_UP_MASK | HAT_DOWN_MASK | \
HAT_LEFT_MASK | HAT_RIGHT_MASK)
// Test this on the joykey. If true, we want to test for a joypad hat
// rather than a button.
#define SSNES_GET_HAT_DIR(x) (x & SSNES_HAT_MASK)
// Gets the joypad hat to be tested for.
// Only valid when SSNES_GET_HAT_DIR() returns true.
#define SSNES_GET_HAT(x) (x & (~SSNES_HAT_MASK))
// key, joykey and joyaxis are all checked at the same time.
// If any one of these are pressed, return 1 in state callback.
struct ssnes_keybind
{
// If analog_x is true, we request an analog device to be polled
// rather than normal keys.
// The returned value should be the delta of
// last frame and current frame in the X-axis.
int analog_x;
// If analog_y is true, we request an analog device to be polled
// rather than normal keys.
// The returned value should be the delta of
// last frame and current frame in the Y-axis.
int analog_y;
// Keyboard key. The key values use the SDL keysyms,
// which probably need to be transformed to the native format.
unsigned short key;
// Joypad key. Joypad POV (hats) are embedded into this key as well.
unsigned short joykey;
// Joypad axis. Negative and positive axes are embedded into this variable.
unsigned joyaxis;
};
typedef struct ssnes_input_driver
{
// Inits input driver.
// Joypad index denotes which joypads are desired for the various players.
// Should an entry be negative,
// do not open joypad for that player.
// Threshold states the minimum offset that a joypad axis
// has to be held for it to be registered.
// The range of this is [0, 1],
// where 0 means any displacement will register,
// and 1 means the axis has to be pressed all the way to register.
void* (*init)(const int joypad_index[5], float axis_threshold);
// Polls input. Called once every frame.
void (*poll)(void* data);
// Queries input state for a certain key on a certain player.
// Players are 1 - 5.
// For digital inputs, pressed key is 1, not pressed key is 0.
// Analog values have same range as a signed 16-bit integer.
int (*input_state)(void* data, const struct ssnes_keybind *bind,
unsigned player);
// Frees the input struct.
void (*free)(void* data);
// Human readable indentification string.
const char *ident;
} ssnes_input_driver_t;
typedef struct ssnes_video_driver
{
// Inits the video driver. Returns an opaque handle pointer to the driver.
// Returns NULL on error.
//
// Should the video driver request that a certain input driver is used,
// it is possible to set the driver to *input.
// If no certain driver is desired, set *input to NULL.
void* (*init)(const ssnes_video_info_t *video,
const ssnes_input_driver_t **input);
// Updates frame on the screen.
// Frame can be either XRGB1555 or ARGB32 format
// depending on rgb32 setting in ssnes_video_info_t.
// Pitch is the distance in bytes between two scanlines in memory.
//
// When msg is non-NULL,
// it's a message that should be displayed to the user.
int (*frame)(void* data, const void* frame,
unsigned width, unsigned height, unsigned pitch, const char *msg);
// Requests nonblocking operation.
// True = VSync is turned off.
// False = VSync is turned on.
void (*set_nonblock_state)(void* data, int toggle);
// This must return false when the user exits the emulator.
int (*alive)(void *data);
// Does the window have focus?
int (*focus)(void *data);
// Frees the video driver.
void (*free)(void* data);
// A human-readable identification of the video driver.
const char *ident;
// Needs to be defined to SSNES_GRAPHICS_API_VERSION.
// This is used to detect API mismatches.
int api_version;
} ssnes_video_driver_t;
// Called by SSNES on startup to get a driver handle.
// This is NOT dynamically allocated.
SSNES_API_EXPORT const ssnes_video_driver_t* SSNES_API_CALLTYPE
ssnes_video_init(void);
#ifdef __cplusplus
}
#endif
#endif
+48 -6
View File
@@ -103,6 +103,23 @@ static bool load_fbo_proc(void) { return true; }
#endif
#endif
#if defined(HAVE_XML) && defined(_WIN32)
PFNGLCLIENTACTIVETEXTUREPROC pglClientActiveTexture = NULL;
PFNGLACTIVETEXTUREPROC pglActiveTexture = NULL;
static inline bool load_gl_proc(void)
{
LOAD_SYM(glClientActiveTexture);
LOAD_SYM(glActiveTexture);
return pglClientActiveTexture && pglActiveTexture;
}
#else
#define pglClientActiveTexture glClientActiveTexture
#define pglActiveTexture glActiveTexture
static inline bool load_gl_proc(void) { return true; }
#endif
#define MAX_SHADERS 16
typedef struct gl
@@ -111,6 +128,8 @@ typedef struct gl
GLuint texture;
GLuint tex_filter;
unsigned frame_count;
#ifdef HAVE_FBO
// Render-to-texture, multipass shaders
GLuint fbo[MAX_SHADERS];
@@ -231,14 +250,15 @@ static void gl_shader_set_proj_matrix(void)
static void gl_shader_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height)
unsigned out_width, unsigned out_height,
unsigned frame_count)
{
#ifdef HAVE_CG
gl_cg_set_params(width, height, tex_width, tex_height, out_width, out_height);
gl_cg_set_params(width, height, tex_width, tex_height, out_width, out_height, frame_count);
#endif
#ifdef HAVE_XML
gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height);
gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height, frame_count);
#endif
}
@@ -634,6 +654,8 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
gl_shader_use(1);
gl->frame_count++;
#ifdef HAVE_FBO
// Render to texture in first pass.
if (gl->fbo_inited)
@@ -746,7 +768,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
}
glClear(GL_COLOR_BUFFER_BIT);
gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height);
gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height, gl->frame_count);
if (width != gl->last_width || height != gl->last_height) // Res change. need to clear out texture.
{
@@ -812,7 +834,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
// Render to FBO with certain size.
set_viewport(gl, rect->img_width, rect->img_height, true);
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height);
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height, gl->frame_count);
glDrawArrays(GL_QUADS, 0, 4);
}
@@ -833,7 +855,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
glClear(GL_COLOR_BUFFER_BIT);
gl->render_to_tex = false;
set_viewport(gl, gl->win_width, gl->win_height, false);
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height);
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height, gl->frame_count);
glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[gl->fbo_pass - 1]);
glDrawArrays(GL_QUADS, 0, 4);
@@ -923,10 +945,22 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
if (attr <= 0)
SSNES_WARN("GL double buffer has not been enabled!\n");
#if defined(HAVE_XML) && defined(_WIN32)
// Win32 GL lib doesn't have some functions needed for XML shaders.
// Need to load dynamically :(
if (!load_gl_proc())
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return NULL;
}
#endif
gl_t *gl = calloc(1, sizeof(gl_t));
if (!gl)
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return NULL;
}
gl->full_x = full_x;
gl->full_y = full_y;
@@ -1004,6 +1038,14 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
#ifdef HAVE_XML
// For texture images.
pglClientActiveTexture(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tex_coords);
pglClientActiveTexture(GL_TEXTURE0);
#endif
gl->tex_w = 256 * video->input_scale;
gl->tex_h = 256 * video->input_scale;
+14
View File
@@ -19,6 +19,11 @@
#define __GL_COMMON_H
#include "general.h"
#include <assert.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef __APPLE__
#include <OpenGL/gl.h>
@@ -93,4 +98,13 @@ struct gl_fbo_scale
bool valid;
};
// Windows ... <_<
#if defined(HAVE_XML) && defined(_WIN32)
extern PFNGLCLIENTACTIVETEXTUREPROC pglClientActiveTexture;
extern PFNGLACTIVETEXTUREPROC pglActiveTexture;
#else
#define pglClientActiveTexture glClientActiveTexture
#define pglActiveTexture glActiveTexture
#endif
#endif
+142
View File
@@ -0,0 +1,142 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "image.h"
#include "file.h"
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "general.h"
#ifdef HAVE_IMLIB
#include <Imlib2.h>
bool texture_image_load(const char *path, struct texture_image *out_img)
{
Imlib_Image img;
img = imlib_load_image(path);
if (!img)
return false;
imlib_context_set_image(img);
out_img->width = imlib_image_get_width();
out_img->height = imlib_image_get_height();
size_t size = out_img->width * out_img->height * sizeof(uint32_t);
out_img->pixels = malloc(size);
if (!out_img->pixels)
{
imlib_free_image();
return false;
}
const uint32_t *read = imlib_image_get_data_for_reading_only();
// Convert ARGB -> RGBA.
for (unsigned i = 0; i < size / sizeof(uint32_t); i++)
out_img->pixels[i] = (read[i] >> 24) | (read[i] << 8);
imlib_free_image();
return true;
}
#else
bool texture_image_load(const char *path, struct texture_image *out_img)
{
// TODO: Check more gracefully.
if (!strstr(path, ".tga"))
return false;
void *raw_buf = NULL;
ssize_t len = read_file(path, &raw_buf);
if (len < 0)
return false;
uint8_t *buf = raw_buf;
if (buf[2] != 2) // Uncompressed RGB
{
free(buf);
return false;
}
unsigned width = 0;
unsigned height = 0;
uint8_t info[6];
memcpy(info, buf + 12, 6);
width = info[0] + ((unsigned)info[1] * 256);
height = info[2] + ((unsigned)info[3] * 256);
unsigned bits = info[4];
SSNES_LOG("Loaded TGA: (%ux%u @ %u bpp)\n", width, height, bits);
unsigned size = width * height * sizeof(uint32_t);
out_img->pixels = malloc(size);
out_img->width = width;
out_img->height = height;
if (!out_img->pixels)
{
free(buf);
return false;
}
const uint8_t *tmp = buf + 18;
if (bits == 32)
{
for (unsigned i = 0; i < width * height; i++)
{
uint32_t r = tmp[i * 4 + 2];
uint32_t g = tmp[i * 4 + 1];
uint32_t b = tmp[i * 4 + 0];
uint32_t a = tmp[i * 4 + 3];
out_img->pixels[i] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
else if (bits == 24)
{
for (unsigned i = 0; i < width * height; i++)
{
uint32_t r = tmp[i * 3 + 2];
uint32_t g = tmp[i * 3 + 1];
uint32_t b = tmp[i * 3 + 0];
uint32_t a = 0xff;
out_img->pixels[i] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
else
{
free(buf);
free(out_img->pixels);
out_img->pixels = NULL;
return false;
}
free(buf);
return true;
}
#endif
+34
View File
@@ -0,0 +1,34 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SSNES_IMAGE_H
#define __SSNES_IMAGE_H
#include <stdint.h>
#include <stdbool.h>
struct texture_image
{
unsigned width;
unsigned height;
uint32_t *pixels;
};
bool texture_image_load(const char *path, struct texture_image* img);
#endif
+8 -1
View File
@@ -63,9 +63,11 @@ struct cg_program
CGparameter vid_size_f;
CGparameter tex_size_f;
CGparameter out_size_f;
CGparameter frame_cnt_f;
CGparameter vid_size_v;
CGparameter tex_size_v;
CGparameter out_size_v;
CGparameter frame_cnt_v;
CGparameter mvp;
};
@@ -82,17 +84,20 @@ void gl_cg_set_proj_matrix(void)
void gl_cg_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height)
unsigned out_width, unsigned out_height,
unsigned frame_count)
{
if (cg_active)
{
cgGLSetParameter2f(prg[active_index].vid_size_f, width, height);
cgGLSetParameter2f(prg[active_index].tex_size_f, tex_width, tex_height);
cgGLSetParameter2f(prg[active_index].out_size_f, out_width, out_height);
cgGLSetParameter1f(prg[active_index].frame_cnt_f, (float)frame_count);
cgGLSetParameter2f(prg[active_index].vid_size_v, width, height);
cgGLSetParameter2f(prg[active_index].tex_size_v, tex_width, tex_height);
cgGLSetParameter2f(prg[active_index].out_size_v, out_width, out_height);
cgGLSetParameter1f(prg[active_index].frame_cnt_v, (float)frame_count);
}
}
@@ -165,9 +170,11 @@ bool gl_cg_init(const char *path)
prg[i].vid_size_f = cgGetNamedParameter(prg[i].fprg, "IN.video_size");
prg[i].tex_size_f = cgGetNamedParameter(prg[i].fprg, "IN.texture_size");
prg[i].out_size_f = cgGetNamedParameter(prg[i].fprg, "IN.output_size");
prg[i].frame_cnt_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_count");
prg[i].vid_size_v = cgGetNamedParameter(prg[i].vprg, "IN.video_size");
prg[i].tex_size_v = cgGetNamedParameter(prg[i].vprg, "IN.texture_size");
prg[i].out_size_v = cgGetNamedParameter(prg[i].vprg, "IN.output_size");
prg[i].frame_cnt_v = cgGetNamedParameter(prg[i].fprg, "IN.frame_count");
prg[i].mvp = cgGetNamedParameter(prg[i].vprg, "modelViewProj");
cgGLSetStateMatrixParameter(prg[i].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
}
+2 -1
View File
@@ -30,7 +30,8 @@ void gl_cg_set_proj_matrix(void);
void gl_cg_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height);
unsigned out_width, unsigned out_height,
unsigned frame_count);
void gl_cg_use(unsigned index);
+115 -1
View File
@@ -19,6 +19,7 @@
#include <string.h>
#include "general.h"
#include "shader_glsl.h"
#include "strl.h"
#ifdef __APPLE__
#include <OpenGL/gl.h>
@@ -37,6 +38,8 @@
#include <libxml/tree.h>
#include "gl_common.h"
#include "image.h"
#ifdef __APPLE__
#define pglCreateProgram glCreateProgram
@@ -81,6 +84,7 @@ static PFNGLGETATTACHEDSHADERSPROC pglGetAttachedShaders = NULL;
#endif
#define MAX_PROGRAMS 16
#define MAX_TEXTURES 8
enum filter_type
{
@@ -96,6 +100,10 @@ static struct gl_fbo_scale gl_scale[MAX_PROGRAMS];
static unsigned gl_num_programs = 0;
static unsigned active_index = 0;
static GLuint gl_teximage[MAX_TEXTURES];
static unsigned gl_teximage_cnt = 0;
static char gl_teximage_uniforms[MAX_TEXTURES][64];
struct shader_program
{
char *vertex;
@@ -273,6 +281,90 @@ static bool get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr)
return true;
}
static bool get_texture_image(const char *shader_path, xmlNodePtr ptr)
{
if (gl_teximage_cnt >= MAX_TEXTURES)
{
SSNES_WARN("Too many texture images! Ignoring ...\n");
return true;
}
bool linear = true;
xmlChar *filename = xmlGetProp(ptr, (const xmlChar*)"file");
xmlChar *filter = xmlGetProp(ptr, (const xmlChar*)"filter");
xmlChar *id = xmlGetProp(ptr, (const xmlChar*)"id");
if (!id)
{
SSNES_ERR("Could not find ID in texture.\n");
goto error;
}
if (!filename)
{
SSNES_ERR("Could not find filename in texture.\n");
goto error;
}
if (filter && strcmp((const char*)filter, "nearest") == 0)
linear = false;
char tex_path[256];
strlcpy(tex_path, shader_path, sizeof(tex_path));
char *last = strrchr(tex_path, '/');
if (!last) last = strrchr(tex_path, '\\');
if (last) last[1] = '\0';
strlcat(tex_path, (const char*)filename, sizeof(tex_path));
struct texture_image img;
SSNES_LOG("Loading texture image from: \"%s\" ...\n", tex_path);
if (!texture_image_load(tex_path, &img))
{
SSNES_ERR("Failed to load texture image from: \"%s\"\n", tex_path);
goto error;
}
strlcpy(gl_teximage_uniforms[gl_teximage_cnt], (const char*)id, sizeof(gl_teximage_uniforms[0]));
glGenTextures(1, &gl_teximage[gl_teximage_cnt]);
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, img.width);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
pglActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
free(img.pixels);
xmlFree(filename);
xmlFree(id);
if (filter)
xmlFree(filter);
gl_teximage_cnt++;
return true;
error:
if (filename)
xmlFree(filename);
if (filter)
xmlFree(filter);
if (filter)
xmlFree(id);
return false;
}
static unsigned get_xml_shaders(const char *path, struct shader_program *prog, size_t size)
{
LIBXML_TEST_VERSION;
@@ -354,6 +446,14 @@ static unsigned get_xml_shaders(const char *path, struct shader_program *prog, s
}
num++;
}
else if (strcmp((const char*)cur->name, "texture") == 0)
{
if (!get_texture_image(path, cur))
{
SSNES_ERR("Texture image failed to load.\n");
goto error;
}
}
}
if (num == 0)
@@ -587,6 +687,10 @@ void gl_glsl_deinit(void)
pglDeleteProgram(gl_program[i]);
}
glDeleteTextures(gl_teximage_cnt, gl_teximage);
gl_teximage_cnt = 0;
memset(gl_teximage_uniforms, 0, sizeof(gl_teximage_uniforms));
}
memset(gl_program, 0, sizeof(gl_program));
@@ -596,7 +700,8 @@ void gl_glsl_deinit(void)
void gl_glsl_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height)
unsigned out_width, unsigned out_height,
unsigned frame_count)
{
if (glsl_enable && gl_program[active_index] > 0)
{
@@ -613,6 +718,15 @@ void gl_glsl_set_params(unsigned width, unsigned height,
float textureSize[2] = {tex_width, tex_height};
location = pglGetUniformLocation(gl_program[active_index], "rubyTextureSize");
pglUniform2fv(location, 1, textureSize);
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameCount");
pglUniform1i(location, frame_count);
for (unsigned i = 0; i < gl_teximage_cnt; i++)
{
location = pglGetUniformLocation(gl_program[active_index], gl_teximage_uniforms[i]);
pglUniform1i(location, i + 1);
}
}
}
+2 -1
View File
@@ -30,7 +30,8 @@ void gl_glsl_set_proj_matrix(void);
void gl_glsl_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height);
unsigned out_width, unsigned out_height,
unsigned frame_counter);
void gl_glsl_use(unsigned index);
+27
View File
@@ -59,6 +59,33 @@ static const struct key_bind lut_binds[] = {
{ XK_Escape, SDLK_ESCAPE },
{ XK_KP_Add, SDLK_KP_PLUS },
{ XK_KP_Subtract, SDLK_KP_MINUS },
{ XK_BackSpace, SDLK_BACKSPACE },
{ XK_KP_Enter, SDLK_KP_ENTER },
{ XK_KP_Add, SDLK_KP_PLUS },
{ XK_KP_Subtract, SDLK_KP_MINUS },
{ XK_KP_Multiply, SDLK_KP_MULTIPLY },
{ XK_KP_Divide, SDLK_KP_DIVIDE },
{ XK_grave, SDLK_BACKQUOTE },
{ XK_KP_0, SDLK_KP0 },
{ XK_KP_1, SDLK_KP1 },
{ XK_KP_2, SDLK_KP2 },
{ XK_KP_3, SDLK_KP3 },
{ XK_KP_4, SDLK_KP4 },
{ XK_KP_5, SDLK_KP5 },
{ XK_KP_6, SDLK_KP6 },
{ XK_KP_7, SDLK_KP7 },
{ XK_KP_8, SDLK_KP8 },
{ XK_KP_9, SDLK_KP9 },
{ XK_0, SDLK_0 },
{ XK_1, SDLK_1 },
{ XK_2, SDLK_2 },
{ XK_3, SDLK_3 },
{ XK_4, SDLK_4 },
{ XK_5, SDLK_5 },
{ XK_6, SDLK_6 },
{ XK_7, SDLK_7 },
{ XK_8, SDLK_8 },
{ XK_9, SDLK_9 },
{ XK_F1, SDLK_F1 },
{ XK_F2, SDLK_F2 },
{ XK_F3, SDLK_F3 },
+3 -2
View File
@@ -15,7 +15,7 @@ if [ $HAVE_DYNAMIC != yes ]; then
add_define_make libsnes $LIBSNES
fi
check_lib FILTER -ldl dlopen
check_lib DYLIB -ldl dlopen
check_lib NETPLAY -lc socket
check_lib ALSA -lasound snd_pcm_open
@@ -43,6 +43,7 @@ check_critical SDL "Cannot find SDL library."
check_lib CG -lCg cgCreateContext
check_pkgconf XML libxml-2.0
check_pkgconf IMLIB imlib2
if [ $HAVE_FFMPEG != no ]; then
check_pkgconf AVCODEC libavcodec
@@ -64,7 +65,7 @@ check_lib XVIDEO -lXv XvShmCreateImage
check_lib STRL -lc strlcpy
# Creates config.mk and config.h.
VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL"
VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML IMLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL"
create_config_make config.mk $VARS
create_config_header config.h $VARS
+3 -2
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.4.1
PACKAGE_VERSION=0.5
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -10,7 +10,7 @@ PACKAGE_VERSION=0.4.1
add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library." no
add_command_line_string LIBSNES "libsnes library used" "-lsnes"
add_command_line_enable FFMPEG "Enable FFmpeg recording support" no
add_command_line_enable FILTER "Enable CPU filter support" auto
add_command_line_enable DYLIB "Enable dynamic loading support" auto
add_command_line_enable NETPLAY "Enable netplay support" auto
add_command_line_enable SRC "Enable libsamplerate support" no
add_command_line_enable CONFIGFILE "Disable support for config file" yes
@@ -26,3 +26,4 @@ add_command_line_enable JACK "Enable JACK support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
add_command_line_enable XVIDEO "Enable XVideo support" auto
add_command_line_enable IMLIB "Enable imlib2 support" auto
+111
View File
@@ -0,0 +1,111 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "screenshot.h"
#include "strl.h"
#include <stdio.h>
#include <time.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "general.h"
// Simple 24bpp .BMP writer.
static void write_header(FILE *file, unsigned width, unsigned height)
{
unsigned line_size = (width * 3 + 3) & ~3;
unsigned size = line_size * height + 54;
unsigned size_array = line_size * height;
// Generic BMP stuff.
const uint8_t header[] = {
'B', 'M',
(size >> 0) & 0xff, (size >> 8) & 0xff, (size >> 16) & 0xff, (size >> 24) & 0xff,
0, 0, 0, 0,
54, 0, 0, 0,
40, 0, 0, 0,
(width >> 0) & 0xff, (width >> 8) & 0xff, (width >> 16) & 0xff, (width >> 24) & 0xff,
(height >> 0) & 0xff, (height >> 8) & 0xff, (height >> 16) & 0xff, (height >> 24) & 0xff,
1, 0,
24, 0,
0, 0, 0, 0,
(size_array >> 0) & 0xff, (size_array >> 8) & 0xff, (size_array >> 16) & 0xff, (size_array >> 24) & 0xff,
19, 11, 0, 0,
19, 11, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
fwrite(header, 1, sizeof(header), file);
}
static void dump_content(FILE *file, const uint16_t *frame, unsigned width, unsigned height, unsigned pitch)
{
pitch >>= 1;
unsigned line_size = (width * 3 + 3) & ~3;
uint8_t line[line_size];
memset(line, 0, sizeof(line));
// BMP likes reverse ordering for some reason :v
for (int j = height - 1; j >= 0; j--)
{
uint8_t *dst = line;
const uint16_t *src = frame + j * pitch;
for (unsigned i = 0; i < width; i++)
{
uint16_t pixel = *src++;
uint8_t b = (pixel & 0x1f) << 3;
uint8_t g = (pixel & 0x03e0) >> 2;
uint8_t r = (pixel & 0x7c00) >> 7;
*dst++ = b;
*dst++ = g;
*dst++ = r;
}
fwrite(line, 1, sizeof(line), file);
}
}
bool screenshot_dump(const char *folder, const uint16_t *frame,
unsigned width, unsigned height, unsigned pitch)
{
time_t cur_time;
time(&cur_time);
char timefmt[128];
strftime(timefmt, sizeof(timefmt), "SSNES-%m%d-%H%M%S.bmp", localtime(&cur_time));
char filename[256];
strlcpy(filename, folder, sizeof(filename));
strlcat(filename, "/", sizeof(filename));
strlcat(filename, timefmt, sizeof(filename));
FILE *file = fopen(filename, "wb");
if (!file)
{
SSNES_ERR("Failed to open file \"%s\" for screenshot.\n", filename);
return false;
}
write_header(file, width, height);
dump_content(file, frame, width, height, pitch);
fclose(file);
return true;
}
+27
View File
@@ -0,0 +1,27 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SSNES_SCREENSHOT_H
#define __SSNES_SCREENSHOT_H
#include <stdint.h>
#include <stdbool.h>
bool screenshot_dump(const char *folder, const uint16_t *frame,
unsigned width, unsigned height, unsigned pitch);
#endif
+63 -12
View File
@@ -50,6 +50,9 @@ static void set_defaults(void)
case VIDEO_SDL:
def_video = "sdl";
break;
case VIDEO_EXT:
def_video = "ext";
break;
default:
break;
}
@@ -80,6 +83,9 @@ static void set_defaults(void)
case AUDIO_PULSE:
def_audio = "pulse";
break;
case AUDIO_EXT:
def_audio = "ext";
break;
default:
break;
}
@@ -112,6 +118,7 @@ static void set_defaults(void)
g_settings.video.vsync = vsync;
g_settings.video.smooth = video_smooth;
g_settings.video.force_aspect = force_aspect;
g_settings.video.crop_overscan = crop_overscan;
g_settings.video.aspect_ratio = SNES_ASPECT_RATIO;
g_settings.video.shader_type = SSNES_SHADER_AUTO;
@@ -148,15 +155,10 @@ static void set_defaults(void)
g_settings.autosave_interval = autosave_interval;
assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1));
assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_2));
assert(sizeof(g_settings.input.binds[2]) >= sizeof(snes_keybinds_3));
assert(sizeof(g_settings.input.binds[3]) >= sizeof(snes_keybinds_4));
assert(sizeof(g_settings.input.binds[4]) >= sizeof(snes_keybinds_5));
assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_rest));
memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1));
memcpy(g_settings.input.binds[1], snes_keybinds_2, sizeof(snes_keybinds_2));
memcpy(g_settings.input.binds[2], snes_keybinds_3, sizeof(snes_keybinds_3));
memcpy(g_settings.input.binds[3], snes_keybinds_4, sizeof(snes_keybinds_4));
memcpy(g_settings.input.binds[4], snes_keybinds_5, sizeof(snes_keybinds_5));
for (unsigned i = 1; i < 5; i++)
memcpy(g_settings.input.binds[i], snes_keybinds_rest, sizeof(snes_keybinds_rest));
g_settings.input.axis_threshold = AXIS_THRESHOLD;
g_settings.input.netplay_client_swap_input = netplay_client_swap_input;
@@ -210,7 +212,7 @@ static config_file_t *open_default_config_file(void)
#else
const char *xdg = getenv("XDG_CONFIG_HOME");
if (!xdg)
SSNES_WARN("XDG_CONFIG_HOME is not defined. Will look for config in $HOME/.ssnesrc ...\n");
SSNES_WARN("XDG_CONFIG_HOME is not defined. Will look for config in $HOME/.ssnes.cfg ...\n");
const char *home = getenv("HOME");
if (xdg)
@@ -290,6 +292,7 @@ static void parse_config_file(void)
CONFIG_GET_BOOL(video.vsync, "video_vsync");
CONFIG_GET_BOOL(video.smooth, "video_smooth");
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
CONFIG_GET_BOOL(video.crop_overscan, "video_crop_overscan");
CONFIG_GET_DOUBLE(video.aspect_ratio, "video_aspect_ratio");
CONFIG_GET_STRING(video.cg_shader_path, "video_cg_shader");
@@ -307,8 +310,10 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
#endif
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
CONFIG_GET_STRING(video.filter_path, "video_filter");
CONFIG_GET_STRING(video.external_driver, "video_external_driver");
CONFIG_GET_STRING(audio.external_driver, "audio_external_driver");
#endif
#if defined(HAVE_CG) || defined(HAVE_XML)
@@ -361,8 +366,10 @@ static void parse_config_file(void)
CONFIG_GET_STRING(video.driver, "video_driver");
CONFIG_GET_STRING(audio.driver, "audio_driver");
CONFIG_GET_STRING(audio.dsp_plugin, "audio_dsp_plugin");
CONFIG_GET_STRING(input.driver, "input_driver");
CONFIG_GET_STRING(libsnes, "libsnes_path");
CONFIG_GET_STRING(screenshot_directory, "screenshot_directory");
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
@@ -424,6 +431,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
},
{
DECLARE_BIND(player2_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -456,6 +465,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
},
{
DECLARE_BIND(player3_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -488,6 +499,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
},
{
DECLARE_BIND(player4_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -520,6 +533,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
},
{
DECLARE_BIND(player5_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -552,6 +567,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
},
};
@@ -569,17 +586,20 @@ static const struct key_map sdlk_map[] = {
{ "up", SDLK_UP },
{ "down", SDLK_DOWN },
{ "enter", SDLK_RETURN },
{ "kp_enter", SDLK_KP_ENTER },
{ "tab", SDLK_TAB },
{ "insert", SDLK_INSERT },
{ "del", SDLK_DELETE },
{ "end", SDLK_END },
{ "home", SDLK_HOME },
{ "rshift", SDLK_RSHIFT },
{ "shift", SDLK_LSHIFT },
{ "ctrl", SDLK_LCTRL },
{ "alt", SDLK_LALT },
{ "space", SDLK_SPACE },
{ "escape", SDLK_ESCAPE },
{ "kp_plus", SDLK_KP_PLUS },
{ "kp_minus", SDLK_KP_MINUS },
{ "add", SDLK_KP_PLUS },
{ "subtract", SDLK_KP_MINUS },
{ "f1", SDLK_F1 },
{ "f2", SDLK_F2 },
{ "f3", SDLK_F3 },
@@ -592,6 +612,37 @@ static const struct key_map sdlk_map[] = {
{ "f10", SDLK_F10 },
{ "f11", SDLK_F11 },
{ "f12", SDLK_F12 },
{ "num0", SDLK_0 },
{ "num1", SDLK_1 },
{ "num2", SDLK_2 },
{ "num3", SDLK_3 },
{ "num4", SDLK_4 },
{ "num5", SDLK_5 },
{ "num6", SDLK_6 },
{ "num7", SDLK_7 },
{ "num8", SDLK_8 },
{ "num9", SDLK_9 },
{ "pageup", SDLK_PAGEUP },
{ "pagedown", SDLK_PAGEDOWN },
{ "keypad0", SDLK_KP0 },
{ "keypad1", SDLK_KP1 },
{ "keypad2", SDLK_KP2 },
{ "keypad3", SDLK_KP3 },
{ "keypad4", SDLK_KP4 },
{ "keypad5", SDLK_KP5 },
{ "keypad6", SDLK_KP6 },
{ "keypad7", SDLK_KP7 },
{ "keypad8", SDLK_KP8 },
{ "keypad9", SDLK_KP9 },
{ "period", SDLK_PERIOD },
{ "capslock", SDLK_CAPSLOCK },
{ "numlock", SDLK_NUMLOCK },
{ "backspace", SDLK_BACKSPACE },
{ "multiply", SDLK_KP_MULTIPLY },
{ "divide", SDLK_KP_DIVIDE },
{ "print_screen", SDLK_PRINT },
{ "scroll_lock", SDLK_SCROLLOCK },
{ "tilde", SDLK_BACKQUOTE },
{ "nul", SDLK_UNKNOWN },
};
+145 -41
View File
@@ -32,6 +32,7 @@
#include "movie.h"
#include "netplay.h"
#include "strl.h"
#include "screenshot.h"
#include "cheats.h"
#include <assert.h>
#ifdef HAVE_SRC
@@ -78,13 +79,54 @@ static void set_fast_forward_button(bool new_button_state)
old_button_state = new_button_state;
}
static void take_screenshot(const uint16_t *frame, unsigned width, unsigned height)
{
if (!*(g_settings.screenshot_directory))
return;
bool ret = screenshot_dump(g_settings.screenshot_directory, frame,
width, height, (height == 448 || height == 478) ? 1024 : 2048);
const char *msg = NULL;
if (ret)
{
SSNES_LOG("Taking screenshot!\n");
msg = "Taking screenshot!";
}
else
{
SSNES_WARN("Failed to take screenshot ...\n");
msg = "Failed to take screenshot!";
}
msg_queue_clear(g_extern.msg_queue);
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
}
// libsnes: 0.065
// Format received is 16-bit 0RRRRRGGGGGBBBBB
static void video_frame(const uint16_t *data, unsigned width, unsigned height)
{
if ( !g_extern.video_active )
if (!g_extern.video_active)
return;
if (g_settings.video.crop_overscan)
{
if (height == 239)
{
data += 7 * 1024; // Skip 7 top scanlines.
height = 224;
}
else if (height == 478)
{
data += 15 * 512; // Skip 15 top scanlines.
height = 448;
}
}
if (g_extern.do_screenshot)
take_screenshot(data, width, height);
#ifdef HAVE_FFMPEG
if (g_extern.recording)
{
@@ -100,7 +142,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
const char *msg = msg_queue_pull(g_extern.msg_queue);
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
if (g_extern.filter.active)
{
unsigned owidth = width;
@@ -138,58 +180,85 @@ static void audio_sample(uint16_t left, uint16_t right)
}
#endif
const float *output_data = NULL;
unsigned output_frames = 0;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
if (g_extern.audio_data.data_ptr >= g_extern.audio_data.chunk_size)
{
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
return;
if (g_extern.frame_is_reverse) // Disable fucked up audio when rewinding...
memset(g_extern.audio_data.data, 0, g_extern.audio_data.chunk_size * sizeof(float));
ssnes_dsp_input_t dsp_input = {
.samples = g_extern.audio_data.data,
.frames = g_extern.audio_data.data_ptr / 2
};
ssnes_dsp_output_t dsp_output = {
.should_resample = SSNES_TRUE
};
if (g_extern.audio_data.dsp_plugin)
g_extern.audio_data.dsp_plugin->process(g_extern.audio_data.dsp_handle, &dsp_output, &dsp_input);
if (g_extern.frame_is_reverse) // Disable fucked up audio when rewinding...
memset(g_extern.audio_data.data, 0, g_extern.audio_data.chunk_size * sizeof(float));
#ifdef HAVE_SRC
SRC_DATA src_data = {
SRC_DATA src_data = {
#else
struct hermite_data src_data = {
struct hermite_data src_data = {
#endif
.data_in = g_extern.audio_data.data,
.data_out = g_extern.audio_data.outsamples,
.input_frames = g_extern.audio_data.chunk_size / 2,
.output_frames = g_extern.audio_data.chunk_size * 8,
.end_of_input = 0,
.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
};
.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data,
.data_out = g_extern.audio_data.outsamples,
.input_frames = dsp_output.samples ? dsp_output.frames : (g_extern.audio_data.data_ptr / 2),
.output_frames = g_extern.audio_data.chunk_size * 8,
.end_of_input = 0,
.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
};
if (dsp_output.should_resample)
{
#ifdef HAVE_SRC
src_process(g_extern.audio_data.source, &src_data);
#else
hermite_process(g_extern.audio_data.source, &src_data);
#endif
if (g_extern.audio_data.use_float)
{
if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, src_data.output_frames_gen * sizeof(float) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
else
{
for (unsigned i = 0; i < src_data.output_frames_gen * 2; i++)
{
int32_t val = g_extern.audio_data.outsamples[i] * 0x8000;
g_extern.audio_data.conv_outsamples[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
}
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * sizeof(int16_t) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
g_extern.audio_data.data_ptr = 0;
output_data = g_extern.audio_data.outsamples;
output_frames = src_data.output_frames_gen;
}
else
{
output_data = dsp_output.samples;
output_frames = dsp_output.frames;
}
if (g_extern.audio_data.use_float)
{
if (driver.audio->write(driver.audio_data, output_data, output_frames * sizeof(float) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
else
{
for (unsigned i = 0; i < output_frames * 2; i++)
{
int32_t val = output_data[i] * 0x8000;
g_extern.audio_data.conv_outsamples[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
}
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, output_frames * sizeof(int16_t) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
g_extern.audio_data.data_ptr = 0;
}
static void input_poll(void)
@@ -249,7 +318,7 @@ static void fill_pathname_noext(char *out_path, const char *in_path, const char
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.4.1"
#define PACKAGE_VERSION "0.5"
#endif
#include "config.features.h"
@@ -268,9 +337,10 @@ static void print_features(void)
_PSUPP(pulse, "PulseAudio", "audio driver");
_PSUPP(xaudio, "XAudio2", "audio driver");
_PSUPP(al, "OpenAL", "audio driver");
_PSUPP(filter, "Filter", "CPU based video filters");
_PSUPP(dylib, "External", "External filter and driver support");
_PSUPP(cg, "Cg", "Cg pixel shaders");
_PSUPP(xml, "XML", "bSNES XML pixel shaders");
_PSUPP(imlib, "Imlib2", "Imlib2 image loading");
_PSUPP(fbo, "FBO", "OpenGL render-to-texture (multi-pass shaders)");
_PSUPP(dynamic, "Dynamic", "Dynamic run-time loading of libsnes library");
_PSUPP(ffmpeg, "FFmpeg", "On-the-fly recording of gameplay with libavcodec");
@@ -1270,6 +1340,29 @@ static void check_cheats(void)
}
#endif
static void check_screenshot(void)
{
static bool old_pressed = false;
bool pressed = driver.input->key_pressed(driver.input_data, SSNES_SCREENSHOT);
g_extern.do_screenshot = pressed && !old_pressed;
old_pressed = pressed;
}
#ifdef HAVE_DYLIB
static void check_dsp_config(void)
{
if (!g_extern.audio_data.dsp_plugin || !g_extern.audio_data.dsp_plugin->config)
return;
static bool old_pressed = false;
bool pressed = driver.input->key_pressed(driver.input_data, SSNES_DSP_CONFIG);
if (pressed && !old_pressed)
g_extern.audio_data.dsp_plugin->config(g_extern.audio_data.dsp_handle);
old_pressed = pressed;
}
#endif
static void do_state_checks(void)
{
if (!g_extern.netplay)
@@ -1297,10 +1390,21 @@ static void do_state_checks(void)
#ifdef HAVE_XML
check_shader_dir();
#endif
#ifdef HAVE_DYLIB
check_dsp_config();
#endif
}
check_fullscreen();
check_input_rate();
#ifdef HAVE_DYLIB
// DSP plugin doesn't use variable input rate.
if (!g_extern.audio_data.dsp_plugin)
#endif
check_input_rate();
check_screenshot();
}
static void fill_title_buf(void)
+25 -1
View File
@@ -5,6 +5,12 @@
#### Video
# Video driver to use. "gl", "xvideo", "sdl" or "ext" (external API driver)
# video_driver = "gl"
# Path to external video driver using the SSNES driver API.
# video_external_driver =
# Windowed xscale and yscale (Real x res: 224 * xscale * aspect_ratio, real y scale: 224 * xscale)
# video_xscale = 3.0
# video_yscale = 3.0
@@ -31,6 +37,9 @@
# A floating point value for video aspect ratio (width / height)
# video_aspect_ratio = 1.333
# Forces cropping of overscanned frames. Crops away top 7 scanlines and 8 bottom scanlines. (15/15 for interlaced frames).
# video_crop_overscan = false
# Path to Cg shader.
# video_cg_shader = "/path/to/cg/shader.cg"
@@ -90,12 +99,18 @@
# When altering audio_in_rate on-the-fly, define by how much each time.
# audio_rate_step = 0.25
# Audio driver backend. Depending on configuration possible candidates are: alsa, pulse, oss, jack, rsound, roar, openal, sdl and xaudio
# Audio driver backend. Depending on configuration possible candidates are: alsa, pulse, oss, jack, rsound, roar, openal, sdl, xaudio and ext (external driver).
# audio_driver =
# Path to external audio driver using the SSNES audio driver API.
# audio_external_driver =
# Override the default audio device the audio_driver uses. This is driver dependant. E.g. ALSA wants a PCM device, OSS wants a path (e.g. /dev/dsp), Jack wants portnames (e.g. system:playback1,system:playback_2), and so on ...
# audio_device =
# External DSP plugin that processes audio before it's sent to the driver.
# audio_dsp_plugin =
# Will sync (block) on audio. Recommended.
# audio_sync = true
@@ -245,6 +260,9 @@
# Reset the emulated SNES.
# input_reset = h
# Configures DSP plugin
# input_dsp_config = c
# Pause gameplay when window focus is lost.
# pause_nonactive = true
@@ -263,3 +281,9 @@
# input_cheat_index_plus = y
# input_cheat_index_minus = t
# input_cheat_toggle = u
# Take screenshot
# input_screenshot = print_screen
# Directory to dump screenshots to.
# screenshot_directory =
+5
View File
@@ -94,6 +94,11 @@ static struct bind binds[] = {
MISC_BIND("Reset", reset)
MISC_BIND("Next shader", shader_next)
MISC_BIND("Previous shader", shader_prev)
MISC_BIND("Toggle cheat on/off", cheat_toggle)
MISC_BIND("Cheat index plus", cheat_index_plus)
MISC_BIND("Cheat index minus", cheat_index_minus)
MISC_BIND("Screenshot", screenshot)
MISC_BIND("DSP config", dsp_config)
};
static void get_binds(config_file_t *conf, int player, int joypad)