Compare commits

...
12 Commits
16 changed files with 175 additions and 70 deletions

No files matched your search

+1 -1
View File
@@ -162,7 +162,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.7beta1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
zip -r ssnes-win32-0.7.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+1 -1
View File
@@ -149,7 +149,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.7beta1.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
zip -r ssnes-win64-0.7.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
+7 -2
View File
@@ -30,7 +30,7 @@ extern "C" {
#define SSNES_TRUE 1
#endif
#define SSNES_DSP_API_VERSION 2
#define SSNES_DSP_API_VERSION 3
typedef struct ssnes_dsp_info
{
@@ -94,7 +94,7 @@ typedef struct ssnes_dsp_plugin
void (*process)(void *data, ssnes_dsp_output_t *output,
const ssnes_dsp_input_t *input);
// Frees the handle.
// Frees the handle.
void (*free)(void *data);
// API version used to compile the plugin.
@@ -110,6 +110,11 @@ typedef struct ssnes_dsp_plugin
// Human readable identification string.
const char *ident;
// Called every frame, allows creating a GUI main loop in the main thread.
// GUI events can be processed here in a non-blocking fashion.
// Can be set to NULL to ignore it.
void (*events)(void *data);
} ssnes_dsp_plugin_t;
// Called by SSNES at startup to get the callback struct.
+17 -18
View File
@@ -127,9 +127,12 @@ static size_t find_buffersize(jack_t *jd, int latency)
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
{
jack_t *jd = calloc(1, sizeof(jack_t));
if ( jd == NULL )
if (!jd)
return NULL;
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
const char **jports = NULL;
jd->client = jack_client_open("SSNES", JackNullOption, NULL);
@@ -148,7 +151,6 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
SSNES_ERR("Failed to register ports.\n");
goto error;
}
char *dest_ports[2];
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
@@ -158,6 +160,18 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
goto error;
}
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
int parsed = parse_ports(dest_ports, jports);
if (jack_activate(jd->client) < 0)
@@ -178,22 +192,7 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
for (int i = 0; i < parsed; i++)
free(dest_ports[i]);
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
jack_free(jports);
return jd;
+6
View File
@@ -157,6 +157,12 @@ static const float fbo_scale_x = 2.0;
static const float fbo_scale_y = 2.0;
static const bool second_pass_smooth = true;
// Record video assuming game runs hi-res.
static const bool hires_record = false;
// Record post-filtered (CPU filter) video rather than raw SNES output.
static const bool post_filter_record = false;
#define SNES_ASPECT_RATIO (4.0/3)
////////////////
+2 -2
View File
@@ -168,7 +168,7 @@ static void init_dsp_plugin(void)
}
const ssnes_dsp_plugin_t* (*SSNES_API_CALLTYPE plugin_init)(void) =
(const ssnes_dsp_plugin_t *(*)(void))dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
(const ssnes_dsp_plugin_t *(SSNES_API_CALLTYPE*)(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");
@@ -190,7 +190,7 @@ static void init_dsp_plugin(void)
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 = {
const ssnes_dsp_info_t info = {
.input_rate = g_settings.audio.in_rate,
.output_rate = g_settings.audio.out_rate
};
+3
View File
@@ -91,6 +91,9 @@ struct settings
bool force_16bit;
bool disable_composition;
bool hires_record;
bool post_filter_record;
char external_driver[256];
} video;
+29 -1
View File
@@ -110,6 +110,34 @@ void gfx_set_dwm(void)
if (FAILED(ret))
SSNES_ERR("Failed to set composition state ...\n");
}
#endif
#include "SDL_syswm.h"
#include "SDL.h"
void gfx_get_window_size(unsigned *width, unsigned *height)
{
#if defined(__APPLE__) || defined(_WIN32)
SDL_Event evnt;
while (SDL_PollEvent(&evnt));
const SDL_VideoInfo *info = SDL_GetVideoInfo();
*width = info->current_w;
*height = info->current_h;
#else
// It seems that we need to go hardcore to get the actual
// window sizes properly right after startup ... :D
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWMInfo(&info);
XWindowAttributes target;
info.info.x11.lock_func();
XGetWindowAttributes(info.info.x11.display, info.info.x11.window,
&target);
info.info.x11.unlock_func();
*width = target.width;
*height = target.height;
#endif
}
+2
View File
@@ -28,4 +28,6 @@ void gfx_window_title_reset(void);
void gfx_set_dwm(void);
#endif
void gfx_get_window_size(unsigned *width, unsigned *height);
#endif
+13
View File
@@ -705,6 +705,19 @@ static void check_window(gl_t *gl)
break;
}
}
if (!gl->should_resize)
{
unsigned new_width, new_height;
gfx_get_window_size(&new_width, &new_height);
if ((new_width != gl->win_width || new_height != gl->win_height) || (gl->frame_count == 10)) // Ugly hack :D
{
gl->should_resize = true;
gl->win_width = new_width;
gl->win_height = new_height;
SSNES_LOG("GL: Verified window size: %u x %u\n", gl->win_width, gl->win_height);
}
}
}
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
+1 -1
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.7beta1
PACKAGE_VERSION=0.7
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
+30 -11
View File
@@ -5,6 +5,7 @@
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avconfig.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
@@ -25,6 +26,9 @@ struct video_info
uint8_t *outbuf;
size_t outbuf_size;
int fmt;
int pix_size;
AVFormatContext *format;
struct SwsContext *sws_ctx;
@@ -41,8 +45,6 @@ struct audio_info
void *outbuf;
size_t outbuf_size;
int pix_fmt;
} audio;
struct muxer_info
@@ -91,7 +93,7 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
if (!audio->buffer)
return false;
audio->outbuf_size = 200000;
audio->outbuf_size = 2000000;
audio->outbuf = av_malloc(audio->outbuf_size);
if (!audio->outbuf)
return false;
@@ -105,6 +107,18 @@ static bool init_video(struct video_info *video, struct ffemu_params *param)
if (!codec)
return false;
#if AV_HAVE_BIGENDIAN
video->fmt = PIX_FMT_RGB555BE;
#else
video->fmt = PIX_FMT_RGB555LE;
#endif
video->pix_size = sizeof(uint16_t);
if (param->rgb32)
{
video->fmt = PIX_FMT_RGB32;
video->pix_size = sizeof(uint32_t);
}
video->codec = avcodec_alloc_context();
video->codec->width = param->out_width;
video->codec->height = param->out_height;
@@ -116,7 +130,7 @@ static bool init_video(struct video_info *video, struct ffemu_params *param)
return false;
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
video->outbuf_size = 2000000;
video->outbuf_size = 1 << 23;
video->outbuf = av_malloc(video->outbuf_size);
// Just to make sure we can handle the biggest frames. Seemed to crash with just 256 * 224.
@@ -177,7 +191,8 @@ static bool init_thread(ffemu_t *handle)
assert(handle->cond = SDL_CreateCond());
assert(handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) * handle->params.channels * MAX_FRAMES / 60));
assert(handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height * sizeof(int16_t) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height *
handle->video.pix_size * MAX_FRAMES));
handle->alive = true;
handle->can_sleep = true;
@@ -365,18 +380,20 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
{
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, PIX_FMT_RGB555LE,
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, handle->video.fmt,
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
NULL, NULL, NULL);
int linesize = data->pitch;
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
handle->video.codec->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
handle->video.conv_frame->pts = handle->video.frame_cnt;
handle->video.conv_frame->display_picture_number = handle->video.frame_cnt;
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame);
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf,
handle->video.outbuf_size, handle->video.conv_frame);
if (outsize < 0)
return -1;
@@ -387,7 +404,8 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
pkt.data = handle->video.outbuf;
pkt.size = outsize;
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base);
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base,
handle->muxer.vstream->time_base);
if (handle->video.codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
@@ -454,7 +472,7 @@ int ffemu_finalize(ffemu_t *handle)
deinit_thread(handle);
// Push out frames still stuck in queue.
uint16_t *video_buf = av_malloc(handle->params.fb_width * handle->params.fb_height * sizeof(uint16_t));
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
if (video_buf)
{
struct ffemu_video_data attr_buf;
@@ -524,7 +542,8 @@ static int SDLCALL ffemu_thread(void *data)
{
ffemu_t *ff = data;
uint16_t *video_buf = av_malloc(ff->params.fb_width * ff->params.fb_height * sizeof(uint16_t));
// For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. :s
void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size);
assert(video_buf);
size_t audio_buf_size = 128 * ff->params.channels * sizeof(int16_t);
+3 -27
View File
@@ -8,29 +8,6 @@
extern "C" {
#endif
// Available video codecs
typedef enum ffemu_video_codec
{
FFEMU_VIDEO_FFV1,
} ffemu_video_codec;
// Available audio codecs
typedef enum ffemu_audio_codec
{
FFEMU_AUDIO_VORBIS,
} ffemu_audio_codec;
// Available pixel formats
typedef enum ffemu_pixel_format
{
FFEMU_FMT_XBGR1555,
} ffemu_pixel_format;
typedef enum ffemu_rescaler
{
FFEMU_RESCALER_POINT
} ffemu_rescaler;
struct ffemu_rational
{
unsigned num;
@@ -40,9 +17,6 @@ struct ffemu_rational
// Parameters passed to ffemu_new()
struct ffemu_params
{
// Video codec to use. If not recording video, select FFEMU_VIDEO_NONE.
ffemu_video_codec vcodec;
// Desired output resolution.
unsigned out_width;
unsigned out_height;
@@ -68,6 +42,9 @@ struct ffemu_params
// Audio channels.
unsigned channels;
// If input is ARGB or XRGB1555.
bool rgb32;
// Audio bits. Sample format is always signed PCM in native byte order.
//unsigned bits;
@@ -107,7 +84,6 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
int ffemu_finalize(ffemu_t *handle);
#ifdef __cplusplus
}
#endif
+6
View File
@@ -142,6 +142,9 @@ static void set_defaults(void)
g_settings.video.second_pass_smooth = second_pass_smooth;
#endif
g_settings.video.hires_record = hires_record;
g_settings.video.post_filter_record = post_filter_record;
g_settings.audio.enable = audio_enable;
g_settings.audio.out_rate = out_rate;
g_settings.audio.in_rate = in_rate;
@@ -325,6 +328,9 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
#endif
CONFIG_GET_BOOL(video.hires_record, "video_hires_record");
CONFIG_GET_BOOL(video.post_filter_record, "video_post_filter_record");
#ifdef HAVE_DYLIB
CONFIG_GET_STRING(video.filter_path, "video_filter");
CONFIG_GET_STRING(video.external_driver, "video_external_driver");
+48 -6
View File
@@ -142,14 +142,16 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
if (g_extern.do_screenshot)
take_screenshot(data, width, height);
// Slightly messy code,
// but we really need to do processing before blocking on VSync for best possible scheduling.
#ifdef HAVE_FFMPEG
if (g_extern.recording)
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record))
{
struct ffemu_video_data ffemu_data = {
.data = data,
.pitch = height == 448 || height == 478 ? 1024 : 2048,
.width = width,
.height = height
.height = height,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
@@ -165,6 +167,20 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
g_extern.filter.psize(&owidth, &oheight);
g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer,
g_extern.filter.pitch, data, (height == 448 || height == 478) ? 1024 : 2048, width, height);
#ifdef HAVE_FFMPEG
if (g_extern.recording && g_settings.video.post_filter_record)
{
struct ffemu_video_data ffemu_data = {
.data = g_extern.filter.buffer,
.pitch = g_extern.filter.pitch,
.width = owidth,
.height = oheight,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
#endif
if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg))
g_extern.video_active = false;
}
@@ -340,7 +356,7 @@ static void fill_pathname_noext(char *out_path, const char *in_path, const char
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.7beta1"
#define PACKAGE_VERSION "0.7"
#endif
#include "config.features.h"
@@ -792,9 +808,11 @@ static inline void save_files(void)
#ifdef HAVE_FFMPEG
static void init_recording(void)
{
// Hardcode these options at the moment. Should be specificed in the config file later on.
if (g_extern.recording)
{
// Not perfectly SNES accurate, but this can be adjusted later during processing
// and playback losslessly, and we please the containers more by
// using "sane" values.
struct ffemu_rational ntsc_fps = {60000, 1000};
struct ffemu_rational pal_fps = {50000, 1000};
struct ffemu_params params = {
@@ -806,9 +824,29 @@ static void init_recording(void)
.samplerate = 32000,
.filename = g_extern.record_path,
.fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps,
.aspect_ratio = 4.0/3
.aspect_ratio = 4.0 / 3,
.rgb32 = false,
};
SSNES_LOG("Recording with FFmpeg to %s.\n", g_extern.record_path);
if (g_settings.video.hires_record)
{
params.out_width = 512;
params.out_height = 448;
}
if (g_settings.video.post_filter_record && g_extern.filter.active)
{
g_extern.filter.psize(&params.out_width, &params.out_height);
params.rgb32 = true;
unsigned max_width = 512;
unsigned max_height = 512;
g_extern.filter.psize(&max_width, &max_height);
params.fb_width = g_extern.filter.pitch >> 2;
params.fb_height = next_pow2(max_height);
}
SSNES_LOG("Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s)\n", g_extern.record_path, params.out_width, params.out_height, params.fb_width, params.fb_height, params.rgb32 ? "yes" : "no");
g_extern.rec = ffemu_new(&params);
if (!g_extern.rec)
{
@@ -1535,6 +1573,10 @@ int main(int argc, char *argv[])
// Main loop
for(;;)
{
// DSP plugin GUI events.
if (g_extern.audio_data.dsp_handle && g_extern.audio_data.dsp_plugin->events)
g_extern.audio_data.dsp_plugin->events(g_extern.audio_data.dsp_handle);
// Time to drop?
if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) ||
!driver.video->alive(driver.video_data) || g_extern.bsv_movie_end)
+6
View File
@@ -293,3 +293,9 @@
# Directory to dump screenshots to.
# screenshot_directory =
# Records video assuming video is hi-res.
# video_hires_record = false
# Records video after CPU video filter.
# video_post_filter_record = false