From fe68295a3df1448a1fda50fd1191044101d98538 Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 3 Jan 2011 17:51:17 +0100 Subject: [PATCH 01/40] Adding initial ffemu --- record/ffemu.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++ record/ffemu.h | 119 ++++++++++++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 record/ffemu.c create mode 100644 record/ffemu.h diff --git a/record/ffemu.c b/record/ffemu.c new file mode 100644 index 0000000000..caa9bd5c6a --- /dev/null +++ b/record/ffemu.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ffemu.h" + +struct video_info +{ + bool enabled; + AVCodecContext *codec; + AVFrame *frame; + FILE *file; + char *file_name; + + uint8_t *outbuf; + size_t outbuf_size; + +} video; + +struct audio_info +{ + bool enabled; + AVCodecContext *codec; + + int16_t *buffer; + size_t frames_in_buffer; + + void *outbuf; + size_t outbuf_size; + + FILE *file; + char *file_name; +} audio; + +struct ffemu +{ + struct video_info video; + struct audio_info audio; + + struct ffemu_params params; +}; + +static int init_video(struct video_info *video, struct ffemu_params *param) +{ + (void)video; + (void)param; + return -1; +} + +static int map_audio_codec(ffemu_audio_codec codec) +{ + (void)codec; + return CODEC_ID_AAC; +} + +static int init_audio(struct audio_info *audio, struct ffemu_params *param) +{ + AVCodec *codec = avcodec_find_encoder(map_audio_codec(param->acodec)); + if (!codec) + return -1; + + audio->codec = avcodec_alloc_context(); + audio->codec->bit_rate = 128000; + audio->codec->sample_rate = param->samplerate; + audio->codec->channels = param->channels; + + if (avcodec_open(audio->codec, codec) != 0) + return -1; + + audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t)); + if (!audio->buffer) + return -1; + + audio->file = fopen("/tmp/audio.aac", "wb"); + if (!audio->file) + return -1; + + audio->outbuf_size = 50000; + audio->outbuf = av_malloc(audio->outbuf_size); + if (!audio->outbuf) + return -1; + + return 0; +} + +ffemu_t *ffemu_new(const struct ffemu_params *params) +{ + avcodec_init(); + av_register_all(); + + ffemu_t *handle = calloc(1, sizeof(*handle)); + if (!handle) + goto error; + + handle->params = *params; + if (handle->params.vcodec != FFEMU_VIDEO_NONE) + handle->video.enabled = true; + if (handle->params.acodec != FFEMU_AUDIO_NONE) + handle->audio.enabled = true; + + if (handle->video.enabled) + if (init_video(&handle->video, &handle->params) < 0) + goto error; + + if (handle->audio.enabled) + if (init_audio(&handle->audio, &handle->params) < 0) + goto error; + + return handle; + +error: + ffemu_free(handle); + return NULL; +} + +void ffemu_free(ffemu_t *handle) +{ + if (handle) + { + if (handle->audio.codec) + { + avcodec_close(handle->audio.codec); + av_free(handle->audio.codec); + } + + if (handle->audio.buffer) + av_free(handle->audio.buffer); + + if (handle->video.codec) + { + avcodec_close(handle->video.codec); + av_free(handle->video.codec); + } + + if (handle->video.frame) + av_free(handle->video.frame); + + if (handle->video.file) + fclose(handle->video.file); + if (handle->audio.file) + fclose(handle->audio.file); + + free(handle); + } +} + +int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) +{ + (void)handle; + (void)data; + if (!handle->video.enabled) + return -1; + return 0; +} + +int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) +{ + size_t written_frames = 0; + while (written_frames < data->frames) + { + size_t can_write = handle->audio.codec->frame_size - handle->audio.frames_in_buffer; + size_t write_frames = data->frames > can_write ? can_write : data->frames; + + memcpy(handle->audio.buffer + handle->audio.frames_in_buffer * handle->params.channels, + data->data + written_frames * handle->params.channels, + write_frames * handle->params.channels * sizeof(int16_t)); + + written_frames += write_frames; + handle->audio.frames_in_buffer += write_frames; + + if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size) + { + size_t out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer); + fwrite(handle->audio.outbuf, 1, out_size, handle->audio.file); + handle->audio.frames_in_buffer = 0; + } + } + return 0; +} + +int ffemu_mux(ffemu_t *handle, const char *path, const struct ffemu_muxer *muxer) +{ + (void)handle; + (void)path; + (void)muxer; + return -1; +} + diff --git a/record/ffemu.h b/record/ffemu.h new file mode 100644 index 0000000000..738a8bfd62 --- /dev/null +++ b/record/ffemu.h @@ -0,0 +1,119 @@ +#ifndef __FFEMU_H +#define __FFEMU_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Available video codecs +typedef enum ffemu_video_codec +{ + FFEMU_VIDEO_NONE, + FFEMU_VIDEO_H264, + FFEMU_VIDEO_MPEG4, +} ffemu_video_codec; + +// Available audio codecs +typedef enum ffemu_audio_codec +{ + FFEMU_AUDIO_NONE, + FFEMU_AUDIO_VORBIS, + FFEMU_AUDIO_MP3, + FFEMU_AUDIO_AAC, +} ffemu_audio_codec; + +// Available pixel formats +typedef enum ffemu_pixel_format +{ + FFEMU_FMT_XBGR1555, + FFEMU_FMT_RGB888, +} ffemu_pixel_format; + +// Available muxer containers +typedef enum ffemu_container +{ + FFEMU_CONTAINER_MKV, + FFEMU_CONTAINER_MP4 +} ffemu_container; + +// 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; + + // Pixel format for video input. + ffemu_pixel_format format; + + // FPS of video input. + double fps; + + // Relative video quality. 0 is lossless (if available), 10 is very low quality. + // A value over 10 is codec defined if it will give even worse quality. + unsigned videoq; + + // Define some video codec dependent option. (E.g. h264 profiles) + uint64_t video_opt; + + // Audio codec. If not recording audio, select FFEMU_AUDIO_NONE. + ffemu_audio_codec acodec; + + // Audio sample rate. + unsigned samplerate; + + // Audio channels. + unsigned channels; + + // Audio bits. Sample format is always signed PCM in native byte order. + //unsigned bits; + + // Relative audio quality. 0 is lossless (if available), 10 is very low quality. + // A value over 10 is codec defined if it will give even worse quality. + // Some codecs might ignore this (lossless codecs such as FLAC). + unsigned audioq; + + // Define some audio codec dependent option. + uint64_t audio_opt; +}; + +struct ffemu_video_data +{ + const void *data; + unsigned width; + unsigned height; +}; + +struct ffemu_audio_data +{ + const int16_t *data; + size_t frames; +}; + +struct ffemu_muxer +{ + ffemu_container container; +}; + +typedef struct ffemu ffemu_t; + +ffemu_t *ffemu_new(const struct ffemu_params *params); +void ffemu_free(ffemu_t* handle); + +int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data); +int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data); + +int ffemu_mux(ffemu_t *handle, const char *path, const struct ffemu_muxer *muxer); + + +#ifdef __cplusplus +} +#endif + +#endif From 4e775736832db8453806495f6d961e5627644c9c Mon Sep 17 00:00:00 2001 From: Themaister Date: Mon, 3 Jan 2011 20:46:50 +0100 Subject: [PATCH 02/40] more progress, but still borked --- Makefile | 4 +-- general.h | 3 ++ record/ffemu.c | 82 ++++++++++++++++++++++++++++++++++++++++++-------- record/ffemu.h | 10 +++++- ssnes.c | 42 ++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 3972d52e75..1ec3db790e 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,9 @@ include config.mk TARGET = ssnes -OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o +OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o record/ffemu.o -LIBS = -lsamplerate +LIBS = -lsamplerate -lavformat -lavutil -lavcodec -lswscale ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o diff --git a/general.h b/general.h index af97152d12..6ecd3bf543 100644 --- a/general.h +++ b/general.h @@ -23,6 +23,7 @@ #include #include "driver.h" #include +#include "record/ffemu.h" #define MAX_PLAYERS 2 @@ -81,6 +82,8 @@ struct global char savefile_name_srm[256]; char config_path[256]; char basename[256]; + + ffemu_t *rec; }; void parse_config(void); diff --git a/record/ffemu.c b/record/ffemu.c index caa9bd5c6a..3ad378fa73 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,11 @@ struct video_info { bool enabled; AVCodecContext *codec; - AVFrame *frame; + + AVFrame *conv_frame; + uint8_t *conv_frame_buf; + int64_t frame_count; + FILE *file; char *file_name; @@ -44,19 +49,18 @@ struct ffemu struct ffemu_params params; }; -static int init_video(struct video_info *video, struct ffemu_params *param) -{ - (void)video; - (void)param; - return -1; -} - static int map_audio_codec(ffemu_audio_codec codec) { (void)codec; return CODEC_ID_AAC; } +static int map_video_codec(ffemu_video_codec codec) +{ + (void)codec; + return CODEC_ID_MPEG2VIDEO; +} + static int init_audio(struct audio_info *audio, struct ffemu_params *param) { AVCodec *codec = avcodec_find_encoder(map_audio_codec(param->acodec)); @@ -87,6 +91,37 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) return 0; } +static int init_video(struct video_info *video, struct ffemu_params *param) +{ + AVCodec *codec = avcodec_find_encoder(map_video_codec(param->vcodec)); + if (!codec) + return -1; + + video->codec = avcodec_alloc_context(); + video->codec->bit_rate = 400000; + video->codec->width = param->out_width; + video->codec->height = param->out_height; + video->codec->time_base = (AVRational) {param->fps.den, param->fps.num}; + video->codec->gop_size = 10; + video->codec->max_b_frames = 1; + video->codec->pix_fmt = PIX_FMT_YUV420P; + + if (avcodec_open(video->codec, codec) != 0) + return -1; + + video->outbuf_size = 100000; + video->outbuf = av_malloc(video->outbuf_size); + + int size = avpicture_get_size(PIX_FMT_YUV420P, param->out_width, param->out_height); + video->conv_frame_buf = av_malloc(size); + video->conv_frame = avcodec_alloc_frame(); + avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_YUV420P, param->out_width, param->out_height); + + video->file = fopen("/tmp/video.mpg", "wb"); + + return 0; +} + ffemu_t *ffemu_new(const struct ffemu_params *params) { avcodec_init(); @@ -136,8 +171,11 @@ void ffemu_free(ffemu_t *handle) av_free(handle->video.codec); } - if (handle->video.frame) - av_free(handle->video.frame); + if (handle->video.conv_frame) + av_free(handle->video.conv_frame); + + if (handle->video.conv_frame_buf) + av_free(handle->video.conv_frame_buf); if (handle->video.file) fclose(handle->video.file); @@ -150,15 +188,35 @@ void ffemu_free(ffemu_t *handle) int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) { - (void)handle; - (void)data; if (!handle->video.enabled) return -1; + + struct SwsContext *conv_ctx = sws_getContext(data->width, data->height, PIX_FMT_RGB555LE, + handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, SWS_BICUBIC, + NULL, NULL, NULL); + + int linesize = data->pitch; + + sws_scale(conv_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize); + + handle->video.conv_frame->pts = handle->video.frame_count; + handle->video.conv_frame->display_picture_number = handle->video.frame_count; + handle->video.frame_count++; + + int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame); + + fwrite(handle->video.outbuf, 1, outsize, handle->video.file); + + sws_freeContext(conv_ctx); + return 0; } int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) { + if (!handle->audio.enabled) + return -1; + size_t written_frames = 0; while (written_frames < data->frames) { diff --git a/record/ffemu.h b/record/ffemu.h index 738a8bfd62..5979bde596 100644 --- a/record/ffemu.h +++ b/record/ffemu.h @@ -39,6 +39,12 @@ typedef enum ffemu_container FFEMU_CONTAINER_MP4 } ffemu_container; +struct ffemu_rational +{ + unsigned num; + unsigned den; +}; + // Parameters passed to ffemu_new() struct ffemu_params { @@ -48,12 +54,13 @@ struct ffemu_params // Desired output resolution. unsigned out_width; unsigned out_height; + float aspect_ratio; // Pixel format for video input. ffemu_pixel_format format; // FPS of video input. - double fps; + struct ffemu_rational fps; // Relative video quality. 0 is lossless (if available), 10 is very low quality. // A value over 10 is codec defined if it will give even worse quality. @@ -88,6 +95,7 @@ struct ffemu_video_data const void *data; unsigned width; unsigned height; + unsigned pitch; }; struct ffemu_audio_data diff --git a/ssnes.c b/ssnes.c index cc0c518b10..f8a5b0c70f 100644 --- a/ssnes.c +++ b/ssnes.c @@ -29,6 +29,8 @@ #include "hqflt/filters.h" #include "general.h" #include "dynamic.h" +#include "record/ffemu.h" +#include struct global g_extern = { .video_active = true, @@ -83,6 +85,16 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) if ( !g_extern.video_active ) return; + ///////////// + struct ffemu_video_data ffemu_data = { + .data = data, + .pitch = 2048, + .width = width, + .height = height + }; + ffemu_push_video(g_extern.rec, &ffemu_data); + ///////////// + #ifdef HAVE_FILTER uint16_t output_filter[width * height * 4 * 4]; uint16_t output[width * height]; @@ -130,6 +142,17 @@ static void audio_sample(uint16_t left, uint16_t right) if ( !g_extern.audio_active ) return; + ///////// + static int16_t static_data[2]; + static_data[0] = left; + static_data[1] = right; + struct ffemu_audio_data ffemu_data = { + .data = static_data, + .frames = 1 + }; + ffemu_push_audio(g_extern.rec, &ffemu_data); + ////////// + static float data[AUDIO_CHUNK_SIZE_NONBLOCKING]; static int data_ptr = 0; @@ -335,6 +358,21 @@ int main(int argc, char *argv[]) load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); load_save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + //////// + struct ffemu_params params = { + .vcodec = FFEMU_VIDEO_H264, + .acodec = FFEMU_AUDIO_AAC, + .out_width = 512, + .out_height = 448, + .channels = 2, + .samplerate = 32040, + .fps = {60000, 1001}, + .aspect_ratio = 4.0/3 + }; + g_extern.rec = ffemu_new(¶ms); + assert(g_extern.rec); + ///////// + ///// TODO: Modular friendly!!! for(;;) { @@ -361,6 +399,10 @@ int main(int argc, char *argv[]) psnes_run(); } + /////////// + ffemu_free(g_extern.rec); + /////////// + save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); From 9bbd42166cd3ecf53409a41b090f7cfed7a50dcf Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 4 Jan 2011 15:44:05 +0100 Subject: [PATCH 03/40] some muxing going on! :D --- record/ffemu.c | 172 +++++++++++++++++++++++++++++++++++++++---------- record/ffemu.h | 17 ++--- ssnes.c | 2 + 3 files changed, 145 insertions(+), 46 deletions(-) diff --git a/record/ffemu.c b/record/ffemu.c index 3ad378fa73..aa00689cfa 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -16,14 +18,13 @@ struct video_info AVFrame *conv_frame; uint8_t *conv_frame_buf; - int64_t frame_count; - - FILE *file; - char *file_name; + int64_t frame_cnt; uint8_t *outbuf; size_t outbuf_size; + AVFormatContext *format; + } video; struct audio_info @@ -36,15 +37,20 @@ struct audio_info void *outbuf; size_t outbuf_size; - - FILE *file; - char *file_name; } audio; +struct muxer_info +{ + AVFormatContext *ctx; + AVStream *astream; + AVStream *vstream; +}; + struct ffemu { struct video_info video; struct audio_info audio; + struct muxer_info muxer; struct ffemu_params params; }; @@ -58,7 +64,7 @@ static int map_audio_codec(ffemu_audio_codec codec) static int map_video_codec(ffemu_video_codec codec) { (void)codec; - return CODEC_ID_MPEG2VIDEO; + return CODEC_ID_H264; } static int init_audio(struct audio_info *audio, struct ffemu_params *param) @@ -68,7 +74,7 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) return -1; audio->codec = avcodec_alloc_context(); - audio->codec->bit_rate = 128000; + audio->codec->bit_rate = 192000; audio->codec->sample_rate = param->samplerate; audio->codec->channels = param->channels; @@ -79,10 +85,6 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) if (!audio->buffer) return -1; - audio->file = fopen("/tmp/audio.aac", "wb"); - if (!audio->file) - return -1; - audio->outbuf_size = 50000; audio->outbuf = av_malloc(audio->outbuf_size); if (!audio->outbuf) @@ -91,6 +93,35 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) return 0; } +static void init_x264_param(AVCodecContext *c) +{ + c->coder_type = 1; // coder = 1 + c->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop + c->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1 + c->partitions|=X264_PART_I8X8+X264_PART_I4X4+X264_PART_P8X8+X264_PART_B8X8; // partitions=+parti8x8+parti4x4+partp8x8+partb8x8 + c->me_method=ME_HEX; // me_method=hex + c->me_subpel_quality = 7; // subq=7 + c->me_range = 16; // me_range=16 + c->gop_size = 250; // g=250 + c->keyint_min = 25; // keyint_min=25 + c->scenechange_threshold = 40; // sc_threshold=40 + c->i_quant_factor = 0.71; // i_qfactor=0.71 + c->b_frame_strategy = 1; // b_strategy=1 + c->qcompress = 0.6; // qcomp=0.6 + c->qmin = 10; // qmin=10 + c->qmax = 51; // qmax=51 + c->max_qdiff = 4; // qdiff=4 + c->max_b_frames = 3; // bf=3 + c->refs = 3; // refs=3 + c->directpred = 1; // directpred=1 + c->trellis = 1; // trellis=1 + c->flags2|=CODEC_FLAG2_BPYRAMID+CODEC_FLAG2_MIXED_REFS+CODEC_FLAG2_WPRED+CODEC_FLAG2_8X8DCT+CODEC_FLAG2_FASTPSKIP; // flags2=+bpyramid+mixed_refs+wpred+dct8x8+fastpskip + c->weighted_p_pred = 2; // wpredp=2 + + // libx264-main.ffpreset preset + c->flags2|=CODEC_FLAG2_8X8DCT;c->flags2^=CODEC_FLAG2_8X8DCT; +} + static int init_video(struct video_info *video, struct ffemu_params *param) { AVCodec *codec = avcodec_find_encoder(map_video_codec(param->vcodec)); @@ -98,13 +129,12 @@ static int init_video(struct video_info *video, struct ffemu_params *param) return -1; video->codec = avcodec_alloc_context(); - video->codec->bit_rate = 400000; video->codec->width = param->out_width; video->codec->height = param->out_height; video->codec->time_base = (AVRational) {param->fps.den, param->fps.num}; - video->codec->gop_size = 10; - video->codec->max_b_frames = 1; + video->codec->crf = 23; video->codec->pix_fmt = PIX_FMT_YUV420P; + init_x264_param(video->codec); if (avcodec_open(video->codec, codec) != 0) return -1; @@ -117,8 +147,45 @@ static int init_video(struct video_info *video, struct ffemu_params *param) video->conv_frame = avcodec_alloc_frame(); avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_YUV420P, param->out_width, param->out_height); - video->file = fopen("/tmp/video.mpg", "wb"); + return 0; +} +static int init_muxer(ffemu_t *handle) +{ + AVFormatContext *ctx = avformat_alloc_context(); + av_strlcpy(ctx->filename, handle->params.filename, sizeof(ctx->filename)); + ctx->oformat = av_guess_format(NULL, ctx->filename, NULL); + if (url_fopen(&ctx->pb, ctx->filename, URL_WRONLY) < 0) + { + av_free(ctx); + return -1; + } + + int stream_cnt = 0; + if (handle->video.enabled) + { + AVStream *stream = av_new_stream(ctx, stream_cnt++); + stream->codec = handle->video.codec; + + if (ctx->oformat->flags & AVFMT_GLOBALHEADER) + handle->video.codec->flags |= CODEC_FLAG_GLOBAL_HEADER; + handle->muxer.vstream = stream; + } + + if (handle->audio.enabled) + { + AVStream *stream = av_new_stream(ctx, stream_cnt++); + stream->codec = handle->audio.codec; + + if (ctx->oformat->flags & AVFMT_GLOBALHEADER) + handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER; + handle->muxer.astream = stream; + } + + if (av_write_header(ctx) < 0) + return -1; + + handle->muxer.ctx = ctx; return 0; } @@ -145,6 +212,9 @@ ffemu_t *ffemu_new(const struct ffemu_params *params) if (init_audio(&handle->audio, &handle->params) < 0) goto error; + if (init_muxer(handle) < 0) + goto error; + return handle; error: @@ -177,11 +247,6 @@ void ffemu_free(ffemu_t *handle) if (handle->video.conv_frame_buf) av_free(handle->video.conv_frame_buf); - if (handle->video.file) - fclose(handle->video.file); - if (handle->audio.file) - fclose(handle->audio.file); - free(handle); } } @@ -199,16 +264,23 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) sws_scale(conv_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize); - handle->video.conv_frame->pts = handle->video.frame_count; - handle->video.conv_frame->display_picture_number = handle->video.frame_count; - handle->video.frame_count++; - int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame); - fwrite(handle->video.outbuf, 1, outsize, handle->video.file); + //fwrite(handle->video.outbuf, 1, outsize, handle->video.file); sws_freeContext(conv_ctx); + AVPacket pkt; + av_init_packet(&pkt); + pkt.stream_index = handle->muxer.vstream->index; + pkt.data = handle->video.outbuf; + pkt.size = outsize; + pkt.pts = av_rescale_q(handle->video.frame_cnt++, handle->video.codec->time_base, handle->muxer.vstream->time_base); + pkt.dts = pkt.pts; + + if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) + return -1; + return 0; } @@ -217,6 +289,13 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) if (!handle->audio.enabled) return -1; + AVPacket pkt; + av_init_packet(&pkt); + pkt.stream_index = handle->muxer.astream->index; + pkt.pts = AV_NOPTS_VALUE; + pkt.dts = pkt.pts; + pkt.data = handle->audio.outbuf; + size_t written_frames = 0; while (written_frames < data->frames) { @@ -233,18 +312,45 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size) { size_t out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer); - fwrite(handle->audio.outbuf, 1, out_size, handle->audio.file); + //fwrite(handle->audio.outbuf, 1, out_size, handle->audio.file); + pkt.size = out_size; handle->audio.frames_in_buffer = 0; + + if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) + return -1; } } return 0; } -int ffemu_mux(ffemu_t *handle, const char *path, const struct ffemu_muxer *muxer) +int ffemu_finalize(ffemu_t *handle) { - (void)handle; - (void)path; - (void)muxer; - return -1; + // Push out delayed frames. + if (handle->video.enabled) + { + AVPacket pkt; + av_init_packet(&pkt); + pkt.stream_index = handle->muxer.vstream->index; + pkt.data = handle->video.outbuf; + + int out_size = 0; + do + { + out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL); + + pkt.size = out_size; + pkt.pts = av_rescale_q(handle->video.frame_cnt++, handle->video.codec->time_base, handle->muxer.vstream->time_base); + pkt.dts = pkt.pts; + + int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt); + if (err < 0) + break; + + } while (out_size > 0); + } + + av_write_trailer(handle->muxer.ctx); + + return 0; } diff --git a/record/ffemu.h b/record/ffemu.h index 5979bde596..e4e8b68ce0 100644 --- a/record/ffemu.h +++ b/record/ffemu.h @@ -32,13 +32,6 @@ typedef enum ffemu_pixel_format FFEMU_FMT_RGB888, } ffemu_pixel_format; -// Available muxer containers -typedef enum ffemu_container -{ - FFEMU_CONTAINER_MKV, - FFEMU_CONTAINER_MP4 -} ffemu_container; - struct ffemu_rational { unsigned num; @@ -88,6 +81,9 @@ struct ffemu_params // Define some audio codec dependent option. uint64_t audio_opt; + + // Filename to dump to. + const char *filename; }; struct ffemu_video_data @@ -104,11 +100,6 @@ struct ffemu_audio_data size_t frames; }; -struct ffemu_muxer -{ - ffemu_container container; -}; - typedef struct ffemu ffemu_t; ffemu_t *ffemu_new(const struct ffemu_params *params); @@ -116,8 +107,8 @@ void ffemu_free(ffemu_t* handle); int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data); int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data); +int ffemu_finalize(ffemu_t *handle); -int ffemu_mux(ffemu_t *handle, const char *path, const struct ffemu_muxer *muxer); #ifdef __cplusplus diff --git a/ssnes.c b/ssnes.c index f8a5b0c70f..f6b52e6a80 100644 --- a/ssnes.c +++ b/ssnes.c @@ -366,6 +366,7 @@ int main(int argc, char *argv[]) .out_height = 448, .channels = 2, .samplerate = 32040, + .filename = "/tmp/ssnes.mkv", .fps = {60000, 1001}, .aspect_ratio = 4.0/3 }; @@ -400,6 +401,7 @@ int main(int argc, char *argv[]) } /////////// + ffemu_finalize(g_extern.rec); ffemu_free(g_extern.rec); /////////// From 1bb4d894b9841c082d82fd312de126ca816948b6 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 4 Jan 2011 16:57:55 +0100 Subject: [PATCH 04/40] Fix aspect ratio. --- record/ffemu.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/record/ffemu.c b/record/ffemu.c index aa00689cfa..c47ac57d88 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -35,6 +35,8 @@ struct audio_info int16_t *buffer; size_t frames_in_buffer; + int64_t frame_cnt; + void *outbuf; size_t outbuf_size; } audio; @@ -58,7 +60,7 @@ struct ffemu static int map_audio_codec(ffemu_audio_codec codec) { (void)codec; - return CODEC_ID_AAC; + return CODEC_ID_VORBIS; } static int map_video_codec(ffemu_video_codec codec) @@ -74,13 +76,17 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) return -1; audio->codec = avcodec_alloc_context(); - audio->codec->bit_rate = 192000; - audio->codec->sample_rate = param->samplerate; - audio->codec->channels = param->channels; + avcodec_get_context_defaults(audio->codec); + audio->codec->global_quality = 50000; + audio->codec->flags |= CODEC_FLAG_QSCALE; + audio->codec->sample_rate = param->samplerate; + audio->codec->time_base = (AVRational) { 1, param->samplerate }; + audio->codec->channels = param->channels; if (avcodec_open(audio->codec, codec) != 0) return -1; + audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t)); if (!audio->buffer) return -1; @@ -134,6 +140,10 @@ static int init_video(struct video_info *video, struct ffemu_params *param) video->codec->time_base = (AVRational) {param->fps.den, param->fps.num}; video->codec->crf = 23; video->codec->pix_fmt = PIX_FMT_YUV420P; + ///// + video->codec->thread_count = 2; + ///// + video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255); init_x264_param(video->codec); if (avcodec_open(video->codec, codec) != 0) @@ -170,6 +180,7 @@ static int init_muxer(ffemu_t *handle) if (ctx->oformat->flags & AVFMT_GLOBALHEADER) handle->video.codec->flags |= CODEC_FLAG_GLOBAL_HEADER; handle->muxer.vstream = stream; + handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio; } if (handle->audio.enabled) @@ -292,7 +303,6 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) AVPacket pkt; av_init_packet(&pkt); pkt.stream_index = handle->muxer.astream->index; - pkt.pts = AV_NOPTS_VALUE; pkt.dts = pkt.pts; pkt.data = handle->audio.outbuf; @@ -311,9 +321,13 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size) { + handle->audio.frame_cnt += handle->audio.codec->frame_size; + size_t out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer); //fwrite(handle->audio.outbuf, 1, out_size, handle->audio.file); pkt.size = out_size; + pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base); + pkt.dts = pkt.pts; handle->audio.frames_in_buffer = 0; if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) From a6b2fafebac3f705f63043e8fe89ddec73e558a9 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 4 Jan 2011 21:21:15 +0100 Subject: [PATCH 05/40] Add option for rescaler --- record/ffemu.c | 51 +++++++++++++++++++++++++++++++++++--------------- record/ffemu.h | 9 +++++++++ ssnes.c | 3 ++- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/record/ffemu.c b/record/ffemu.c index c47ac57d88..72d23a662c 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -60,7 +60,7 @@ struct ffemu static int map_audio_codec(ffemu_audio_codec codec) { (void)codec; - return CODEC_ID_VORBIS; + return CODEC_ID_FLAC; } static int map_video_codec(ffemu_video_codec codec) @@ -78,11 +78,12 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) audio->codec = avcodec_alloc_context(); avcodec_get_context_defaults(audio->codec); - audio->codec->global_quality = 50000; + audio->codec->global_quality = 100000; audio->codec->flags |= CODEC_FLAG_QSCALE; audio->codec->sample_rate = param->samplerate; audio->codec->time_base = (AVRational) { 1, param->samplerate }; audio->codec->channels = param->channels; + audio->codec->sample_fmt = AV_SAMPLE_FMT_S16; if (avcodec_open(audio->codec, codec) != 0) return -1; @@ -125,7 +126,8 @@ static void init_x264_param(AVCodecContext *c) c->weighted_p_pred = 2; // wpredp=2 // libx264-main.ffpreset preset - c->flags2|=CODEC_FLAG2_8X8DCT;c->flags2^=CODEC_FLAG2_8X8DCT; + c->flags2|=CODEC_FLAG2_8X8DCT; + c->flags2^=CODEC_FLAG2_8X8DCT; } static int init_video(struct video_info *video, struct ffemu_params *param) @@ -138,10 +140,10 @@ static int init_video(struct video_info *video, struct ffemu_params *param) video->codec->width = param->out_width; video->codec->height = param->out_height; video->codec->time_base = (AVRational) {param->fps.den, param->fps.num}; - video->codec->crf = 23; + video->codec->crf = 25; video->codec->pix_fmt = PIX_FMT_YUV420P; - ///// - video->codec->thread_count = 2; + ///// Is this element in all recent ffmpeg versions? + video->codec->thread_count = 4; ///// video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255); init_x264_param(video->codec); @@ -149,7 +151,7 @@ static int init_video(struct video_info *video, struct ffemu_params *param) if (avcodec_open(video->codec, codec) != 0) return -1; - video->outbuf_size = 100000; + video->outbuf_size = 1000000; video->outbuf = av_malloc(video->outbuf_size); int size = avpicture_get_size(PIX_FMT_YUV420P, param->out_width, param->out_height); @@ -268,7 +270,7 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) return -1; struct SwsContext *conv_ctx = sws_getContext(data->width, data->height, PIX_FMT_RGB555LE, - handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, SWS_BICUBIC, + handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, handle->params.rescaler == FFEMU_RESCALER_LANCZOS ? SWS_LANCZOS : SWS_POINT, NULL, NULL, NULL); int linesize = data->pitch; @@ -277,8 +279,6 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame); - //fwrite(handle->video.outbuf, 1, outsize, handle->video.file); - sws_freeContext(conv_ctx); AVPacket pkt; @@ -286,8 +286,13 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) pkt.stream_index = handle->muxer.vstream->index; pkt.data = handle->video.outbuf; pkt.size = outsize; + pkt.pts = av_rescale_q(handle->video.frame_cnt++, handle->video.codec->time_base, handle->muxer.vstream->time_base); pkt.dts = pkt.pts; + fprintf(stderr, "Video PTS: %lld\n", (long long)pkt.pts); + + if (handle->video.codec->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) return -1; @@ -321,14 +326,26 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size) { - handle->audio.frame_cnt += handle->audio.codec->frame_size; size_t out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer); //fwrite(handle->audio.outbuf, 1, out_size, handle->audio.file); pkt.size = out_size; - pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base); - pkt.dts = pkt.pts; + if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE) + { + pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base); + pkt.dts = pkt.pts; + fprintf(stderr, "Audio PTS: %d\n", (int)pkt.pts); + } + else + { + pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base); + pkt.dts = pkt.pts; + fprintf(stderr, "Audio PTS (calculated): %d\n", (int)pkt.pts); + } + + pkt.flags |= AV_PKT_FLAG_KEY; handle->audio.frames_in_buffer = 0; + handle->audio.frame_cnt += handle->audio.codec->frame_size; if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) return -1; @@ -351,11 +368,15 @@ int ffemu_finalize(ffemu_t *handle) do { out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL); - - pkt.size = out_size; pkt.pts = av_rescale_q(handle->video.frame_cnt++, handle->video.codec->time_base, handle->muxer.vstream->time_base); pkt.dts = pkt.pts; + if (handle->video.codec->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; + + + pkt.size = out_size; + int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt); if (err < 0) break; diff --git a/record/ffemu.h b/record/ffemu.h index e4e8b68ce0..f00a1c131d 100644 --- a/record/ffemu.h +++ b/record/ffemu.h @@ -32,6 +32,12 @@ typedef enum ffemu_pixel_format FFEMU_FMT_RGB888, } ffemu_pixel_format; +typedef enum ffemu_rescaler +{ + FFEMU_RESCALER_LANCZOS, + FFEMU_RESCALER_POINT +} ffemu_rescaler; + struct ffemu_rational { unsigned num; @@ -49,6 +55,9 @@ struct ffemu_params unsigned out_height; float aspect_ratio; + // Rescaler for video. + ffemu_rescaler rescaler; + // Pixel format for video input. ffemu_pixel_format format; diff --git a/ssnes.c b/ssnes.c index f6b52e6a80..9130c14184 100644 --- a/ssnes.c +++ b/ssnes.c @@ -361,7 +361,8 @@ int main(int argc, char *argv[]) //////// struct ffemu_params params = { .vcodec = FFEMU_VIDEO_H264, - .acodec = FFEMU_AUDIO_AAC, + .acodec = FFEMU_AUDIO_VORBIS, + .rescaler = FFEMU_RESCALER_POINT, .out_width = 512, .out_height = 448, .channels = 2, From a9938a408d19edb1475b68e69cc172ffda7333b0 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 4 Jan 2011 22:35:49 +0100 Subject: [PATCH 06/40] A/V sync works pretty well now. --- record/ffemu.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/record/ffemu.c b/record/ffemu.c index 72d23a662c..065449a304 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -269,6 +269,7 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) if (!handle->video.enabled) return -1; + // This is deprecated, can't find a replacement... :( struct SwsContext *conv_ctx = sws_getContext(data->width, data->height, PIX_FMT_RGB555LE, handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, handle->params.rescaler == FFEMU_RESCALER_LANCZOS ? SWS_LANCZOS : SWS_POINT, NULL, NULL, NULL); @@ -277,6 +278,9 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) sws_scale(conv_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, 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); sws_freeContext(conv_ctx); @@ -287,15 +291,18 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) pkt.data = handle->video.outbuf; pkt.size = outsize; - pkt.pts = av_rescale_q(handle->video.frame_cnt++, handle->video.codec->time_base, handle->muxer.vstream->time_base); - pkt.dts = pkt.pts; - fprintf(stderr, "Video PTS: %lld\n", (long long)pkt.pts); + 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; - if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) - return -1; + if (pkt.size > 0) + { + if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) + return -1; + } + + handle->video.frame_cnt++; return 0; } @@ -308,7 +315,6 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) AVPacket pkt; av_init_packet(&pkt); pkt.stream_index = handle->muxer.astream->index; - pkt.dts = pkt.pts; pkt.data = handle->audio.outbuf; size_t written_frames = 0; @@ -333,22 +339,21 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE) { pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base); - pkt.dts = pkt.pts; - fprintf(stderr, "Audio PTS: %d\n", (int)pkt.pts); } else { pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base); - pkt.dts = pkt.pts; - fprintf(stderr, "Audio PTS (calculated): %d\n", (int)pkt.pts); } pkt.flags |= AV_PKT_FLAG_KEY; handle->audio.frames_in_buffer = 0; handle->audio.frame_cnt += handle->audio.codec->frame_size; - if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) - return -1; + if (pkt.size > 0) + { + if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) + return -1; + } } } return 0; @@ -368,18 +373,19 @@ int ffemu_finalize(ffemu_t *handle) do { out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL); - pkt.pts = av_rescale_q(handle->video.frame_cnt++, handle->video.codec->time_base, handle->muxer.vstream->time_base); - pkt.dts = pkt.pts; + 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; - pkt.size = out_size; - int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt); - if (err < 0) - break; + if (pkt.size > 0) + { + int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt); + if (err < 0) + break; + } } while (out_size > 0); } From 0e82a1a3ec9a631d9e17f1df6d81fbc632a6806c Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 4 Jan 2011 23:55:29 +0100 Subject: [PATCH 07/40] Adding some comments. --- record/ffemu.c | 23 +++++++++++++++++++---- ssnes.c | 6 ++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/record/ffemu.c b/record/ffemu.c index 065449a304..b5a632d16f 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -57,12 +57,14 @@ struct ffemu struct ffemu_params params; }; +// Currently hardcoded atm. :) static int map_audio_codec(ffemu_audio_codec codec) { (void)codec; - return CODEC_ID_FLAC; + return CODEC_ID_VORBIS; } +// Currently hardcoded atm. :) static int map_video_codec(ffemu_video_codec codec) { (void)codec; @@ -78,6 +80,8 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) audio->codec = avcodec_alloc_context(); avcodec_get_context_defaults(audio->codec); + + // Hardcode this atm. audio->codec->global_quality = 100000; audio->codec->flags |= CODEC_FLAG_QSCALE; audio->codec->sample_rate = param->samplerate; @@ -100,6 +104,7 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param) return 0; } +// Hardcode. static void init_x264_param(AVCodecContext *c) { c->coder_type = 1; // coder = 1 @@ -141,6 +146,7 @@ static int init_video(struct video_info *video, struct ffemu_params *param) video->codec->height = param->out_height; video->codec->time_base = (AVRational) {param->fps.den, param->fps.num}; video->codec->crf = 25; + // Might have to change this later to account for RGB codecs. video->codec->pix_fmt = PIX_FMT_YUV420P; ///// Is this element in all recent ffmpeg versions? video->codec->thread_count = 4; @@ -151,6 +157,7 @@ static int init_video(struct video_info *video, struct ffemu_params *param) if (avcodec_open(video->codec, codec) != 0) return -1; + // Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be. video->outbuf_size = 1000000; video->outbuf = av_malloc(video->outbuf_size); @@ -264,12 +271,14 @@ void ffemu_free(ffemu_t *handle) } } +// Need to make this thread based, but hey. int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) { if (!handle->video.enabled) return -1; // This is deprecated, can't find a replacement... :( + // Hardcode pixel format for now (SNES) struct SwsContext *conv_ctx = sws_getContext(data->width, data->height, PIX_FMT_RGB555LE, handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, handle->params.rescaler == FFEMU_RESCALER_LANCZOS ? SWS_LANCZOS : SWS_POINT, NULL, NULL, NULL); @@ -283,6 +292,9 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data) int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame); + if (outsize < 0) + return -1; + sws_freeContext(conv_ctx); AVPacket pkt; @@ -333,8 +345,10 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size) { - size_t out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer); - //fwrite(handle->audio.outbuf, 1, out_size, handle->audio.file); + int out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer); + if (out_size < 0) + return -1; + pkt.size = out_size; if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE) { @@ -361,7 +375,7 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data) int ffemu_finalize(ffemu_t *handle) { - // Push out delayed frames. + // Push out delayed frames. (MPEG codecs) if (handle->video.enabled) { AVPacket pkt; @@ -390,6 +404,7 @@ int ffemu_finalize(ffemu_t *handle) } while (out_size > 0); } + // Write final data. av_write_trailer(handle->muxer.ctx); return 0; diff --git a/ssnes.c b/ssnes.c index 9130c14184..ee9160649e 100644 --- a/ssnes.c +++ b/ssnes.c @@ -88,7 +88,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) ///////////// struct ffemu_video_data ffemu_data = { .data = data, - .pitch = 2048, + .pitch = height == 448 || height == 478 ? 1024 : 2048, .width = width, .height = height }; @@ -358,6 +358,8 @@ int main(int argc, char *argv[]) load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); load_save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); + struct ffemu_rational ntsc_fps = {60000, 1001}; + struct ffemu_rational pal_fps = {50000, 1001}; //////// struct ffemu_params params = { .vcodec = FFEMU_VIDEO_H264, @@ -368,7 +370,7 @@ int main(int argc, char *argv[]) .channels = 2, .samplerate = 32040, .filename = "/tmp/ssnes.mkv", - .fps = {60000, 1001}, + .fps = snes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps, .aspect_ratio = 4.0/3 }; g_extern.rec = ffemu_new(¶ms); From 633cc4394944fa74b3372097709944164fbce103 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 17:22:12 +0100 Subject: [PATCH 08/40] Refactor shader code. Start adding code for bSNES xml shaders as well... --- Makefile | 1 + gfx/gl.c | 149 ++++++++++++++++++++---------------------------- gfx/shader_cg.c | 104 +++++++++++++++++++++++++++++++++ gfx/shader_cg.h | 34 +++++++++++ 4 files changed, 202 insertions(+), 86 deletions(-) create mode 100644 gfx/shader_cg.c create mode 100644 gfx/shader_cg.h diff --git a/Makefile b/Makefile index 3972d52e75..4c482a5bf0 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ ifeq ($(HAVE_GLFW), 1) endif ifeq ($(HAVE_CG), 1) + OBJ += gfx/shader_cg.o LIBS += -lCg -lCgGL endif diff --git a/gfx/gl.c b/gfx/gl.c index 3e9b38c351..5c0a8088fd 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -30,8 +30,11 @@ #ifdef HAVE_CG -#include -#include +#include "shader_cg.h" +#endif + +#ifdef HAVE_XML +#include "shader_glsl.h" #endif static const GLfloat vertexes[] = { @@ -49,23 +52,10 @@ static const GLfloat tex_coords[] = { }; static bool keep_aspect = true; -#ifdef HAVE_CG -static CGparameter cg_mvp_matrix; -static bool cg_active = false; -#endif static GLuint gl_width = 0, gl_height = 0; typedef struct gl { bool vsync; -#ifdef HAVE_CG - CGcontext cgCtx; - CGprogram cgFPrg; - CGprogram cgVPrg; - CGprofile cgFProf; - CGprofile cgVProf; - CGparameter cg_video_size, cg_texture_size, cg_output_size; - CGparameter cg_Vvideo_size, cg_Vtexture_size, cg_Voutput_size; // Vertexes -#endif GLuint texture; GLuint tex_filter; @@ -180,6 +170,56 @@ static const input_driver_t input_glfw = { .ident = "glfw" }; +static inline bool gl_shader_init(void) +{ +#ifdef HAVE_CG + if (strlen(g_settings.video.cg_shader_path) > 0) + return gl_cg_init(g_settings.video.cg_shader_path); +#endif + +#ifdef HAVE_XML + if (strlen(g_settings.video.bsnes_shader_path) > 0) + return gl_glsl_init(g_settings.video.bsnes_shader_path); +#endif + + return true; +} + +static inline void gl_shader_deinit(void) +{ +#ifdef HAVE_CG + gl_cg_deinit(); +#endif + +#ifdef HAVE_XML + gl_glsl_deinit(); +#endif +} + +static inline void gl_shader_set_proj_matrix(void) +{ +#ifdef HAVE_CG + gl_cg_set_proj_matrix(); +#endif + +#ifdef HAVE_XML + gl_glsl_set_proj_matrix(); +#endif +} + +static inline void gl_shader_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height) +{ +#ifdef HAVE_CG + gl_cg_set_params(width, height, tex_width, tex_height, out_width, out_height); +#endif + +#ifdef HAVE_XML + gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height); +#endif +} + static void GLFWCALL resize(int width, int height) { glMatrixMode(GL_PROJECTION); @@ -215,10 +255,9 @@ static void GLFWCALL resize(int width, int height) glOrtho(0, 1, 0, 1, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); -#ifdef HAVE_CG - if (cg_active) - cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); -#endif + + gl_shader_set_proj_matrix(); + gl_width = out_width; gl_height = out_height; } @@ -260,18 +299,7 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i glClear(GL_COLOR_BUFFER_BIT); -#if HAVE_CG - if (cg_active) - { - cgGLSetParameter2f(gl->cg_video_size, width, height); - cgGLSetParameter2f(gl->cg_texture_size, gl->tex_w, gl->tex_h); - cgGLSetParameter2f(gl->cg_output_size, gl_width, gl_height); - - cgGLSetParameter2f(gl->cg_Vvideo_size, width, height); - cgGLSetParameter2f(gl->cg_Vtexture_size, gl->tex_w, gl->tex_h); - cgGLSetParameter2f(gl->cg_Voutput_size, gl_width, gl_height); - } -#endif + gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl_width, gl_height); if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture. { @@ -310,10 +338,8 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i static void gl_free(void *data) { gl_t *gl = data; -#ifdef HAVE_CG - if (cg_active) - cgDestroyContext(gl->cgCtx); -#endif + + gl_shader_deinit(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDeleteTextures(1, &gl->texture); @@ -352,6 +378,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) if (!res) { glfwTerminate(); + free(gl); return NULL; } @@ -400,60 +427,10 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) gl->last_width = gl->tex_w; gl->last_height = gl->tex_h; -#ifdef HAVE_CG - cg_active = false; - if (strlen(g_settings.video.cg_shader_path) > 0) - { - SSNES_LOG("Loading Cg file: %s\n", g_settings.video.cg_shader_path); - gl->cgCtx = cgCreateContext(); - if (gl->cgCtx == NULL) - { - fprintf(stderr, "Failed to create Cg context\n"); - goto error; - } - gl->cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); - gl->cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX); - if (gl->cgFProf == CG_PROFILE_UNKNOWN || gl->cgVProf == CG_PROFILE_UNKNOWN) - { - fprintf(stderr, "Invalid profile type\n"); - goto error; - } - cgGLSetOptimalOptions(gl->cgFProf); - cgGLSetOptimalOptions(gl->cgVProf); - gl->cgFPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, g_settings.video.cg_shader_path, gl->cgFProf, "main_fragment", 0); - gl->cgVPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, g_settings.video.cg_shader_path, gl->cgVProf, "main_vertex", 0); - if (gl->cgFPrg == NULL || gl->cgVPrg == NULL) - { - CGerror err = cgGetError(); - fprintf(stderr, "CG error: %s\n", cgGetErrorString(err)); - goto error; - } - cgGLLoadProgram(gl->cgFPrg); - cgGLLoadProgram(gl->cgVPrg); - cgGLEnableProfile(gl->cgFProf); - cgGLEnableProfile(gl->cgVProf); - cgGLBindProgram(gl->cgFPrg); - cgGLBindProgram(gl->cgVPrg); - - gl->cg_video_size = cgGetNamedParameter(gl->cgFPrg, "IN.video_size"); - gl->cg_texture_size = cgGetNamedParameter(gl->cgFPrg, "IN.texture_size"); - gl->cg_output_size = cgGetNamedParameter(gl->cgFPrg, "IN.output_size"); - gl->cg_Vvideo_size = cgGetNamedParameter(gl->cgVPrg, "IN.video_size"); - gl->cg_Vtexture_size = cgGetNamedParameter(gl->cgVPrg, "IN.texture_size"); - gl->cg_Voutput_size = cgGetNamedParameter(gl->cgVPrg, "IN.output_size"); - cg_mvp_matrix = cgGetNamedParameter(gl->cgVPrg, "modelViewProj"); - cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); - cg_active = true; - } -#endif + gl_shader_init(); *input = &input_glfw; return gl; -#ifdef HAVE_CG -error: - free(gl); - return NULL; -#endif } const video_driver_t video_gl = { diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c new file mode 100644 index 0000000000..1c1005a794 --- /dev/null +++ b/gfx/shader_cg.c @@ -0,0 +1,104 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + +#include "shader_cg.h" +#include +#include +#include "general.h" + +static CGcontext cgCtx; +static CGprogram cgFPrg; +static CGprogram cgVPrg; +static CGprofile cgFProf; +static CGprofile cgVProf; +static CGparameter cg_video_size, cg_texture_size, cg_output_size; +static CGparameter cg_Vvideo_size, cg_Vtexture_size, cg_Voutput_size; // Vertexes +static CGparameter cg_mvp_matrix; +static bool cg_active = false; + +void gl_cg_set_proj_matrix(void) +{ + if (cg_active) + cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); +} + +void gl_cg_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height) +{ + if (cg_active) + { + cgGLSetParameter2f(cg_video_size, width, height); + cgGLSetParameter2f(cg_texture_size, tex_width, tex_height); + cgGLSetParameter2f(cg_output_size, out_width, out_height); + + cgGLSetParameter2f(cg_Vvideo_size, width, height); + cgGLSetParameter2f(cg_Vtexture_size, tex_width, tex_height); + cgGLSetParameter2f(cg_Voutput_size, out_width, out_height); + } +} + +void gl_cg_deinit(void) +{ + if (cg_active) + cgDestroyContext(cgCtx); +} + +bool gl_cg_init(const char *path) +{ + SSNES_LOG("Loading Cg file: %s\n", path); + cgCtx = cgCreateContext(); + if (cgCtx == NULL) + { + SSNES_ERR("Failed to create Cg context\n"); + return false; + } + cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); + cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX); + if (cgFProf == CG_PROFILE_UNKNOWN || cgVProf == CG_PROFILE_UNKNOWN) + { + SSNES_ERR("Invalid profile type\n"); + return false; + } + cgGLSetOptimalOptions(cgFProf); + cgGLSetOptimalOptions(cgVProf); + cgFPrg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgFProf, "main_fragment", 0); + cgVPrg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgVProf, "main_vertex", 0); + if (cgFPrg == NULL || cgVPrg == NULL) + { + CGerror err = cgGetError(); + SSNES_ERR("CG error: %s\n", cgGetErrorString(err)); + return false; + } + cgGLLoadProgram(cgFPrg); + cgGLLoadProgram(cgVPrg); + cgGLEnableProfile(cgFProf); + cgGLEnableProfile(cgVProf); + cgGLBindProgram(cgFPrg); + cgGLBindProgram(cgVPrg); + + cg_video_size = cgGetNamedParameter(cgFPrg, "IN.video_size"); + cg_texture_size = cgGetNamedParameter(cgFPrg, "IN.texture_size"); + cg_output_size = cgGetNamedParameter(cgFPrg, "IN.output_size"); + cg_Vvideo_size = cgGetNamedParameter(cgVPrg, "IN.video_size"); + cg_Vtexture_size = cgGetNamedParameter(cgVPrg, "IN.texture_size"); + cg_Voutput_size = cgGetNamedParameter(cgVPrg, "IN.output_size"); + cg_mvp_matrix = cgGetNamedParameter(cgVPrg, "modelViewProj"); + cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); + cg_active = true; + return true; +} diff --git a/gfx/shader_cg.h b/gfx/shader_cg.h new file mode 100644 index 0000000000..f788466c0a --- /dev/null +++ b/gfx/shader_cg.h @@ -0,0 +1,34 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + + +#ifndef __SSNES_CG_H +#define __SSNES_CG_H + +#include + +bool gl_cg_init(const char *path); + +void gl_cg_deinit(void); + +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); + +#endif From 56e050f10d9577ef73ec5deec84b059d3308f206 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 17:32:30 +0100 Subject: [PATCH 09/40] Add config option for XML shader. --- config.def.h | 2 +- driver.h | 2 +- general.h | 1 + gfx/gl.c | 2 +- settings.c | 12 ++++++++---- ssnes.cfg | 3 +++ 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/config.def.h b/config.def.h index d2ce444381..477d637d9b 100644 --- a/config.def.h +++ b/config.def.h @@ -127,7 +127,7 @@ static const struct snes_keybind snes_keybinds_1[] = { { SNES_DEVICE_ID_JOYPAD_DOWN, GLFW_KEY_DOWN, 14, AXIS_NEG(1) }, { SNES_DEVICE_ID_JOYPAD_START, GLFW_KEY_ENTER, 7, AXIS_NONE }, { SNES_DEVICE_ID_JOYPAD_SELECT, GLFW_KEY_RSHIFT, 6, AXIS_NONE }, - { SNES_FAST_FORWARD_KEY, GLFW_KEY_SPACE, 10, AXIS_NONE }, + { SSNES_FAST_FORWARD_KEY, GLFW_KEY_SPACE, 10, AXIS_NONE }, { -1 } }; diff --git a/driver.h b/driver.h index f695746cd3..0deb3672d7 100644 --- a/driver.h +++ b/driver.h @@ -24,7 +24,7 @@ #include #include -#define SNES_FAST_FORWARD_KEY 0x666 // Hurr, durr +#define SSNES_FAST_FORWARD_KEY 0x666 // Hurr, durr void set_fast_forward_button(bool state); struct snes_keybind diff --git a/general.h b/general.h index af97152d12..048abde579 100644 --- a/general.h +++ b/general.h @@ -41,6 +41,7 @@ struct settings bool smooth; bool force_aspect; char cg_shader_path[256]; + char bsnes_shader_path[256]; unsigned filter; } video; diff --git a/gfx/gl.c b/gfx/gl.c index 5c0a8088fd..dc7a399e67 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -150,7 +150,7 @@ static int16_t glfw_input_state(void *data, const struct snes_keybind **binds, b // Checks if button is pressed, and sets fast-forwarding state bool pressed = false; for ( int i = 0; snes_keybinds[i].id != -1; i++ ) - if ( snes_keybinds[i].id == SNES_FAST_FORWARD_KEY ) + if ( snes_keybinds[i].id == SSNES_FAST_FORWARD_KEY ) set_fast_forward_button(glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes)); else if ( !pressed && snes_keybinds[i].id == (int)id ) pressed = glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes); diff --git a/settings.c b/settings.c index 0288dd3dd6..71b7c1be82 100644 --- a/settings.c +++ b/settings.c @@ -177,6 +177,12 @@ void parse_config(void) free(tmp_str); } + if (config_get_string(conf, "video_bsnes_shader", &tmp_str)) + { + strncpy(g_settings.video.bsnes_shader_path, tmp_str, sizeof(g_settings.video.bsnes_shader_path)); + free(tmp_str); + } + #ifdef HAVE_FILTER if (config_get_string(conf, "video_filter", &tmp_str)) { @@ -263,8 +269,6 @@ void parse_config(void) read_keybinds(conf); - // TODO: Keybinds. - config_file_free(conf); } @@ -291,7 +295,7 @@ static const struct bind_map bind_maps[2][13] = { { "input_player1_right", "input_player1_right_btn", "input_player1_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT }, { "input_player1_up", "input_player1_up_btn", "input_player1_up_axis", SNES_DEVICE_ID_JOYPAD_UP }, { "input_player1_down", "input_player1_down_btn", "input_player1_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN }, - { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SNES_FAST_FORWARD_KEY } + { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SSNES_FAST_FORWARD_KEY } }, { { "input_player2_a", "input_player2_a_btn", NULL, SNES_DEVICE_ID_JOYPAD_A }, @@ -306,7 +310,7 @@ static const struct bind_map bind_maps[2][13] = { { "input_player2_right", "input_player2_right_btn", "input_player2_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT }, { "input_player2_up", "input_player2_up_btn", "input_player2_up_axis", SNES_DEVICE_ID_JOYPAD_UP }, { "input_player2_down", "input_player2_down_btn", "input_player2_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN }, - { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SNES_FAST_FORWARD_KEY } + { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SSNES_FAST_FORWARD_KEY } } }; diff --git a/ssnes.cfg b/ssnes.cfg index b9d5f9aaba..894e7379f6 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -28,6 +28,9 @@ # Path to Cg shader. If enabled # video_cg_shader = "/path/to/cg/shader.cg" +# Path to bSNES-style XML shader. If both Cg shader path and XML shader path are defined, Cg shader will take priority. +# video_bsnes_shader = "/path/to/bsnes/xml/shader.shader" + # CPU-based filter. Valid ones are: hq2x, hq4x, grayscale, bleed, ntsc. # video_filter = ntsc From 7cc96aff01ea210ecc6f6fe5b7399adf350cad8f Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 17:42:58 +0100 Subject: [PATCH 10/40] Update build system for xml shaders. --- Makefile | 9 ++++++++- qb/config.libs.sh | 3 ++- qb/config.params.sh | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4c482a5bf0..8dfc8a60a7 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ TARGET = ssnes OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o LIBS = -lsamplerate +DEFINES = ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o @@ -40,6 +41,12 @@ ifeq ($(HAVE_CG), 1) LIBS += -lCg -lCgGL endif +ifeq ($(HAVE_XML), 1) + OBJ += gfx/shader_glsl.o + LIBS += $(XML_LIBS) + DEFINES += $(XML_CFLAGS) +endif + ifeq ($(HAVE_FILTER), 1) OBJ += hqflt/hq.o OBJ += hqflt/grayscale.o @@ -66,7 +73,7 @@ ssnes: $(OBJ) $(CXX) -o $@ $(OBJ) $(LIBS) $(CFLAGS) %.o: %.c config.h config.mk - $(CC) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< install: $(TARGET) install -m755 $(TARGET) $(DESTDIR)/$(PREFIX)/bin diff --git a/qb/config.libs.sh b/qb/config.libs.sh index a01eaf03d9..cf0bfb0774 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -20,13 +20,14 @@ check_lib GLFW -lglfw glfwInit check_critical GLFW "Cannot find GLFW library." check_lib CG -lCg cgCreateContext +check_pkgconf XML libxml-2.0 check_lib SRC -lsamplerate src_callback_new check_lib DYNAMIC -ldl dlopen # Creates config.mk. -VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG DYNAMIC" +VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG XML DYNAMIC" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index 34dc9a88d2..9e3ea58c8d 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -10,7 +10,8 @@ PACKAGE_VERSION=0.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 FILTER "Disable CPU filter support" yes -add_command_line_enable CG "Enable CG shader support" auto +add_command_line_enable CG "Enable Cg shader support" auto +add_command_line_enable XML "Enable bSNES-style XML shader support" auto add_command_line_enable ALSA "Enable ALSA support" auto add_command_line_enable OSS "Enable OSS support" auto add_command_line_enable RSOUND "Enable RSound support" auto From 42e7ac70d4bb47ff721cf14f1b584f417096a933 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 19:07:12 +0100 Subject: [PATCH 11/40] Add XML shader code support. :) --- gfx/shader_glsl.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++ gfx/shader_glsl.h | 34 +++++++ 2 files changed, 258 insertions(+) create mode 100644 gfx/shader_glsl.c create mode 100644 gfx/shader_glsl.h diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c new file mode 100644 index 0000000000..5e2ea1a8e3 --- /dev/null +++ b/gfx/shader_glsl.c @@ -0,0 +1,224 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + +// +// GLSL code here is mostly copypasted from bSNES. +// + +#include +#include +#include "general.h" + +#include +#include +#include +#include +#include + +static PFNGLCREATEPROGRAMPROC glCreateProgram = NULL; +static PFNGLUSEPROGRAMPROC glUseProgram = NULL; +static PFNGLCREATESHADERPROC glCreateShader = NULL; +static PFNGLDELETESHADERPROC glDeleteShader = NULL; +static PFNGLSHADERSOURCEPROC glShaderSource = NULL; +static PFNGLCOMPILESHADERPROC glCompileShader = NULL; +static PFNGLATTACHSHADERPROC glAttachShader = NULL; +static PFNGLDETACHSHADERPROC glDetachShader = NULL; +static PFNGLLINKPROGRAMPROC glLinkProgram = NULL; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL; +static PFNGLUNIFORM1IPROC glUniform1i = NULL; +static PFNGLUNIFORM2FVPROC glUniform2fv = NULL; +static PFNGLUNIFORM4FVPROC glUniform4fv = NULL; + +static bool glsl_enable = false; +static GLuint gl_program; +static GLuint fragment_shader; +static GLuint vertex_shader; + +static bool get_xml_shaders(const char *path, char **vertex_shader, char **fragment_shader) +{ + LIBXML_TEST_VERSION; + + xmlParserCtxtPtr ctx = xmlNewParserCtxt(); + if (!ctx) + { + SSNES_ERR("Failed to load libxml2 context.\n"); + return false; + } + + SSNES_LOG("Loading XML shader: %s\n", path); + xmlDocPtr doc = xmlCtxtReadFile(ctx, path, NULL, 0); + if (!doc) + { + SSNES_ERR("Failed to parse XML file: %s\n", path); + goto error; + } + + if (ctx->valid == 0) + { + SSNES_ERR("Cannot validate XML shader: %s\n", path); + goto error; + } + + xmlNodePtr head = xmlDocGetRootElement(doc); + xmlNodePtr cur = NULL; + for (cur = head; cur; cur = cur->next) + { + if (cur->type == XML_ELEMENT_NODE && strcmp((const char*)cur->name, "shader") == 0) + { + xmlChar *attr; + if ((attr = xmlGetProp(cur, (const xmlChar*)"language")) && strcmp((const char*)attr, "GLSL") == 0) + break; + } + } + + if (!cur) // We couldn't find any GLSL shader :( + goto error; + + bool vertex_found = false; + bool fragment_found = false; + // Iterate to check if we find fragment and/or vertex shaders. + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type != XML_ELEMENT_NODE) + continue; + + xmlChar *content = xmlNodeGetContent(cur); + if (!content) + continue; + + if (strcmp((const char*)cur->name, "vertex") == 0 && !vertex_found) + { + *vertex_shader = malloc(xmlStrlen(content) + 1); + strcpy(*vertex_shader, (const char*)content); + vertex_found = true; + } + else if (strcmp((const char*)cur->name, "fragment") == 0 && !fragment_found) + { + *fragment_shader = malloc(xmlStrlen(content) + 1); + strcpy(*fragment_shader, (const char*)content); + fragment_found = true; + } + } + + if (!vertex_found && !fragment_found) + { + SSNES_ERR("Couldn't find vertex shader nor fragment shader in XML file.\n"); + goto error; + } + + + xmlFreeDoc(doc); + xmlFreeParserCtxt(ctx); + return true; + +error: + if (doc) + xmlFreeDoc(doc); + xmlFreeParserCtxt(ctx); + return false; +} + +bool gl_glsl_init(const char *path) +{ + // Load shader functions. + glCreateProgram = glfwGetProcAddress("glCreateProgram"); + glUseProgram = glfwGetProcAddress("glUseProgram"); + glCreateShader = glfwGetProcAddress("glCreateShader"); + glDeleteShader = glfwGetProcAddress("glDeleteShader"); + glShaderSource = glfwGetProcAddress("glShaderSource"); + glCompileShader = glfwGetProcAddress("glCompileShader"); + glAttachShader = glfwGetProcAddress("glAttachShader"); + glDetachShader = glfwGetProcAddress("glDetachShader"); + glLinkProgram = glfwGetProcAddress("glLinkProgram"); + glGetUniformLocation = glfwGetProcAddress("glGetUniformLocation"); + glUniform1i = glfwGetProcAddress("glUniform1i"); + glUniform2fv = glfwGetProcAddress("glUniform2fv"); + glUniform4fv = glfwGetProcAddress("glUniform4fv"); + + SSNES_LOG("Checking GLSL shader support ...\n"); + bool shader_support = glCreateProgram && glUseProgram && glCreateShader + && glDeleteShader && glShaderSource && glCompileShader && glAttachShader + && glDetachShader && glLinkProgram && glGetUniformLocation + && glUniform1i && glUniform2fv && glUniform4fv; + + if (!shader_support) + { + SSNES_ERR("GLSL shaders aren't supported by your GL driver.\n"); + return false; + } + + gl_program = glCreateProgram(); + + char *vertex_prog = NULL; + char *fragment_prog = NULL; + if (!get_xml_shaders(path, &vertex_prog, &fragment_prog)) + return false; + + if (vertex_prog) + { + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, (const char**)&vertex_prog, 0); + glCompileShader(vertex_shader); + glAttachShader(gl_program, vertex_shader); + free(vertex_prog); + } + if (fragment_prog) + { + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, (const char**)&fragment_prog, 0); + glCompileShader(fragment_shader); + glAttachShader(gl_program, fragment_shader); + free(fragment_prog); + } + + if (vertex_prog || fragment_prog) + { + glLinkProgram(gl_program); + glUseProgram(gl_program); + } + + glsl_enable = true; + return true; +} + +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) +{ + if (glsl_enable) + { + GLint location; + + float inputSize[2] = {width, height}; + location = glGetUniformLocation(gl_program, "rubyInputSize"); + glUniform2fv(location, 1, inputSize); + + float outputSize[2] = {out_width, out_height}; + location = glGetUniformLocation(gl_program, "rubyOutputSize"); + glUniform2fv(location, 1, outputSize); + + float textureSize[2] = {tex_width, tex_height}; + location = glGetUniformLocation(gl_program, "rubyTextureSize"); + glUniform2fv(location, 1, textureSize); + } +} + +void gl_glsl_set_proj_matrix(void) +{} diff --git a/gfx/shader_glsl.h b/gfx/shader_glsl.h new file mode 100644 index 0000000000..4b0ad8761e --- /dev/null +++ b/gfx/shader_glsl.h @@ -0,0 +1,34 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + + +#ifndef __SSNES_GLSL_H +#define __SSNES_GLSL_H + +#include + +bool gl_glsl_init(const char *path); + +void gl_glsl_deinit(void); + +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); + +#endif From e5382f863100007e3a098a306373c7eb02ee2ce0 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 19:25:27 +0100 Subject: [PATCH 12/40] Fix odd bug that broke cgwg-CRT shader. --- gfx/gl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index dc7a399e67..bf2cdaca75 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -220,6 +220,8 @@ static inline void gl_shader_set_params(unsigned width, unsigned height, #endif } +#define SNES_ASPECT_RATIO (4.0/3) + static void GLFWCALL resize(int width, int height) { glMatrixMode(GL_PROJECTION); @@ -228,7 +230,7 @@ static void GLFWCALL resize(int width, int height) if ( keep_aspect ) { - float desired_aspect = 4.0/3; + float desired_aspect = SNES_ASPECT_RATIO; float device_aspect = (float)width / height; // If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff), @@ -405,8 +407,8 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) glBindTexture(GL_TEXTURE_2D, gl->texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + 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, gl->tex_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); From 988d1b3334ede2aeb0806c5ec54f4b903c6261bf Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 19:29:29 +0100 Subject: [PATCH 13/40] Add a warning when both Cg and XML shaders are selected. --- general.h | 4 ++++ gfx/gl.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/general.h b/general.h index 048abde579..a26b35b0d2 100644 --- a/general.h +++ b/general.h @@ -98,4 +98,8 @@ extern struct global g_extern; fprintf(stderr, "SSNES [ERROR] :: " msg, ##args); \ } while(0) +#define SSNES_WARN(msg, args...) do { \ + fprintf(stderr, "SSNES [WARN] :: " msg, ##args); \ + } while(0) + #endif diff --git a/gfx/gl.c b/gfx/gl.c index bf2cdaca75..04d8606c2e 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -172,6 +172,9 @@ static const input_driver_t input_glfw = { static inline bool gl_shader_init(void) { + if (strlen(g_settings.video.cg_shader_path) > 0 && strlen(g_settings.video.bsnes_shader_path) > 0) + SSNES_WARN("Both Cg and bSNES XML shader are defined in config file. Cg shader will be selected by default.\n"); + #ifdef HAVE_CG if (strlen(g_settings.video.cg_shader_path) > 0) return gl_cg_init(g_settings.video.cg_shader_path); From 6f9796a3fb0929f44430b7f2311cdbe105dc0620 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 19:51:19 +0100 Subject: [PATCH 14/40] Update build system to enable/disable ffmpeg. --- Makefile | 9 +++++++-- qb/config.libs.sh | 14 ++++++++++++-- qb/config.params.sh | 1 + 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 09c6457fde..1d50fa7187 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,8 @@ include config.mk TARGET = ssnes -OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o record/ffemu.o +OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o -LIBS = -lsamplerate -lavformat -lavutil -lavcodec -lswscale LIBS = -lsamplerate DEFINES = @@ -56,6 +55,12 @@ ifeq ($(HAVE_FILTER), 1) OBJ += hqflt/snes_ntsc/snes_ntsc.o endif +ifeq ($(HAVE_FFMPEG), 1) + OBJ += record/ffemu.o + LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS) + DEFINES += $(AVCODEC_CFLAGS) $(AVCORE_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS) +endif + ifeq ($(HAVE_DYNAMIC), 1) LIBS += -ldl else diff --git a/qb/config.libs.sh b/qb/config.libs.sh index cf0bfb0774..20ad85e671 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -22,12 +22,22 @@ check_critical GLFW "Cannot find GLFW library." check_lib CG -lCg cgCreateContext check_pkgconf XML libxml-2.0 +if [ $HAVE_FFMPEG != no ]; then + check_pkgconf AVCODEC libavcodec + check_pkgconf AVFORMAT libavformat + check_pkgconf AVCORE libavcore + check_pkgconf AVUTIL libavutil + check_pkgconf SWSCALE libswscale + + ( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVCORE = no ] || [ $HAVE_AVUTIL = no ] || [ $HAVE_SWSCALE = no ] ) && HAVE_FFMPEG=no ) || HAVE_FFMPEG=yes +fi + check_lib SRC -lsamplerate src_callback_new check_lib DYNAMIC -ldl dlopen -# Creates config.mk. -VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG XML DYNAMIC" +# Creates config.mk and config.h. +VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index 9e3ea58c8d..20d930236f 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -9,6 +9,7 @@ PACKAGE_VERSION=0.1 # $3: Default arg. auto implies that HAVE_ALSA will be set according to library checks later on. 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" auto add_command_line_enable FILTER "Disable CPU filter support" yes add_command_line_enable CG "Enable Cg shader support" auto add_command_line_enable XML "Enable bSNES-style XML shader support" auto From ab30663b37aafe417fa461ff1dac55420739125a Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 20:07:55 +0100 Subject: [PATCH 15/40] Can start recording at will with -r/--record. --- general.h | 5 +++ ssnes.c | 130 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 92 insertions(+), 43 deletions(-) diff --git a/general.h b/general.h index e6b7d550e8..20e9b5a7f9 100644 --- a/general.h +++ b/general.h @@ -24,6 +24,7 @@ #include "driver.h" #include #include "record/ffemu.h" +#include "config.h" #define MAX_PLAYERS 2 @@ -84,7 +85,11 @@ struct global char config_path[256]; char basename[256]; +#ifdef HAVE_FFMPEG ffemu_t *rec; + char record_path[256]; + bool recording; +#endif }; void parse_config(void); diff --git a/ssnes.c b/ssnes.c index ee9160649e..eae767bddd 100644 --- a/ssnes.c +++ b/ssnes.c @@ -85,15 +85,18 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) if ( !g_extern.video_active ) return; - ///////////// - struct ffemu_video_data ffemu_data = { - .data = data, - .pitch = height == 448 || height == 478 ? 1024 : 2048, - .width = width, - .height = height - }; - ffemu_push_video(g_extern.rec, &ffemu_data); - ///////////// +#ifdef HAVE_FFMPEG + if (g_extern.recording) + { + struct ffemu_video_data ffemu_data = { + .data = data, + .pitch = height == 448 || height == 478 ? 1024 : 2048, + .width = width, + .height = height + }; + ffemu_push_video(g_extern.rec, &ffemu_data); + } +#endif #ifdef HAVE_FILTER uint16_t output_filter[width * height * 4 * 4]; @@ -142,16 +145,19 @@ static void audio_sample(uint16_t left, uint16_t right) if ( !g_extern.audio_active ) return; - ///////// - static int16_t static_data[2]; - static_data[0] = left; - static_data[1] = right; - struct ffemu_audio_data ffemu_data = { - .data = static_data, - .frames = 1 - }; - ffemu_push_audio(g_extern.rec, &ffemu_data); - ////////// +#ifdef HAVE_FFMPEG + if (g_extern.recording) + { + static int16_t static_data[2]; + static_data[0] = left; + static_data[1] = right; + struct ffemu_audio_data ffemu_data = { + .data = static_data, + .frames = 1 + }; + ffemu_push_audio(g_extern.rec, &ffemu_data); + } +#endif static float data[AUDIO_CHUNK_SIZE_NONBLOCKING]; static int data_ptr = 0; @@ -210,15 +216,24 @@ static void fill_pathname(char *out_path, char *in_path, const char *replace) strcat(out_path, replace); } +#ifdef HAVE_FFMPEG +#define FFMPEG_HELP_QUARK " | -r/--record " +#else +#define FFMPEG_HELP_QUARK +#endif + static void print_help(void) { puts("================================================="); puts("ssnes: Simple Super Nintendo Emulator (libsnes)"); puts("================================================="); - puts("Usage: ssnes [rom file] [-h/--help | -s/--save]"); + puts("Usage: ssnes [rom file] [-h/--help | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-c/--config: Path for config file. Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg"); +#ifdef HAVE_FFMPEG + puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); +#endif puts("\t-v/--verbose: Verbose logging"); } @@ -233,13 +248,23 @@ static void parse_input(int argc, char *argv[]) struct option opts[] = { { "help", 0, NULL, 'h' }, { "save", 1, NULL, 's' }, +#ifdef HAVE_FFMPEG + { "record", 1, NULL, 'r' }, +#endif { "verbose", 0, NULL, 'v' }, { "config", 0, NULL, 'c' }, { NULL, 0, NULL, 0 } }; int option_index = 0; - char optstring[] = "hs:vc:"; + +#ifdef HAVE_FFMPEG +#define FFMPEG_RECORD_ARG "r:" +#else +#define FFMPEG_RECORD_ARG +#endif + + char optstring[] = "hs:vc:" FFMPEG_RECORD_ARG; for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -266,6 +291,13 @@ static void parse_input(int argc, char *argv[]) strncpy(g_extern.config_path, optarg, sizeof(g_extern.config_path) - 1); break; +#ifdef HAVE_FFMPEG + case 'r': + strncpy(g_extern.record_path, optarg, sizeof(g_extern.record_path) - 1); + g_extern.recording = true; + break; +#endif + case '?': print_help(); exit(1); @@ -358,24 +390,33 @@ int main(int argc, char *argv[]) load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); load_save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); - struct ffemu_rational ntsc_fps = {60000, 1001}; - struct ffemu_rational pal_fps = {50000, 1001}; - //////// - struct ffemu_params params = { - .vcodec = FFEMU_VIDEO_H264, - .acodec = FFEMU_AUDIO_VORBIS, - .rescaler = FFEMU_RESCALER_POINT, - .out_width = 512, - .out_height = 448, - .channels = 2, - .samplerate = 32040, - .filename = "/tmp/ssnes.mkv", - .fps = snes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps, - .aspect_ratio = 4.0/3 - }; - g_extern.rec = ffemu_new(¶ms); - assert(g_extern.rec); - ///////// +#ifdef HAVE_FFMPEG + // Hardcode these options at the moment. Should be specificed in the config file later on. + if (g_extern.recording) + { + struct ffemu_rational ntsc_fps = {60000, 1001}; + struct ffemu_rational pal_fps = {50000, 1001}; + struct ffemu_params params = { + .vcodec = FFEMU_VIDEO_H264, + .acodec = FFEMU_AUDIO_VORBIS, + .rescaler = FFEMU_RESCALER_POINT, + .out_width = 512, + .out_height = 448, + .channels = 2, + .samplerate = 32040, + .filename = g_extern.record_path, + .fps = snes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps, + .aspect_ratio = 4.0/3 + }; + SSNES_LOG("Recording with FFmpeg to %s.\n", g_extern.record_path); + g_extern.rec = ffemu_new(¶ms); + if (!g_extern.rec) + { + SSNES_ERR("Failed to start FFmpeg recording.\n"); + g_extern.recording = false; + } + } +#endif ///// TODO: Modular friendly!!! for(;;) @@ -403,10 +444,13 @@ int main(int argc, char *argv[]) psnes_run(); } - /////////// - ffemu_finalize(g_extern.rec); - ffemu_free(g_extern.rec); - /////////// +#ifdef HAVE_FFMPEG + if (g_extern.recording) + { + ffemu_finalize(g_extern.rec); + ffemu_free(g_extern.rec); + } +#endif save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM); save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC); From 7bac622bf3a2edc97868fc03c60158243834a05a Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 20:09:12 +0100 Subject: [PATCH 16/40] Fix header guard conflict --- config.def.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.def.h b/config.def.h index 477d637d9b..0bf44024f2 100644 --- a/config.def.h +++ b/config.def.h @@ -19,8 +19,8 @@ // // -#ifndef __CONFIG_H -#define __CONFIG_H +#ifndef __CONFIG_DEF_H +#define __CONFIG_DEF_H #include #include From 889924283fd0374b1870b37c745f8a50d58751cb Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 5 Jan 2011 20:47:31 +0100 Subject: [PATCH 17/40] Remove undeeded slash in Makefile install target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1d50fa7187..4017c073b5 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ ssnes: $(OBJ) $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< install: $(TARGET) - install -m755 $(TARGET) $(DESTDIR)/$(PREFIX)/bin + install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg uninstall: $(TARGET) From 4cc3f7aaabc0d4db22a83a1b2824aa6d4552eeaf Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 18:34:11 +0100 Subject: [PATCH 18/40] Start moving over to SDL rather than GLFW to minimize dependencies. --- Makefile | 10 +-- config.def.h | 62 +++++++++-------- driver.c | 31 ++++++++- driver.h | 2 + gfx/gl.c | 167 ++++++++------------------------------------ gfx/shader_glsl.c | 31 +++++---- input/sdl.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ qb/config.libs.sh | 6 +- settings.c | 101 +++++++++++++++------------ ssnes.c | 11 ++- 10 files changed, 352 insertions(+), 240 deletions(-) create mode 100644 input/sdl.c diff --git a/Makefile b/Makefile index 4017c073b5..30cae318a8 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ TARGET = ssnes OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o -LIBS = -lsamplerate +LIBS = -lsamplerate -lSDL DEFINES = ifeq ($(HAVE_RSOUND), 1) @@ -31,9 +31,10 @@ ifeq ($(HAVE_JACK),1) LIBS += -ljack endif -ifeq ($(HAVE_GLFW), 1) - OBJ += gfx/gl.o - LIBS += -lglfw +ifeq ($(HAVE_SDL), 1) + OBJ += gfx/gl.o input/sdl.o + LIBS += $(SDL_LIBS) -lGL + DEFINES += $(SDL_CFLAGS) endif ifeq ($(HAVE_CG), 1) @@ -93,6 +94,7 @@ clean: rm -f audio/*.o rm -f conf/*.o rm -f gfx/*.o + rm -f record/*.o rm -f hqflt/*.o rm -f hqflt/snes_ntsc/*.o rm -f $(TARGET) diff --git a/config.def.h b/config.def.h index 0bf44024f2..a30f08adc0 100644 --- a/config.def.h +++ b/config.def.h @@ -22,7 +22,7 @@ #ifndef __CONFIG_DEF_H #define __CONFIG_DEF_H -#include +#include #include #include "libsnes.hpp" #include "driver.h" @@ -39,9 +39,12 @@ #define AUDIO_AL 5 #define AUDIO_JACK 6 //////////////////////// +#define INPUT_SDL 7 +//////////////////////// #define VIDEO_DEFAULT_DRIVER VIDEO_GL #define AUDIO_DEFAULT_DRIVER AUDIO_ALSA +#define INPUT_DEFAULT_DRIVER INPUT_SDL //////////////// @@ -115,48 +118,47 @@ static const bool audio_sync = true; // Player 1 static const struct snes_keybind snes_keybinds_1[] = { // SNES button | keyboard key | js btn | js axis | - { SNES_DEVICE_ID_JOYPAD_A, 'X', 1, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_B, 'Z', 0, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_X, 'S', 3, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_Y, 'A', 2, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_L, 'Q', 4, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_R, 'W', 5, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_LEFT, GLFW_KEY_LEFT, 11, AXIS_NEG(0) }, - { SNES_DEVICE_ID_JOYPAD_RIGHT, GLFW_KEY_RIGHT, 12, AXIS_POS(0) }, - { SNES_DEVICE_ID_JOYPAD_UP, GLFW_KEY_UP, 13, AXIS_POS(1) }, - { SNES_DEVICE_ID_JOYPAD_DOWN, GLFW_KEY_DOWN, 14, AXIS_NEG(1) }, - { SNES_DEVICE_ID_JOYPAD_START, GLFW_KEY_ENTER, 7, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_SELECT, GLFW_KEY_RSHIFT, 6, AXIS_NONE }, - { SSNES_FAST_FORWARD_KEY, GLFW_KEY_SPACE, 10, AXIS_NONE }, + { 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_POS(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, 14, AXIS_NEG(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 }, { -1 } }; // Player 2 static const struct snes_keybind snes_keybinds_2[] = { // SNES button | keyboard key | js btn | js axis | - { SNES_DEVICE_ID_JOYPAD_A, 'B', 1, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_B, 'V', 0, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_X, 'G', 3, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_Y, 'F', 2, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_L, 'R', 4, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_R, 'T', 5, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_LEFT, 'J', 11, AXIS_NEG(0) }, - { SNES_DEVICE_ID_JOYPAD_RIGHT, 'L', 12, AXIS_POS(0) }, - { SNES_DEVICE_ID_JOYPAD_UP, 'I', 13, AXIS_POS(1) }, - { SNES_DEVICE_ID_JOYPAD_DOWN, 'K', 14, AXIS_NEG(1) }, - { SNES_DEVICE_ID_JOYPAD_START, 'P', 6, AXIS_NONE }, - { SNES_DEVICE_ID_JOYPAD_SELECT, 'O', 7, AXIS_NONE }, + { 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_POS(1) }, + { SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_NEG(1) }, + { SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE }, + { SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE }, { -1 } }; ///// Save state -#define SAVE_STATE_KEY GLFW_KEY_F2 +#define SAVE_STATE_KEY SDLK_F2 ///// Load state -#define LOAD_STATE_KEY GLFW_KEY_F4 +#define LOAD_STATE_KEY SDLK_F4 //// Toggles between fullscreen and windowed mode. -#define TOGGLE_FULLSCREEN 'F' - +#define TOGGLE_FULLSCREEN SDLK_f #endif diff --git a/driver.c b/driver.c index 4e694af930..656f869006 100644 --- a/driver.c +++ b/driver.c @@ -50,6 +50,12 @@ static const video_driver_t *video_drivers[] = { #endif }; +static const input_driver_t *input_drivers[] = { +#ifdef HAVE_SDL + &input_sdl, +#endif +}; + static void find_audio_driver(void) { for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++) @@ -86,6 +92,24 @@ static void find_video_driver(void) exit(1); } +static void find_input_driver(void) +{ + for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++) + { + if (strcasecmp(g_settings.input.driver, input_drivers[i]->ident) == 0) + { + driver.input = input_drivers[i]; + return; + } + } + SSNES_ERR("Couldn't find any input driver named \"%s\"\n", g_settings.input.driver); + fprintf(stderr, "Available video drivers are:\n"); + for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++) + fprintf(stderr, "\t%s\n", video_drivers[i]->ident); + + exit(1); +} + void init_drivers(void) { init_video_input(); @@ -141,6 +165,7 @@ void init_video_input(void) int scale = 2; find_video_driver(); + find_input_driver(); // We multiply scales with 2 to allow for hi-res games. #if HAVE_FILTER @@ -177,18 +202,22 @@ void init_video_input(void) exit(1); } + // Video driver also provides an input driver. if ( driver.input != NULL ) { driver.input_data = driver.video_data; } - else + else // We use our configured input driver. { driver.input = tmp; if (driver.input != NULL) { driver.input_data = driver.input->init(); if ( driver.input_data == NULL ) + { + SSNES_ERR("Cannot init input driver. Exiting ...\n"); exit(1); + } } else { diff --git a/driver.h b/driver.h index 0deb3672d7..9d8a0f55b7 100644 --- a/driver.h +++ b/driver.h @@ -65,6 +65,7 @@ typedef struct input_driver void* (*init)(void); void (*poll)(void* data); int16_t (*input_state)(void* data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id); + bool (*key_pressed)(void* data, int key); void (*free)(void* data); const char *ident; } input_driver_t; @@ -108,6 +109,7 @@ extern const audio_driver_t audio_roar; extern const audio_driver_t audio_openal; extern const audio_driver_t audio_jack; extern const video_driver_t video_gl; +extern const input_driver_t input_sdl; //////////////////////////////////////////////// #endif diff --git a/gfx/gl.c b/gfx/gl.c index 04d8606c2e..44dd7f7346 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -15,11 +15,9 @@ * If not, see . */ -#define GL_GLEXT_PROTOTYPES #include "driver.h" -#include -#include + #include #include "libsnes.hpp" #include @@ -28,6 +26,12 @@ #include "general.h" #include "config.h" +#define NO_SDL_GLEXT +#include +#include + +#define GL_GLEXT_PROTOTYPES +#include #ifdef HAVE_CG #include "shader_cg.h" @@ -66,110 +70,6 @@ typedef struct gl } gl_t; -static void glfw_input_poll(void *data) -{ - (void)data; - glfwPollEvents(); -} - -#define BUTTONS_MAX 128 -#define AXES_MAX 128 - -static unsigned joypad_id[2]; -static unsigned joypad_buttons[2]; -static unsigned joypad_axes[2]; -static bool joypad_inited = false; -static unsigned joypad_count = 0; - -static int init_joypads(int max_pads) -{ - // Finds the first (two) joypads that are alive - int count = 0; - for ( int i = GLFW_JOYSTICK_1; (i <= GLFW_JOYSTICK_LAST) && (count < max_pads); i++ ) - { - if ( glfwGetJoystickParam(i, GLFW_PRESENT) == GL_TRUE ) - { - joypad_id[count] = i; - joypad_buttons[count] = glfwGetJoystickParam(i, GLFW_BUTTONS); - if (joypad_buttons[count] > BUTTONS_MAX) - joypad_buttons[count] = BUTTONS_MAX; - joypad_axes[count] = glfwGetJoystickParam(i, GLFW_AXES); - if (joypad_axes[count] > AXES_MAX) - joypad_axes[count] = AXES_MAX; - count++; - } - } - joypad_inited = true; - return count; -} - -static bool glfw_is_pressed(int port_num, const struct snes_keybind *key, unsigned char *buttons, float *axes) -{ - if (glfwGetKey(key->key)) - return true; - if (port_num >= joypad_count) - return false; - if (key->joykey < joypad_buttons[port_num] && buttons[key->joykey] == GLFW_PRESS) - return true; - - if (key->joyaxis != AXIS_NONE) - { - if (AXIS_NEG_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_NEG_GET(key->joyaxis)] <= -g_settings.input.axis_threshold) - return true; - if (AXIS_POS_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_POS_GET(key->joyaxis)] >= g_settings.input.axis_threshold) - return true; - } - return false; -} - -static int16_t glfw_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) -{ - if ( device != SNES_DEVICE_JOYPAD ) - return 0; - - if ( !joypad_inited ) - joypad_count = init_joypads(2); - - int port_num = port ? 1 : 0; - unsigned char buttons[BUTTONS_MAX]; - float axes[AXES_MAX]; - - if ( joypad_count > port_num ) - { - glfwGetJoystickButtons(joypad_id[port_num], buttons, joypad_buttons[port_num]); - glfwGetJoystickPos(joypad_id[port_num], axes, joypad_axes[port_num]); - } - - - const struct snes_keybind *snes_keybinds; - if (port == SNES_PORT_1) - snes_keybinds = binds[0]; - else - snes_keybinds = binds[1]; - - // Checks if button is pressed, and sets fast-forwarding state - bool pressed = false; - for ( int i = 0; snes_keybinds[i].id != -1; i++ ) - if ( snes_keybinds[i].id == SSNES_FAST_FORWARD_KEY ) - set_fast_forward_button(glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes)); - else if ( !pressed && snes_keybinds[i].id == (int)id ) - pressed = glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes); - - return pressed; -} - -static void glfw_free_input(void *data) -{ - free(data); -} - -static const input_driver_t input_glfw = { - .poll = glfw_input_poll, - .input_state = glfw_input_state, - .free = glfw_free_input, - .ident = "glfw" -}; - static inline bool gl_shader_init(void) { if (strlen(g_settings.video.cg_shader_path) > 0 && strlen(g_settings.video.bsnes_shader_path) > 0) @@ -225,7 +125,7 @@ static inline void gl_shader_set_params(unsigned width, unsigned height, #define SNES_ASPECT_RATIO (4.0/3) -static void GLFWCALL resize(int width, int height) +static void set_viewport(int width, int height) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -273,7 +173,7 @@ static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, i return frames/time; } -static inline void show_fps(void) +static void show_fps(void) { // Shows FPS in taskbar. static int frames = 0; @@ -292,8 +192,8 @@ static inline void show_fps(void) float fps = tv_to_fps(&tmp_tv, &new_tv, 180); - snprintf(tmpstr, sizeof(tmpstr) - 1, "SSNES || FPS: %6.1f || Frames: %d", fps, frames); - glfwSetWindowTitle(tmpstr); + snprintf(tmpstr, sizeof(tmpstr), "SSNES || FPS: %6.1f || Frames: %d", fps, frames); + SDL_WM_SetCaption(tmpstr, NULL); } frames++; } @@ -335,7 +235,7 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i glDrawArrays(GL_QUADS, 0, 4); show_fps(); - glfwSwapBuffers(); + SDL_GL_SwapBuffers(); return true; } @@ -348,7 +248,7 @@ static void gl_free(void *data) glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDeleteTextures(1, &gl->texture); - glfwTerminate(); + SDL_QuitSubSystem(SDL_INIT_VIDEO); } static void gl_set_nonblock_state(void *data, bool state) @@ -356,15 +256,22 @@ static void gl_set_nonblock_state(void *data, bool state) gl_t *gl = data; if (gl->vsync) { - if (state) - glfwSwapInterval(0); - else - glfwSwapInterval(1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, state ? 0 : 1); + //SDL_SetVideoMode(gl->width, gl->height, 32, SDL_OPENGL | (video->fullscreen ? SDL_FULLSCREEN : 0)); } } static void* gl_init(video_info_t *video, const input_driver_t **input) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) + return NULL; + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0); + + if (!SDL_SetVideoMode(video->width, video->height, 32, SDL_OPENGL | (video->fullscreen ? SDL_FULLSCREEN : 0))) + return NULL; + gl_t *gl = calloc(1, sizeof(gl_t)); if ( gl == NULL ) return NULL; @@ -375,25 +282,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) else gl->tex_filter = GL_NEAREST; - glfwInit(); - - int res; - res = glfwOpenWindow(video->width, video->height, 0, 0, 0, 0, 0, 0, (video->fullscreen) ? GLFW_FULLSCREEN : GLFW_WINDOW); - - if (!res) - { - glfwTerminate(); - free(gl); - return NULL; - } - - glfwSetWindowSizeCallback(resize); - - if ( video->vsync ) - glfwSwapInterval(1); // Force vsync - else - glfwSwapInterval(0); - gl->vsync = video->vsync; + set_viewport(video->width, video->height); glEnable(GL_TEXTURE_2D); glDisable(GL_DITHER); @@ -401,7 +290,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) glColor3f(1, 1, 1); glClearColor(0, 0, 0, 0); - glfwSetWindowTitle("SSNES"); + SDL_WM_SetCaption("SSNES", NULL); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -434,7 +323,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) gl_shader_init(); - *input = &input_glfw; + *input = NULL; return gl; } @@ -443,7 +332,7 @@ const video_driver_t video_gl = { .frame = gl_frame, .set_nonblock_state = gl_set_nonblock_state, .free = gl_free, - .ident = "glfw" + .ident = "gl" }; diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 5e2ea1a8e3..4a5af0b8ef 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -23,8 +23,11 @@ #include #include "general.h" +#define NO_SDL_GLEXT #include -#include +//#include +#include +#include #include #include #include @@ -135,19 +138,19 @@ error: bool gl_glsl_init(const char *path) { // Load shader functions. - glCreateProgram = glfwGetProcAddress("glCreateProgram"); - glUseProgram = glfwGetProcAddress("glUseProgram"); - glCreateShader = glfwGetProcAddress("glCreateShader"); - glDeleteShader = glfwGetProcAddress("glDeleteShader"); - glShaderSource = glfwGetProcAddress("glShaderSource"); - glCompileShader = glfwGetProcAddress("glCompileShader"); - glAttachShader = glfwGetProcAddress("glAttachShader"); - glDetachShader = glfwGetProcAddress("glDetachShader"); - glLinkProgram = glfwGetProcAddress("glLinkProgram"); - glGetUniformLocation = glfwGetProcAddress("glGetUniformLocation"); - glUniform1i = glfwGetProcAddress("glUniform1i"); - glUniform2fv = glfwGetProcAddress("glUniform2fv"); - glUniform4fv = glfwGetProcAddress("glUniform4fv"); + glCreateProgram = SDL_GL_GetProcAddress("glCreateProgram"); + glUseProgram = SDL_GL_GetProcAddress("glUseProgram"); + glCreateShader = SDL_GL_GetProcAddress("glCreateShader"); + glDeleteShader = SDL_GL_GetProcAddress("glDeleteShader"); + glShaderSource = SDL_GL_GetProcAddress("glShaderSource"); + glCompileShader = SDL_GL_GetProcAddress("glCompileShader"); + glAttachShader = SDL_GL_GetProcAddress("glAttachShader"); + glDetachShader = SDL_GL_GetProcAddress("glDetachShader"); + glLinkProgram = SDL_GL_GetProcAddress("glLinkProgram"); + glGetUniformLocation = SDL_GL_GetProcAddress("glGetUniformLocation"); + glUniform1i = SDL_GL_GetProcAddress("glUniform1i"); + glUniform2fv = SDL_GL_GetProcAddress("glUniform2fv"); + glUniform4fv = SDL_GL_GetProcAddress("glUniform4fv"); SSNES_LOG("Checking GLSL shader support ...\n"); bool shader_support = glCreateProgram && glUseProgram && glCreateShader diff --git a/input/sdl.c b/input/sdl.c new file mode 100644 index 0000000000..68c4c058ac --- /dev/null +++ b/input/sdl.c @@ -0,0 +1,171 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + +#include "driver.h" + +#include +#include +#include "general.h" +#include +#include +#include + +typedef struct sdl_input +{ + bool quitting; + SDL_Joystick *joysticks[2]; + unsigned num_axes[2]; + unsigned num_buttons[2]; + unsigned num_joysticks; +} sdl_input_t; + +static void* sdl_input_init(void) +{ + sdl_input_t *sdl = calloc(1, sizeof(*sdl)); + if (!sdl) + return NULL; + + if (SDL_Init(SDL_INIT_JOYSTICK) < 0) + return NULL; + + sdl->num_joysticks = SDL_NumJoysticks(); + for (unsigned i = 0; i < sdl->num_joysticks; i++) + { + sdl->joysticks[i] = SDL_JoystickOpen(i); + if (!sdl->joysticks[i]) + { + SSNES_ERR("Couldn't open SDL joystick %d\n", i); + free(sdl); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + return NULL; + } + + SSNES_LOG("Opened Joystick: %s\n", SDL_JoystickName(i)); + sdl->num_axes[i] = SDL_JoystickNumAxes(sdl->joysticks[i]); + sdl->num_buttons[i] = SDL_JoystickNumButtons(sdl->joysticks[i]); + } + + return sdl; +} + +static bool sdl_key_pressed(void *data, int key) +{ + // Check to see if we have to exit. + sdl_input_t *sdl = data; + if (sdl->quitting && key == g_settings.input.exit_emulator_key) + return true; + + int num_keys; + Uint8 *keymap = SDL_GetKeyState(&num_keys); + + if (key >= num_keys) + return false; + + return keymap[key]; +} + +static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_keybind *key) +{ + if (sdl_key_pressed(sdl, key->key)) + return true; + if (port_num >= sdl->num_joysticks) + return false; + if (key->joykey < sdl->num_buttons[port_num] && SDL_JoystickGetButton(sdl->joysticks[port_num], key->joykey)) + return true; + + if (key->joyaxis != AXIS_NONE) + { + if (AXIS_NEG_GET(key->joyaxis) < sdl->num_axes[port_num]) + { + Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(key->joyaxis)); + float scaled = (float)val / 0x8000; + if (scaled < -g_settings.input.axis_threshold) + return true; + } + if (AXIS_POS_GET(key->joyaxis) < sdl->num_axes[port_num]) + { + Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(key->joyaxis)); + float scaled = (float)val / 0x8000; + if (scaled > g_settings.input.axis_threshold) + return true; + } + } + + return false; +} + +static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id) +{ + sdl_input_t *sdl = data; + if (device != SNES_DEVICE_JOYPAD) + return 0; + + const struct snes_keybind *snes_keybinds = binds[port == SNES_PORT_1 ? 0 : 1]; + + // Checks if button is pressed, and sets fast-forwarding state + bool pressed = false; + int port_num = port == SNES_PORT_1 ? 0 : 1; + for (int i = 0; snes_keybinds[i].id != -1; i++) + { + if (snes_keybinds[i].id == SSNES_FAST_FORWARD_KEY) + set_fast_forward_button(sdl_is_pressed(sdl, port_num, &snes_keybinds[i])); + else if (!pressed && snes_keybinds[i].id == (int)id) + pressed = sdl_is_pressed(sdl, port_num, &snes_keybinds[i]); + } + + return pressed; +} + +static void sdl_input_free(void *data) +{ + if (data) + { + sdl_input_t *sdl = data; + for (int i = 0; i < sdl->num_joysticks; i++) + SDL_JoystickClose(i); + + free(data); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + } +} + +static void sdl_input_poll(void *data) +{ + SDL_PumpEvents(); + SDL_Event event; + + // Search for SDL_QUIT + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + sdl_input_t *sdl = data; + sdl->quitting = true; + break; + } + } +} + +const input_driver_t input_sdl = { + .init = sdl_input_init, + .poll = sdl_input_poll, + .input_state = sdl_input_state, + .key_pressed = sdl_key_pressed, + .free = sdl_input_free, + .ident = "sdl" +}; + diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 20ad85e671..a5e0b281f4 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -16,8 +16,8 @@ check_lib RSOUND -lrsound rsd_init check_lib ROAR -lroar roar_vs_new check_lib JACK -ljack jack_client_open -check_lib GLFW -lglfw glfwInit -check_critical GLFW "Cannot find GLFW library." +check_pkgconf SDL SDL_gfx +check_critical SDL "Cannot find SDL library." check_lib CG -lCg cgCreateContext check_pkgconf XML libxml-2.0 @@ -37,7 +37,7 @@ check_lib SRC -lsamplerate src_callback_new check_lib DYNAMIC -ldl dlopen # Creates config.mk and config.h. -VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE" +VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/settings.c b/settings.c index 71b7c1be82..3bcf33db48 100644 --- a/settings.c +++ b/settings.c @@ -32,11 +32,12 @@ static void set_defaults(void) { const char *def_video = NULL; const char *def_audio = NULL; + const char *def_input = NULL; switch (VIDEO_DEFAULT_DRIVER) { case VIDEO_GL: - def_video = "glfw"; + def_video = "gl"; break; default: break; @@ -63,12 +64,23 @@ static void set_defaults(void) break; } + switch (INPUT_DEFAULT_DRIVER) + { + case INPUT_SDL: + def_input = "sdl"; + break; + default: + break; + } + // No input atm ... It is in the GLFW driver. if (def_video) strncpy(g_settings.video.driver, def_video, sizeof(g_settings.video.driver) - 1); if (def_audio) strncpy(g_settings.audio.driver, def_audio, sizeof(g_settings.audio.driver) - 1); + if (def_input) + strncpy(g_settings.input.driver, def_input, sizeof(g_settings.input.driver) - 1); g_settings.video.xscale = xscale; g_settings.video.yscale = yscale; @@ -97,7 +109,7 @@ static void set_defaults(void) g_settings.input.load_state_key = LOAD_STATE_KEY; g_settings.input.toggle_fullscreen_key = TOGGLE_FULLSCREEN; g_settings.input.axis_threshold = AXIS_THRESHOLD; - g_settings.input.exit_emulator_key = GLFW_KEY_ESC; + g_settings.input.exit_emulator_key = SDLK_ESCAPE; } void parse_config(void) @@ -261,6 +273,11 @@ void parse_config(void) strncpy(g_settings.audio.driver, tmp_str, sizeof(g_settings.audio.driver) - 1); free(tmp_str); } + if (config_get_string(conf, "input_driver", &tmp_str)) + { + strncpy(g_settings.input.driver, tmp_str, sizeof(g_settings.input.driver) - 1); + free(tmp_str); + } if (config_get_string(conf, "libsnes_path", &tmp_str)) { strncpy(g_settings.libsnes, tmp_str, sizeof(g_settings.libsnes) - 1); @@ -314,40 +331,40 @@ static const struct bind_map bind_maps[2][13] = { } }; -struct glfw_map +struct key_map { const char *str; int key; }; // Edit: Not portable to different input systems atm. Might move this map into the driver itself or something. -static const struct glfw_map glfw_map[] = { - { "left", GLFW_KEY_LEFT }, - { "right", GLFW_KEY_RIGHT }, - { "up", GLFW_KEY_UP }, - { "down", GLFW_KEY_DOWN }, - { "enter", GLFW_KEY_ENTER }, - { "tab", GLFW_KEY_TAB }, - { "insert", GLFW_KEY_INSERT }, - { "del", GLFW_KEY_DEL }, - { "rshift", GLFW_KEY_RSHIFT }, - { "shift", GLFW_KEY_LSHIFT }, - { "ctrl", GLFW_KEY_LCTRL }, - { "alt", GLFW_KEY_LALT }, - { "space", GLFW_KEY_SPACE }, - { "escape", GLFW_KEY_ESC }, - { "f1", GLFW_KEY_F1 }, - { "f2", GLFW_KEY_F2 }, - { "f3", GLFW_KEY_F3 }, - { "f4", GLFW_KEY_F4 }, - { "f5", GLFW_KEY_F5 }, - { "f6", GLFW_KEY_F6 }, - { "f7", GLFW_KEY_F7 }, - { "f8", GLFW_KEY_F8 }, - { "f9", GLFW_KEY_F9 }, - { "f10", GLFW_KEY_F10 }, - { "f11", GLFW_KEY_F11 }, - { "f12", GLFW_KEY_F12 }, +static const struct key_map sdlk_map[] = { + { "left", SDLK_LEFT }, + { "right", SDLK_RIGHT }, + { "up", SDLK_UP }, + { "down", SDLK_DOWN }, + { "enter", SDLK_RETURN }, + { "tab", SDLK_TAB }, + { "insert", SDLK_INSERT }, + { "del", SDLK_DELETE }, + { "rshift", SDLK_RSHIFT }, + { "shift", SDLK_LSHIFT }, + { "ctrl", SDLK_LCTRL }, + { "alt", SDLK_LALT }, + { "space", SDLK_SPACE }, + { "escape", SDLK_ESCAPE }, + { "f1", SDLK_F1 }, + { "f2", SDLK_F2 }, + { "f3", SDLK_F3 }, + { "f4", SDLK_F4 }, + { "f5", SDLK_F5 }, + { "f6", SDLK_F6 }, + { "f7", SDLK_F7 }, + { "f8", SDLK_F8 }, + { "f9", SDLK_F9 }, + { "f10", SDLK_F10 }, + { "f11", SDLK_F11 }, + { "f12", SDLK_F12 }, }; static struct snes_keybind *find_snes_bind(unsigned port, int id) @@ -362,23 +379,23 @@ static struct snes_keybind *find_snes_bind(unsigned port, int id) return NULL; } -static int find_glfw_bind(const char *str) +static int find_sdlk_bind(const char *str) { - for (int i = 0; i < sizeof(glfw_map)/sizeof(struct glfw_map); i++) + for (int i = 0; i < sizeof(sdlk_map)/sizeof(struct key_map); i++) { - if (strcasecmp(glfw_map[i].str, str) == 0) - return glfw_map[i].key; + if (strcasecmp(sdlk_map[i].str, str) == 0) + return sdlk_map[i].key; } return -1; } -static int find_glfw_key(const char *str) +static int find_sdlk_key(const char *str) { // If the bind is a normal key-press ... if (strlen(str) == 1 && isalpha(*str)) - return toupper(*str); + return (int)SDLK_a + (tolower(*str) - (int)'a'); else // Check if we have a special mapping for it. - return find_glfw_bind(str); + return find_sdlk_bind(str); } static void read_keybinds(config_file_t *conf) @@ -397,7 +414,7 @@ static void read_keybinds(config_file_t *conf) if (bind_maps[j][i].key && config_get_string(conf, bind_maps[j][i].key, &tmp_key)) { - int key = find_glfw_key(tmp_key); + int key = find_sdlk_key(tmp_key); if (key >= 0) bind->key = key; @@ -432,28 +449,28 @@ static void read_keybinds(config_file_t *conf) char *tmp_str; if (config_get_string(conf, "input_toggle_fullscreen", &tmp_str)) { - int key = find_glfw_key(tmp_str); + int key = find_sdlk_key(tmp_str); if (key >= 0) g_settings.input.toggle_fullscreen_key = key; free(tmp_str); } if (config_get_string(conf, "input_save_state", &tmp_str)) { - int key = find_glfw_key(tmp_str); + int key = find_sdlk_key(tmp_str); if (key >= 0) g_settings.input.save_state_key = key; free(tmp_str); } if (config_get_string(conf, "input_load_state", &tmp_str)) { - int key = find_glfw_key(tmp_str); + int key = find_sdlk_key(tmp_str); if (key >= 0) g_settings.input.load_state_key = key; free(tmp_str); } if (config_get_string(conf, "input_exit_emulator", &tmp_str)) { - int key = find_glfw_key(tmp_str); + int key = find_sdlk_key(tmp_str); if (key >= 0) g_settings.input.exit_emulator_key = key; free(tmp_str); diff --git a/ssnes.c b/ssnes.c index eae767bddd..75ffc0da2d 100644 --- a/ssnes.c +++ b/ssnes.c @@ -17,7 +17,6 @@ #include -#include #include #include #include @@ -421,20 +420,18 @@ int main(int argc, char *argv[]) ///// TODO: Modular friendly!!! for(;;) { - bool quitting = glfwGetKey(g_settings.input.exit_emulator_key) || !glfwGetWindowParam(GLFW_OPENED); - - if ( quitting ) + if (driver.input->key_pressed(driver.input_data, g_settings.input.exit_emulator_key)) break; - if ( glfwGetKey( g_settings.input.save_state_key )) + if (driver.input->key_pressed(driver.input_data, g_settings.input.save_state_key)) { write_file(statefile_name, serial_data, serial_size); } - else if ( glfwGetKey( g_settings.input.load_state_key ) ) + else if (driver.input->key_pressed(driver.input_data, g_settings.input.load_state_key)) load_state(statefile_name, serial_data, serial_size); - else if ( glfwGetKey( g_settings.input.toggle_fullscreen_key ) ) + else if (driver.input->key_pressed(driver.input_data, g_settings.input.toggle_fullscreen_key)) { g_settings.video.fullscreen = !g_settings.video.fullscreen; uninit_drivers(); From eca7a59dd1757ff31ded9397e75140a8f6c6b775 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 18:38:02 +0100 Subject: [PATCH 19/40] change an #ifdef to HAVE_SDL --- driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver.c b/driver.c index 656f869006..034605a0cc 100644 --- a/driver.c +++ b/driver.c @@ -45,7 +45,7 @@ static const audio_driver_t *audio_drivers[] = { }; static const video_driver_t *video_drivers[] = { -#ifdef HAVE_GLFW +#ifdef HAVE_SDL &video_gl, #endif }; From 86e080476a3bcf996e6ce9ba0b6bdae0d8ba02cf Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 20:01:32 +0100 Subject: [PATCH 20/40] Fixes here and there... --- config.def.h | 2 ++ driver.c | 12 +++---- driver.h | 6 ++-- general.h | 1 + gfx/gl.c | 91 ++++++++++++++++++++++++++++++++++++----------- input/sdl.c | 48 ++++++++++++++----------- input/sdl_input.h | 36 +++++++++++++++++++ settings.c | 4 +++ ssnes.c | 4 +-- ssnes.cfg | 8 ++++- 10 files changed, 158 insertions(+), 54 deletions(-) create mode 100644 input/sdl_input.h diff --git a/config.def.h b/config.def.h index a30f08adc0..6c0376f444 100644 --- a/config.def.h +++ b/config.def.h @@ -69,6 +69,8 @@ static const bool video_smooth = true; // On resize and fullscreen, rendering area will stay 4:3 static const bool force_aspect = true; +#define SNES_ASPECT_RATIO (4.0/3) + //////////////// // Audio //////////////// diff --git a/driver.c b/driver.c index 034605a0cc..8c73f53eef 100644 --- a/driver.c +++ b/driver.c @@ -103,7 +103,7 @@ static void find_input_driver(void) } } SSNES_ERR("Couldn't find any input driver named \"%s\"\n", g_settings.input.driver); - fprintf(stderr, "Available video drivers are:\n"); + fprintf(stderr, "Available input drivers are:\n"); for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++) fprintf(stderr, "\t%s\n", video_drivers[i]->ident); @@ -194,7 +194,7 @@ void init_video_input(void) }; const input_driver_t *tmp = driver.input; - driver.video_data = driver.video->init(&video, &driver.input); + driver.video_data = driver.video->init(&video, &driver.input, &driver.input_data); if ( driver.video_data == NULL ) { @@ -202,12 +202,8 @@ void init_video_input(void) exit(1); } - // Video driver also provides an input driver. - if ( driver.input != NULL ) - { - driver.input_data = driver.video_data; - } - else // We use our configured input driver. + // Video driver didn't provide an input driver so we use configured one. + if (driver.input == NULL) { driver.input = tmp; if (driver.input != NULL) diff --git a/driver.h b/driver.h index 9d8a0f55b7..83d6c092fd 100644 --- a/driver.h +++ b/driver.h @@ -72,10 +72,12 @@ typedef struct input_driver typedef struct video_driver { - void* (*init)(video_info_t *video, const input_driver_t **input); - // Should the video driver act as an input driver as well? :) + void* (*init)(video_info_t *video, const input_driver_t **input, void **input_data); + // Should the video driver act as an input driver as well? :) The video init might preinitialize an input driver to override the settings in case the video driver relies on input driver for event handling, e.g. bool (*frame)(void* data, const uint16_t* frame, int width, int height, int pitch); void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding. + // Is the window still active? + bool (*alive)(void *data); void (*free)(void* data); const char *ident; } video_driver_t; diff --git a/general.h b/general.h index 20e9b5a7f9..1ad972dee9 100644 --- a/general.h +++ b/general.h @@ -42,6 +42,7 @@ struct settings bool vsync; bool smooth; bool force_aspect; + float aspect_ratio; char cg_shader_path[256]; char bsnes_shader_path[256]; unsigned filter; diff --git a/gfx/gl.c b/gfx/gl.c index 44dd7f7346..70a9f83e15 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -29,6 +29,7 @@ #define NO_SDL_GLEXT #include #include +#include "input/sdl_input.h" #define GL_GLEXT_PROTOTYPES #include @@ -63,6 +64,13 @@ typedef struct gl GLuint texture; GLuint tex_filter; + bool should_resize; + bool quitting; + + unsigned win_width; + unsigned win_height; + unsigned vp_width; + unsigned vp_height; unsigned last_width; unsigned last_height; unsigned tex_w, tex_h; @@ -123,39 +131,37 @@ static inline void gl_shader_set_params(unsigned width, unsigned height, #endif } -#define SNES_ASPECT_RATIO (4.0/3) - -static void set_viewport(int width, int height) +static void set_viewport(gl_t *gl) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - GLuint out_width = width, out_height = height; + GLuint out_width = gl->win_width, out_height = gl->win_height; if ( keep_aspect ) { - float desired_aspect = SNES_ASPECT_RATIO; - float device_aspect = (float)width / height; + float desired_aspect = g_settings.video.aspect_ratio; + float device_aspect = (float)gl->win_width / gl->win_height; // If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff), // assume they are actually equal. if ( (int)(device_aspect*1000) > (int)(desired_aspect*1000) ) { float delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5; - glViewport(width * (0.5 - delta), 0, 2.0 * width * delta, height); - out_width = (int)(2.0 * width * delta); + glViewport(gl->win_width * (0.5 - delta), 0, 2.0 * gl->win_width * delta, gl->win_height); + out_width = (int)(2.0 * gl->win_width * delta); } else if ( (int)(device_aspect*1000) < (int)(desired_aspect*1000) ) { float delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5; - glViewport(0, height * (0.5 - delta), width, 2.0 * height * delta); - out_height = (int)(2.0 * height * delta); + glViewport(0, gl->win_height * (0.5 - delta), gl->win_width, 2.0 * gl->win_height * delta); + out_height = (int)(2.0 * gl->win_height * delta); } else - glViewport(0, 0, width, height); + glViewport(0, 0, gl->win_width, gl->win_height); } else - glViewport(0, 0, width, height); + glViewport(0, 0, gl->win_width, gl->win_height); glOrtho(0, 1, 0, 1, -1, 1); glMatrixMode(GL_MODELVIEW); @@ -163,8 +169,8 @@ static void set_viewport(int width, int height) gl_shader_set_proj_matrix(); - gl_width = out_width; - gl_height = out_height; + gl->vp_width = out_width; + gl->vp_height = out_height; } static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, int frames) @@ -202,6 +208,13 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i { gl_t *gl = data; + if (gl->should_resize) + { + gl->should_resize = false; + SDL_SetVideoMode(gl->win_width, gl->win_height, 32, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0)); + set_viewport(gl); + } + glClear(GL_COLOR_BUFFER_BIT); gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl_width, gl_height); @@ -235,6 +248,7 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i glDrawArrays(GL_QUADS, 0, 4); show_fps(); + glFlush(); SDL_GL_SwapBuffers(); return true; @@ -256,12 +270,13 @@ static void gl_set_nonblock_state(void *data, bool state) gl_t *gl = data; if (gl->vsync) { + SSNES_LOG("GL VSync => %s\n", state ? "off" : "on"); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, state ? 0 : 1); - //SDL_SetVideoMode(gl->width, gl->height, 32, SDL_OPENGL | (video->fullscreen ? SDL_FULLSCREEN : 0)); + //SDL_SetVideoMode(gl->win_width, gl->win_height, 32, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0)); } } -static void* gl_init(video_info_t *video, const input_driver_t **input) +static void* gl_init(video_info_t *video, const input_driver_t **input, void **input_data) { if (SDL_Init(SDL_INIT_VIDEO) < 0) return NULL; @@ -269,20 +284,36 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0); - if (!SDL_SetVideoMode(video->width, video->height, 32, SDL_OPENGL | (video->fullscreen ? SDL_FULLSCREEN : 0))) + if (!SDL_SetVideoMode(video->width, video->height, 32, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0))) return NULL; + + int attr = 0; + SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr); + if (attr <= 0 && video->vsync) + SSNES_WARN("GL VSync has not been enabled!\n"); + attr = 0; + SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &attr); + if (attr <= 0) + SSNES_WARN("GL double buffer has not been enabled!\n"); + + // Remove that ugly mouse :D + SDL_ShowCursor(SDL_DISABLE); gl_t *gl = calloc(1, sizeof(gl_t)); - if ( gl == NULL ) + if (!gl) return NULL; + gl->win_width = video->width; + gl->win_height = video->height; + gl->vsync = video->vsync; + keep_aspect = video->force_aspect; if ( video->smooth ) gl->tex_filter = GL_LINEAR; else gl->tex_filter = GL_NEAREST; - set_viewport(video->width, video->height); + set_viewport(gl); glEnable(GL_TEXTURE_2D); glDisable(GL_DITHER); @@ -323,13 +354,33 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) gl_shader_init(); - *input = NULL; + // Hook up SDL input driver to get SDL_QUIT events and RESIZE. + sdl_input_t *sdl_input = input_sdl.init(); + if (sdl_input) + { + sdl_input->quitting = &gl->quitting; + sdl_input->should_resize = &gl->should_resize; + sdl_input->new_width = &gl->win_width; + sdl_input->new_height = &gl->win_height; + *input = &input_sdl; + *input_data = sdl_input; + } + else + *input = NULL; + return gl; } +static bool gl_alive(void *data) +{ + gl_t *gl = data; + return !gl->quitting; +} + const video_driver_t video_gl = { .init = gl_init, .frame = gl_frame, + .alive = gl_alive, .set_nonblock_state = gl_set_nonblock_state, .free = gl_free, .ident = "gl" diff --git a/input/sdl.c b/input/sdl.c index 68c4c058ac..b4300fade7 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -23,15 +23,7 @@ #include #include #include - -typedef struct sdl_input -{ - bool quitting; - SDL_Joystick *joysticks[2]; - unsigned num_axes[2]; - unsigned num_buttons[2]; - unsigned num_joysticks; -} sdl_input_t; +#include "sdl_input.h" static void* sdl_input_init(void) { @@ -43,6 +35,8 @@ static void* sdl_input_init(void) return NULL; sdl->num_joysticks = SDL_NumJoysticks(); + if (sdl->num_joysticks > 2) + sdl->num_joysticks = 2; for (unsigned i = 0; i < sdl->num_joysticks; i++) { sdl->joysticks[i] = SDL_JoystickOpen(i); @@ -64,11 +58,6 @@ static void* sdl_input_init(void) static bool sdl_key_pressed(void *data, int key) { - // Check to see if we have to exit. - sdl_input_t *sdl = data; - if (sdl->quitting && key == g_settings.input.exit_emulator_key) - return true; - int num_keys; Uint8 *keymap = SDL_GetKeyState(&num_keys); @@ -98,7 +87,7 @@ static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_key } if (AXIS_POS_GET(key->joyaxis) < sdl->num_axes[port_num]) { - Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(key->joyaxis)); + Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(key->joyaxis)); float scaled = (float)val / 0x8000; if (scaled > g_settings.input.axis_threshold) return true; @@ -136,7 +125,7 @@ static void sdl_input_free(void *data) { sdl_input_t *sdl = data; for (int i = 0; i < sdl->num_joysticks; i++) - SDL_JoystickClose(i); + SDL_JoystickClose(sdl->joysticks[i]); free(data); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); @@ -148,14 +137,31 @@ static void sdl_input_poll(void *data) SDL_PumpEvents(); SDL_Event event; - // Search for SDL_QUIT + sdl_input_t *sdl = data; + // Search for events... while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) + switch (event.type) { - sdl_input_t *sdl = data; - sdl->quitting = true; - break; + case SDL_QUIT: + if (sdl->quitting) + { + *sdl->quitting = true; + return; + } + break; + + case SDL_VIDEORESIZE: + if (sdl->should_resize) + { + *sdl->new_width = event.resize.w; + *sdl->new_height = event.resize.h; + *sdl->should_resize = true; + } + break; + + default: + break; } } } diff --git a/input/sdl_input.h b/input/sdl_input.h new file mode 100644 index 0000000000..d934a8712d --- /dev/null +++ b/input/sdl_input.h @@ -0,0 +1,36 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + +#ifndef __SSNES_SDL_INPUT_H +#define __SSNES_SDL_INPUT_H + +#include +typedef struct sdl_input +{ + SDL_Joystick *joysticks[2]; + unsigned num_axes[2]; + unsigned num_buttons[2]; + unsigned num_joysticks; + + // A video driver could pre-init with the SDL driver and have it handle resizing events... + bool *quitting; + bool *should_resize; + unsigned *new_width; + unsigned *new_height; +} sdl_input_t; + +#endif diff --git a/settings.c b/settings.c index 3bcf33db48..d0916fcda2 100644 --- a/settings.c +++ b/settings.c @@ -90,6 +90,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.aspect_ratio = SNES_ASPECT_RATIO; g_settings.audio.enable = audio_enable; g_settings.audio.out_rate = out_rate; @@ -183,6 +184,9 @@ void parse_config(void) if (config_get_bool(conf, "video_force_aspect", &tmp_bool)) g_settings.video.force_aspect = tmp_bool; + if (config_get_double(conf, "video_aspect_ratio", &tmp_double)) + g_settings.video.aspect_ratio = tmp_double; + if (config_get_string(conf, "video_cg_shader", &tmp_str)) { strncpy(g_settings.video.cg_shader_path, tmp_str, sizeof(g_settings.video.cg_shader_path) - 1); diff --git a/ssnes.c b/ssnes.c index 75ffc0da2d..9b0bc1d9d9 100644 --- a/ssnes.c +++ b/ssnes.c @@ -417,10 +417,10 @@ int main(int argc, char *argv[]) } #endif - ///// TODO: Modular friendly!!! for(;;) { - if (driver.input->key_pressed(driver.input_data, g_settings.input.exit_emulator_key)) + if (driver.input->key_pressed(driver.input_data, g_settings.input.exit_emulator_key) || + !driver.video->alive(driver.video_data)) break; if (driver.input->key_pressed(driver.input_data, g_settings.input.save_state_key)) diff --git a/ssnes.cfg b/ssnes.cfg index 894e7379f6..2d38075cd4 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -22,9 +22,12 @@ # Smoothens picture with bilinear filtering. Should be disabled if using Cg shaders. # video_smooth = true -# Forces rendering area to stay 4:3. +# Forces rendering area to stay equal to SNES aspect ratio 4:3 or as defined in video_aspect_ratio. # video_force_aspect = true +# A floating point value for video aspect ratio (width / height) +# video_aspect_ratio = 1.333 + # Path to Cg shader. If enabled # video_cg_shader = "/path/to/cg/shader.cg" @@ -65,6 +68,9 @@ ### Input +# Input driver. Depending on video driver, it might force a different input driver. +# input_driver = sdl + # Defines axis threshold. Possible values are [0.0, 1.0] # input_axis_threshold = 0.6 From ce3bdc9aeceb151f373d84ee73913348dbefd632 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 20:06:48 +0100 Subject: [PATCH 21/40] Add a warning when vsync cannot be changed... --- gfx/gl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gfx/gl.c b/gfx/gl.c index 70a9f83e15..b65e9f2e52 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -272,6 +272,10 @@ static void gl_set_nonblock_state(void *data, bool state) { SSNES_LOG("GL VSync => %s\n", state ? "off" : "on"); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, state ? 0 : 1); + int attr = 0; + SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr); + if ((bool)attr == state) + SSNES_WARN("Couldn't change VSync settings. Fast forwarding might not work.\n"); //SDL_SetVideoMode(gl->win_width, gl->win_height, 32, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0)); } } From 948de957993a7bf5ace5bb3c283e8e3cd589afd0 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 20:12:25 +0100 Subject: [PATCH 22/40] Update build system --- Makefile | 2 +- README.md | 12 +++++++----- qb/config.libs.sh | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 30cae318a8..1cafde2d08 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ TARGET = ssnes OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o -LIBS = -lsamplerate -lSDL +LIBS = -lsamplerate DEFINES = ifeq ($(HAVE_RSOUND), 1) diff --git a/README.md b/README.md index a6ce0b1d0c..57a4be2f72 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,13 @@ It is used through command-line. SSNES requires these libraries to build: - [libsnes](http://byuu.org/bsnes/) - - GLFW + - SDL - libsamplerate SSNES can utilize these libraries if enabled: - nvidia-cg-toolkit + - libxml2 (bSNES XML shaders) SSNES needs one of these audio driver libraries: @@ -67,13 +68,14 @@ Do note that these two options are mutually exclusive. -# Filters and Cg shader support +# Filters, bSNES XML shaders and Cg shader support This is not strictly not necessary for an emulator, but it can be enabled if desired. -For best performance, Cg shaders are recommended as they do not eat up valuable CPU time. -Cg shaders are compiled at run-time, and shaders could be dropped in. +For best performance, Cg shaders or bSNES XML shaders are recommended as they do not eat up valuable CPU time (assuming your GPU can handle the shaders). +Cg shaders and XML shaders (GLSL) are compiled at run-time. All shaders share a common interface to pass some essential arguments such as texture size and viewport size. (Common for pixel art scalers) Some Cg shaders are included in hqflt/cg/ and could be used as an example. +bSNES XML shaders can be found on various places on the net. Best place to start looking are the bSNES forums. -While these shaders are Cg, they closely resemble the GLSL shaders found in bSNES shader pack, so porting them is trivial. +The Cg shaders closely resemble the GLSL shaders found in bSNES shader pack, so porting them is trivial if desired. diff --git a/qb/config.libs.sh b/qb/config.libs.sh index a5e0b281f4..b4650c7847 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -16,7 +16,7 @@ check_lib RSOUND -lrsound rsd_init check_lib ROAR -lroar roar_vs_new check_lib JACK -ljack jack_client_open -check_pkgconf SDL SDL_gfx +check_pkgconf SDL sdl check_critical SDL "Cannot find SDL library." check_lib CG -lCg cgCreateContext From d37bd214e647f9ce911c9e80f695b46f759128b5 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 20:29:04 +0100 Subject: [PATCH 23/40] Update build system to check for minimum SDL version. --- qb/config.libs.sh | 2 +- qb/qb.libs.sh | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/qb/config.libs.sh b/qb/config.libs.sh index b4650c7847..d2f9f6cef1 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -16,7 +16,7 @@ check_lib RSOUND -lrsound rsd_init check_lib ROAR -lroar roar_vs_new check_lib JACK -ljack jack_client_open -check_pkgconf SDL sdl +check_pkgconf SDL sdl 1.2.10 check_critical SDL "Cannot find SDL library." check_lib CG -lCg cgCreateContext diff --git a/qb/qb.libs.sh b/qb/qb.libs.sh index ab8585fc83..579c91a44d 100644 --- a/qb/qb.libs.sh +++ b/qb/qb.libs.sh @@ -106,14 +106,15 @@ check_pkgconf() eval tmpval=\$$tmpval [ "$tmpval" = "no" ] && return 0 - echo -n "Checking presence of package $2 ... " + echo -n "Checking presence of package $2" eval HAVE_$1=no eval $1_CFLAGS="" eval $1_LIBS="" answer=no minver=0.0 - [ ! -z $3 ] && minver=$3 - pkg-config --atleast-version=$minver --exists "$2" && eval HAVE_$1=yes && eval $1_CFLAGS='"`pkg-config $2 --cflags`"' && eval $1_LIBS='"`pkg-config $2 --libs`"' && answer=yes + [ ! -z $3 ] && minver=$3 && echo -n " with minimum version $minver" + echo -n " ... " + pkg-config --atleast-version=$minver "$2" && eval HAVE_$1=yes && eval $1_CFLAGS='"`pkg-config $2 --cflags`"' && eval $1_LIBS='"`pkg-config $2 --libs`"' && answer=yes echo $answer PKG_CONF_USED="$PKG_CONF_USED $1" From 064ab9e7bdb9f14da356526bb922994b625e0626 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 6 Jan 2011 23:15:49 +0100 Subject: [PATCH 24/40] Use glX/wgl VSync toggling... Detect default audio driver. --- config.def.h | 15 +++++++++++++++ gfx/gl.c | 24 ++++++++++++++++++------ gfx/shader_glsl.c | 1 - settings.c | 7 +++++-- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/config.def.h b/config.def.h index 6c0376f444..b2bb457c9c 100644 --- a/config.def.h +++ b/config.def.h @@ -26,6 +26,7 @@ #include #include "libsnes.hpp" #include "driver.h" +#include "config.h" #include @@ -43,7 +44,21 @@ //////////////////////// #define VIDEO_DEFAULT_DRIVER VIDEO_GL + +#if HAVE_ALSA #define AUDIO_DEFAULT_DRIVER AUDIO_ALSA +#elif HAVE_OSS +#define AUDIO_DEFAULT_DRIVER AUDIO_OSS +#elif HAVE_JACK +#define AUDIO_DEFAULT_DRIVER AUDIO_JACK +#elif HAVE_RSOUND +#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND +#elif HAVE_ROAR +#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR +#elif HAVE_AL +#define AUDIO_DEFAULT_DRIVER AUDIO_AL +#endif + #define INPUT_DEFAULT_DRIVER INPUT_SDL diff --git a/gfx/gl.c b/gfx/gl.c index b65e9f2e52..49f7773008 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -34,6 +34,10 @@ #define GL_GLEXT_PROTOTYPES #include +#ifndef _WIN32 +#include +#endif + #ifdef HAVE_CG #include "shader_cg.h" #endif @@ -271,12 +275,20 @@ static void gl_set_nonblock_state(void *data, bool state) if (gl->vsync) { SSNES_LOG("GL VSync => %s\n", state ? "off" : "on"); - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, state ? 0 : 1); - int attr = 0; - SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr); - if ((bool)attr == state) - SSNES_WARN("Couldn't change VSync settings. Fast forwarding might not work.\n"); - //SDL_SetVideoMode(gl->win_width, gl->win_height, 32, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0)); +#ifdef _WIN32 + static BOOL (APIENTRY wgl_swap_interval*)(int) = NULL; + if (!wgl_swap_interval) + SSNES_WARN("SDL VSync toggling seems to be broken, attempting to use WGL VSync call directly instead.\n"); + if (!wgl_swap_interval) wgl_swap_interval = (BOOL (APIENTRY*)(int)) wglGetProcAddress("wglSwapIntervalEXT"); + if (wgl_swap_interval) wgl_swap_interval(state ? 0 : 1); +#else + static int (*glx_swap_interval)(int) = NULL; + if (!glx_swap_interval) + SSNES_WARN("SDL VSync toggling seems to be broken, attempting to use GLX VSync call directly instead.\n"); + if (!glx_swap_interval) glx_swap_interval = (int (*)(int))glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI"); + if (!glx_swap_interval) glx_swap_interval = (int (*)(int))glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalMESA"); + if (glx_swap_interval) glx_swap_interval(state ? 0 : 1); +#endif } } diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 4a5af0b8ef..b6b120472f 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -25,7 +25,6 @@ #define NO_SDL_GLEXT #include -//#include #include #include #include diff --git a/settings.c b/settings.c index d0916fcda2..654b342bff 100644 --- a/settings.c +++ b/settings.c @@ -73,8 +73,6 @@ static void set_defaults(void) break; } - // No input atm ... It is in the GLFW driver. - if (def_video) strncpy(g_settings.video.driver, def_video, sizeof(g_settings.video.driver) - 1); if (def_audio) @@ -129,6 +127,10 @@ void parse_config(void) } else { +#ifdef _WIN32 + // Just do something for now. + conf = config_file_new("ssnes.cfg"); +#else const char *xdg = getenv("XDG_CONFIG_HOME"); const char *home = getenv("HOME"); if (xdg) @@ -148,6 +150,7 @@ void parse_config(void) // Try this as a last chance... if (!conf) conf = config_file_new("/etc/ssnes.cfg"); +#endif } set_defaults(); From 270292db2cd0b41d3e20b9aa509904880b3e8f92 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 11:09:56 +0100 Subject: [PATCH 25/40] Check libsamplerate as critical and use HAVE_SRC --- Makefile | 8 +++++++- config.def.h | 3 +++ general.h | 4 +++- qb/config.libs.sh | 5 +++-- ssnes.c | 4 +++- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 1cafde2d08..97522ce10c 100644 --- a/Makefile +++ b/Makefile @@ -4,13 +4,19 @@ TARGET = ssnes OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o -LIBS = -lsamplerate +LIBS = DEFINES = +ifeq ($(HAVE_SRC), 1) + LIBS += $(SRC_LIBS) + DEFINES += $(SRC_CFLAGS) +endif + ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o LIBS += -lrsound endif + ifeq ($(HAVE_OSS), 1) OBJ += audio/oss.o endif diff --git a/config.def.h b/config.def.h index b2bb457c9c..1453430a5b 100644 --- a/config.def.h +++ b/config.def.h @@ -27,7 +27,10 @@ #include "libsnes.hpp" #include "driver.h" #include "config.h" + +#ifdef HAVE_SRC #include +#endif ///////////////// Drivers diff --git a/general.h b/general.h index 1ad972dee9..9488c56327 100644 --- a/general.h +++ b/general.h @@ -20,11 +20,13 @@ #define __SSNES_GENERAL_H #include -#include #include "driver.h" #include #include "record/ffemu.h" #include "config.h" +#ifdef HAVE_SRC +#include +#endif #define MAX_PLAYERS 2 diff --git a/qb/config.libs.sh b/qb/config.libs.sh index d2f9f6cef1..9e910d9cc2 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -32,12 +32,13 @@ if [ $HAVE_FFMPEG != no ]; then ( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVCORE = no ] || [ $HAVE_AVUTIL = no ] || [ $HAVE_SWSCALE = no ] ) && HAVE_FFMPEG=no ) || HAVE_FFMPEG=yes fi -check_lib SRC -lsamplerate src_callback_new +check_pkgconf SRC samplerate +check_critical SRC "Cannot find libsamplerate." check_lib DYNAMIC -ldl dlopen # Creates config.mk and config.h. -VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE" +VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/ssnes.c b/ssnes.c index 9b0bc1d9d9..2043640e5f 100644 --- a/ssnes.c +++ b/ssnes.c @@ -17,7 +17,6 @@ #include -#include #include #include #include @@ -30,6 +29,9 @@ #include "dynamic.h" #include "record/ffemu.h" #include +#ifdef HAVE_SRC +#include +#endif struct global g_extern = { .video_active = true, From c5d1f7d60b2fcb8005f136b892fecfb6d2137470 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 11:18:41 +0100 Subject: [PATCH 26/40] Change build rules to use LDFLAGS when linking. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 97522ce10c..511e40341d 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ config.mk: configure qb/* @exit 1 ssnes: $(OBJ) - $(CXX) -o $@ $(OBJ) $(LIBS) $(CFLAGS) + $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) %.o: %.c config.h config.mk $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< From 622a1a9c70d1bdf2094839d4902a0bb55e193183 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 11:45:53 +0100 Subject: [PATCH 27/40] Fix segfault when ssnes.cfg is not found in $XDG_CONFIG_HOME. --- settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.c b/settings.c index 654b342bff..b0df409ba7 100644 --- a/settings.c +++ b/settings.c @@ -143,7 +143,7 @@ void parse_config(void) else if (home) { char conf_path[strlen(home) + strlen("/.ssnesrc ")]; - strcpy(conf_path, xdg); + strcpy(conf_path, home); strcat(conf_path, "/.ssnesrc"); conf = config_file_new(conf_path); } From ff78f74393035b68f032262a89318745fc99b43f Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 12:50:14 +0100 Subject: [PATCH 28/40] Should fix ssize_t issue on some platforms. --- driver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/driver.h b/driver.h index 83d6c092fd..ed80623f4f 100644 --- a/driver.h +++ b/driver.h @@ -19,6 +19,7 @@ #ifndef __DRIVER__H #define __DRIVER__H +#include #include #include #include From a5bbc68a8442a35cbcc9139b69ed7f8f51a58923 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 15:50:16 +0100 Subject: [PATCH 29/40] Add SDL audio driver. --- Makefile | 2 +- audio/buffer.c | 121 +++++++++++++++++++++++++++++++ audio/buffer.h | 36 ++++++++++ audio/sdl.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++ config.def.h | 3 + driver.c | 3 + driver.h | 1 + 7 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 audio/buffer.c create mode 100644 audio/buffer.h create mode 100644 audio/sdl.c diff --git a/Makefile b/Makefile index 511e40341d..31cfd447da 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ ifeq ($(HAVE_JACK),1) endif ifeq ($(HAVE_SDL), 1) - OBJ += gfx/gl.o input/sdl.o + OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o LIBS += $(SDL_LIBS) -lGL DEFINES += $(SDL_CFLAGS) endif diff --git a/audio/buffer.c b/audio/buffer.c new file mode 100644 index 0000000000..f3df435acb --- /dev/null +++ b/audio/buffer.c @@ -0,0 +1,121 @@ +/* RSound - A PCM audio client/server + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * RSound 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. + * + * RSound 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 RSound. + * If not, see . + */ + +#include "buffer.h" + +struct rsound_fifo_buffer +{ + char *buffer; + size_t bufsize; + size_t first; + size_t end; +}; + +rsound_fifo_buffer_t* rsnd_fifo_new(size_t size) +{ + rsound_fifo_buffer_t *buf = calloc(1, sizeof(*buf)); + if (buf == NULL) + return NULL; + + buf->buffer = calloc(1, size + 1); + if (buf->buffer == NULL) + { + free(buf); + return NULL; + } + buf->bufsize = size + 1; + + return buf; +} + +void rsnd_fifo_free(rsound_fifo_buffer_t* buffer) +{ + assert(buffer); + assert(buffer->buffer); + + free(buffer->buffer); + free(buffer); +} + +size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer) +{ + assert(buffer); + assert(buffer->buffer); + + size_t first = buffer->first; + size_t end = buffer->end; + if (end < first) + end += buffer->bufsize; + return end - first; +} + +size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer) +{ + assert(buffer); + assert(buffer->buffer); + + size_t first = buffer->first; + size_t end = buffer->end; + if (end < first) + end += buffer->bufsize; + + return (buffer->bufsize - 1) - (end - first); +} + +void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size) +{ + assert(buffer); + assert(buffer->buffer); + assert(in_buf); + assert(rsnd_fifo_write_avail(buffer) >= size); + + size_t first_write = size; + size_t rest_write = 0; + if (buffer->end + size > buffer->bufsize) + { + first_write = buffer->bufsize - buffer->end; + rest_write = size - first_write; + } + + memcpy(buffer->buffer + buffer->end, in_buf, first_write); + if (rest_write > 0) + memcpy(buffer->buffer, (const char*)in_buf + first_write, rest_write); + + buffer->end = (buffer->end + size) % buffer->bufsize; +} + + +void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size) +{ + assert(buffer); + assert(buffer->buffer); + assert(in_buf); + assert(rsnd_fifo_read_avail(buffer) >= size); + + size_t first_read = size; + size_t rest_read = 0; + if (buffer->first + size > buffer->bufsize) + { + first_read = buffer->bufsize - buffer->first; + rest_read = size - first_read; + } + + memcpy(in_buf, (const char*)buffer->buffer + buffer->first, first_read); + if (rest_read > 0) + memcpy((char*)in_buf + first_read, buffer->buffer, rest_read); + + buffer->first = (buffer->first + size) % buffer->bufsize; +} + diff --git a/audio/buffer.h b/audio/buffer.h new file mode 100644 index 0000000000..63678d2223 --- /dev/null +++ b/audio/buffer.h @@ -0,0 +1,36 @@ +/* RSound - A PCM audio client/server + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * RSound 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. + * + * RSound 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 RSound. + * If not, see . + */ + +#ifndef __BUFFER_H +#define __BUFFER_H + +#include +#include +#include +#include + +#ifndef RSD_FIFO_BUF_TYPEDEF +#define RSD_FIFO_BUF_TYPEDEF +typedef struct rsound_fifo_buffer rsound_fifo_buffer_t; +#endif + +rsound_fifo_buffer_t* rsnd_fifo_new(size_t size); +void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size); +void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size); +void rsnd_fifo_free(rsound_fifo_buffer_t* buffer); +size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer); +size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer); + +#endif diff --git a/audio/sdl.c b/audio/sdl.c new file mode 100644 index 0000000000..f24836f570 --- /dev/null +++ b/audio/sdl.c @@ -0,0 +1,191 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - 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 . + */ + + +#include "driver.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include "general.h" +#include "buffer.h" + +typedef struct sdl_audio +{ + bool nonblock; + + SDL_mutex *lock; + SDL_cond *cond; + rsound_fifo_buffer_t *buffer; +} sdl_audio_t; + +static void sdl_audio_cb(void *data, Uint8 *stream, int len) +{ + sdl_audio_t *sdl = data; + + size_t avail = rsnd_fifo_read_avail(sdl->buffer); + size_t write_size = len > avail ? avail : len; + rsnd_fifo_read(sdl->buffer, stream, write_size); + SDL_CondSignal(sdl->cond); + + // If underrun, fill rest with silence. + memset(stream + write_size, 0, len - write_size); +} + +static void* sdl_audio_init(const char* device, int rate, int latency) +{ + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) + return NULL; + + sdl_audio_t *sdl = calloc(1, sizeof(*sdl)); + if (!sdl) + return NULL; + + SDL_AudioSpec spec = { + .freq = rate, + .format = AUDIO_S16SYS, + .channels = 2, + .samples = 512, + .callback = sdl_audio_cb, + .userdata = sdl + }; + + SDL_AudioSpec out; + + if (SDL_OpenAudio(&spec, &out) < 0) + { + SSNES_ERR("Failed to open SDL audio: %s\n", SDL_GetError()); + free(sdl); + return 0; + } + g_settings.audio.out_rate = out.freq; + + sdl->lock = SDL_CreateMutex(); + sdl->cond = SDL_CreateCond(); + + // Create a buffer twice as big as needed and prefill the buffer. + size_t bufsize = out.samples * 4 * sizeof(int16_t); + void *tmp = calloc(1, bufsize); + sdl->buffer = rsnd_fifo_new(bufsize); + if (tmp) + { + rsnd_fifo_write(sdl->buffer, tmp, bufsize); + free(tmp); + } + + SDL_PauseAudio(0); + return sdl; +} + +static ssize_t sdl_audio_write(void* data, const void* buf, size_t size) +{ + sdl_audio_t *sdl = data; + + ssize_t ret = 0; + if (sdl->nonblock) + { + SDL_LockAudio(); + size_t avail = rsnd_fifo_write_avail(sdl->buffer); + size_t write_amt = avail > size ? size : avail; + rsnd_fifo_write(sdl->buffer, buf, write_amt); + SDL_UnlockAudio(); + ret = write_amt; + } + else + { + size_t written = 0; + while (written < size) + { + SDL_LockAudio(); + size_t avail = rsnd_fifo_write_avail(sdl->buffer); + + if (avail == 0) + { + SDL_UnlockAudio(); + SDL_mutexP(sdl->lock); + SDL_CondWait(sdl->cond, sdl->lock); + SDL_mutexV(sdl->lock); + } + else + { + size_t write_amt = size - written > avail ? avail : size - written; + rsnd_fifo_write(sdl->buffer, (const char*)buf + written, write_amt); + SDL_UnlockAudio(); + written += write_amt; + } + } + ret = written; + } + + return ret; +} + +static bool sdl_audio_stop(void *data) +{ + (void)data; + SDL_PauseAudio(1); + return true; +} + +static bool sdl_audio_start(void *data) +{ + (void)data; + SDL_PauseAudio(0); + return true; +} + +static void sdl_audio_set_nonblock_state(void *data, bool state) +{ + sdl_audio_t *sdl = data; + sdl->nonblock = state; +} + +static void sdl_audio_free(void *data) +{ + SDL_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + + sdl_audio_t *sdl = data; + if (sdl) + { + rsnd_fifo_free(sdl->buffer); + SDL_DestroyMutex(sdl->lock); + SDL_DestroyCond(sdl->cond); + } + free(sdl); +} + +const audio_driver_t audio_sdl = { + .init = sdl_audio_init, + .write = sdl_audio_write, + .stop = sdl_audio_stop, + .start = sdl_audio_start, + .set_nonblock_state = sdl_audio_set_nonblock_state, + .free = sdl_audio_free, + .ident = "sdl" +}; + + + + + + diff --git a/config.def.h b/config.def.h index 1453430a5b..fea34b99a7 100644 --- a/config.def.h +++ b/config.def.h @@ -42,6 +42,7 @@ #define AUDIO_ROAR 4 #define AUDIO_AL 5 #define AUDIO_JACK 6 +#define AUDIO_SDL 8 //////////////////////// #define INPUT_SDL 7 //////////////////////// @@ -60,6 +61,8 @@ #define AUDIO_DEFAULT_DRIVER AUDIO_ROAR #elif HAVE_AL #define AUDIO_DEFAULT_DRIVER AUDIO_AL +#elif HAVE_SDL +#define AUDIO_DEFAULT_DRIVER AUDIO_SDL #endif #define INPUT_DEFAULT_DRIVER INPUT_SDL diff --git a/driver.c b/driver.c index 8c73f53eef..fafb0139d1 100644 --- a/driver.c +++ b/driver.c @@ -42,6 +42,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_JACK &audio_jack, #endif +#ifdef HAVE_SDL + &audio_sdl, +#endif }; static const video_driver_t *video_drivers[] = { diff --git a/driver.h b/driver.h index ed80623f4f..ec5f3dcca8 100644 --- a/driver.h +++ b/driver.h @@ -111,6 +111,7 @@ extern const audio_driver_t audio_alsa; extern const audio_driver_t audio_roar; extern const audio_driver_t audio_openal; extern const audio_driver_t audio_jack; +extern const audio_driver_t audio_sdl; extern const video_driver_t video_gl; extern const input_driver_t input_sdl; //////////////////////////////////////////////// From 3d20fe70cd2a07bb73b6b836dd92a907664c75cd Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 16:11:48 +0100 Subject: [PATCH 30/40] Add configurable latency (however, SDL will most likely ignore this anyways.) --- audio/sdl.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/audio/sdl.c b/audio/sdl.c index f24836f570..821e822a9d 100644 --- a/audio/sdl.c +++ b/audio/sdl.c @@ -51,8 +51,29 @@ static void sdl_audio_cb(void *data, Uint8 *stream, int len) memset(stream + write_size, 0, len - write_size); } +// Interesting hack from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +static inline uint32_t next_pow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline int find_num_frames(int rate, int latency) +{ + int frames = (rate * latency) / 1000; + // SDL only likes 2^n sized buffers. + return next_pow2(frames); +} + static void* sdl_audio_init(const char* device, int rate, int latency) { + (void)device; if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) return NULL; @@ -60,11 +81,14 @@ static void* sdl_audio_init(const char* device, int rate, int latency) if (!sdl) return NULL; + // We have to buffer up some data ourselves, so we let SDL carry approx half of the latency. + int frames = find_num_frames(rate, latency / 2); + SDL_AudioSpec spec = { .freq = rate, .format = AUDIO_S16SYS, .channels = 2, - .samples = 512, + .samples = frames, // This is in audio frames, not samples ... :( .callback = sdl_audio_cb, .userdata = sdl }; @@ -82,6 +106,8 @@ static void* sdl_audio_init(const char* device, int rate, int latency) sdl->lock = SDL_CreateMutex(); sdl->cond = SDL_CreateCond(); + SSNES_LOG("SDL audio: Requested %d ms latency, got %d ms\n", latency, (int)(out.samples * 3 * 1000 / g_settings.audio.out_rate)); + // Create a buffer twice as big as needed and prefill the buffer. size_t bufsize = out.samples * 4 * sizeof(int16_t); void *tmp = calloc(1, bufsize); From 2a4995c21f297c73a0ff703f84656b13f4218e3f Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 17:59:53 +0100 Subject: [PATCH 31/40] Starting Win32 support. --- Makefile | 2 +- Makefile.win32 | 58 ++++++++++++++++++++++++++++++++++++++++++++++ conf/config_file.c | 2 +- config.def.h | 16 ++++++++++--- driver.c | 3 +++ dynamic.c | 5 ++++ general.h | 11 +++++++-- gfx/gl.c | 5 +++- hqflt/filters.h | 2 ++ settings.c | 7 ++++++ ssnes.c | 11 +++++++-- ssnes.cfg | 12 +++++----- 12 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 Makefile.win32 diff --git a/Makefile b/Makefile index 31cfd447da..1c42120fc7 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ TARGET = ssnes OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o LIBS = -DEFINES = +DEFINES = -DHAVE_CONFIG_H ifeq ($(HAVE_SRC), 1) LIBS += $(SRC_LIBS) diff --git a/Makefile.win32 b/Makefile.win32 new file mode 100644 index 0000000000..0e717c06c6 --- /dev/null +++ b/Makefile.win32 @@ -0,0 +1,58 @@ +TARGET = ssnes.exe +OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o + +CC = gcc +CXX = g++ + +HAVE_SRC = 1 +HAVE_SDL = 1 +libsnes = -lsnes + +LIBS = +DEFINES = -I. -DHAVE_SRC -DHAVE_SDL +LDFLAGS = -L. + +SRC_LIBS = -lsamplerate-0 +SDL_LIBS = -lSDLmain -lSDL + +ifeq ($(HAVE_SRC), 1) + LIBS += $(SRC_LIBS) + DEFINES += $(SRC_CFLAGS) +endif + +ifeq ($(HAVE_SDL), 1) + OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o + LIBS += $(SDL_LIBS) -lopengl32 + DEFINES += $(SDL_CFLAGS) +endif + +LIBS += $(libsnes) + +CFLAGS = -Wall -O3 -g -std=gnu99 -I. + +all: $(TARGET) + +$(TARGET): $(OBJ) + $(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + +install: $(TARGET) + install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin + install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg + +uninstall: $(TARGET) + rm -rf $(DESTDIR)/$(PREFIX)/bin/$(TARGET) + +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 hqflt/snes_ntsc/*.o + rm -f $(TARGET) + +.PHONY: all install uninstall clean diff --git a/conf/config_file.c b/conf/config_file.c index 441256f9cd..dc945c2e6a 100644 --- a/conf/config_file.c +++ b/conf/config_file.c @@ -132,7 +132,7 @@ static void print_config(config_file_t *conf) struct entry_list *tmp = conf->entries; while (tmp != NULL) { - printf("Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value); + SSNES_LOG("Config => Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value); tmp = tmp->next; } } diff --git a/config.def.h b/config.def.h index fea34b99a7..1a0e8881b0 100644 --- a/config.def.h +++ b/config.def.h @@ -22,14 +22,24 @@ #ifndef __CONFIG_DEF_H #define __CONFIG_DEF_H -#include #include #include "libsnes.hpp" #include "driver.h" + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + +#ifdef HAVE_SDL +#include +#else +#error HAVE_SDL is not defined! +#endif #ifdef HAVE_SRC #include +#else +#error HAVE_SRC is not defined! #endif @@ -104,7 +114,7 @@ static const unsigned out_rate = 48000; // Input samplerate from libSNES. // Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled. -static const unsigned in_rate = 31950; +static const unsigned in_rate = 31980; // Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults. static const char* audio_device = NULL; @@ -127,7 +137,7 @@ static const bool audio_sync = true; // Axis threshold (between 0.0 and 1.0) // How far an axis must be tilted to result in a button press -#define AXIS_THRESHOLD 0.8 +#define AXIS_THRESHOLD 0.5 #define AXIS_NEG(x) ((uint32_t)(x << 16) | 0xFFFF) #define AXIS_POS(x) ((uint32_t)(x) | 0xFFFF0000U) diff --git a/driver.c b/driver.c index fafb0139d1..67ce93c661 100644 --- a/driver.c +++ b/driver.c @@ -21,7 +21,10 @@ #include #include #include "hqflt/filters.h" + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_ALSA diff --git a/dynamic.c b/dynamic.c index 5c8ff14ceb..54bf5a1177 100644 --- a/dynamic.c +++ b/dynamic.c @@ -18,7 +18,12 @@ #include "dynamic.h" #include "general.h" #include + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + +#include #ifdef HAVE_DYNAMIC #include diff --git a/general.h b/general.h index 9488c56327..ef1bfffbb3 100644 --- a/general.h +++ b/general.h @@ -23,7 +23,11 @@ #include "driver.h" #include #include "record/ffemu.h" + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + #ifdef HAVE_SRC #include #endif @@ -103,14 +107,17 @@ extern struct global g_extern; #define SSNES_LOG(msg, args...) do { \ if (g_extern.verbose) \ fprintf(stderr, "SSNES: " msg, ##args); \ + fflush(stderr); \ } while(0) #define SSNES_ERR(msg, args...) do { \ - fprintf(stderr, "SSNES [ERROR] :: " msg, ##args); \ + fprintf(stderr, "SSNES [ERROR] :: " msg, ##args); \ + fflush(stderr); \ } while(0) #define SSNES_WARN(msg, args...) do { \ - fprintf(stderr, "SSNES [WARN] :: " msg, ##args); \ + fprintf(stderr, "SSNES [WARN] :: " msg, ##args); \ + fflush(stderr); \ } while(0) #endif diff --git a/gfx/gl.c b/gfx/gl.c index 49f7773008..1044615bf8 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -24,7 +24,10 @@ #include #include #include "general.h" + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif #define NO_SDL_GLEXT #include @@ -276,7 +279,7 @@ static void gl_set_nonblock_state(void *data, bool state) { SSNES_LOG("GL VSync => %s\n", state ? "off" : "on"); #ifdef _WIN32 - static BOOL (APIENTRY wgl_swap_interval*)(int) = NULL; + static BOOL (APIENTRY *wgl_swap_interval)(int) = NULL; if (!wgl_swap_interval) SSNES_WARN("SDL VSync toggling seems to be broken, attempting to use WGL VSync call directly instead.\n"); if (!wgl_swap_interval) wgl_swap_interval = (BOOL (APIENTRY*)(int)) wglGetProcAddress("wglSwapIntervalEXT"); diff --git a/hqflt/filters.h b/hqflt/filters.h index 439b3f3727..0e73b294f2 100644 --- a/hqflt/filters.h +++ b/hqflt/filters.h @@ -19,7 +19,9 @@ #ifndef __FILTERS_H #define __FILTERS_H +#ifdef HAVE_CONFIG_H #include "config.h" +#endif #ifdef HAVE_FILTER diff --git a/settings.c b/settings.c index b0df409ba7..5337f042c7 100644 --- a/settings.c +++ b/settings.c @@ -21,7 +21,11 @@ #include #include #include "hqflt/filters.h" + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif + #include struct settings g_settings; @@ -60,6 +64,9 @@ static void set_defaults(void) case AUDIO_AL: def_audio = "openal"; break; + case AUDIO_SDL: + def_audio = "sdl"; + break; default: break; } diff --git a/ssnes.c b/ssnes.c index 2043640e5f..af3a1b724c 100644 --- a/ssnes.c +++ b/ssnes.c @@ -223,6 +223,12 @@ static void fill_pathname(char *out_path, char *in_path, const char *replace) #define FFMPEG_HELP_QUARK #endif +#ifdef _WIN32 +#define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe" +#else +#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg" +#endif + static void print_help(void) { puts("================================================="); @@ -231,7 +237,8 @@ static void print_help(void) puts("Usage: ssnes [rom file] [-h/--help | -s/--save" FFMPEG_HELP_QUARK "]"); puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); - puts("\t-c/--config: Path for config file. Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg"); + puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR); + #ifdef HAVE_FFMPEG puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file."); #endif @@ -354,7 +361,7 @@ int main(int argc, char *argv[]) SSNES_ERR("Could not read ROM file.\n"); exit(1); } - SSNES_LOG("ROM size: %zi bytes\n", rom_len); + SSNES_LOG("ROM size: %d bytes\n", (int)rom_len); if (g_extern.rom_file != NULL) fclose(g_extern.rom_file); diff --git a/ssnes.cfg b/ssnes.cfg index 2d38075cd4..ec4a31fc04 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -10,8 +10,8 @@ # video_yscale = 3.0 # Fullscreen resolution -# video_fullscreen_x = 1280 -# video_fullscreen_y = 720 + video_fullscreen_x = 1920 + video_fullscreen_y = 1200 # Start in fullscreen. Can be changed at runtime. # video_fullscreen = false @@ -43,16 +43,16 @@ # audio_enable = true # Audio output samplerate. -# audio_out_rate = 48000 + audio_out_rate = 48000 # Audio input samplerate from libsnes. # Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled. # Conversely, increase this slightly if you are experiencing good audio, # but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz. -# audio_in_rate = 31950 + audio_in_rate = 31980 # Audio driver backend. Depending on configuration possible candidates are: alsa, oss, rsound, roar, openal -# audio_driver = alsa +# audio_driver = sdl # Override the default audio device the audio_driver uses. # audio_device = @@ -61,7 +61,7 @@ # audio_sync = true # Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency. -# audio_latency = 64 + audio_latency = 16 # libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best. # audio_src_quality = From 35124dc28883880f79007dfc0a48139e2d3499b9 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 18:11:06 +0100 Subject: [PATCH 32/40] Use the more portable "SDL.h" include convention for SDL. --- Makefile.win32 | 10 ++-------- audio/sdl.c | 6 +++--- gfx/gl.c | 6 +++--- gfx/shader_glsl.c | 4 ++-- input/sdl.c | 4 ++-- input/{sdl_input.h => ssnes_sdl_input.h} | 2 +- 6 files changed, 13 insertions(+), 19 deletions(-) rename input/{sdl_input.h => ssnes_sdl_input.h} (98%) diff --git a/Makefile.win32 b/Makefile.win32 index 0e717c06c6..cd56da6e8e 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -6,7 +6,7 @@ CXX = g++ HAVE_SRC = 1 HAVE_SDL = 1 -libsnes = -lsnes +libsnes ?= -lsnes LIBS = DEFINES = -I. -DHAVE_SRC -DHAVE_SDL @@ -14,6 +14,7 @@ LDFLAGS = -L. SRC_LIBS = -lsamplerate-0 SDL_LIBS = -lSDLmain -lSDL +SDL_CFLAGS = -ISDL ifeq ($(HAVE_SRC), 1) LIBS += $(SRC_LIBS) @@ -38,13 +39,6 @@ $(TARGET): $(OBJ) %.o: %.c $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< -install: $(TARGET) - install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin - install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg - -uninstall: $(TARGET) - rm -rf $(DESTDIR)/$(PREFIX)/bin/$(TARGET) - clean: rm -f *.o rm -f audio/*.o diff --git a/audio/sdl.c b/audio/sdl.c index 821e822a9d..2c3eb279f3 100644 --- a/audio/sdl.c +++ b/audio/sdl.c @@ -23,9 +23,9 @@ #include #include -#include -#include -#include +#include "SDL.h" +#include "SDL_audio.h" +#include "SDL_thread.h" #include "general.h" #include "buffer.h" diff --git a/gfx/gl.c b/gfx/gl.c index 1044615bf8..fd77397af9 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -30,9 +30,9 @@ #endif #define NO_SDL_GLEXT -#include -#include -#include "input/sdl_input.h" +#include "SDL.h" +#include "SDL_opengl.h" +#include "input/ssnes_sdl_input.h" #define GL_GLEXT_PROTOTYPES #include diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index b6b120472f..1aeab39b92 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -25,8 +25,8 @@ #define NO_SDL_GLEXT #include -#include -#include +#include "SDL.h" +#include "SDL_opengl.h" #include #include #include diff --git a/input/sdl.c b/input/sdl.c index b4300fade7..a2260ebe2e 100644 --- a/input/sdl.c +++ b/input/sdl.c @@ -17,13 +17,13 @@ #include "driver.h" -#include +#include "SDL.h" #include #include "general.h" #include #include #include -#include "sdl_input.h" +#include "ssnes_sdl_input.h" static void* sdl_input_init(void) { diff --git a/input/sdl_input.h b/input/ssnes_sdl_input.h similarity index 98% rename from input/sdl_input.h rename to input/ssnes_sdl_input.h index d934a8712d..5833396e3f 100644 --- a/input/sdl_input.h +++ b/input/ssnes_sdl_input.h @@ -18,7 +18,7 @@ #ifndef __SSNES_SDL_INPUT_H #define __SSNES_SDL_INPUT_H -#include +#include "SDL.h" typedef struct sdl_input { SDL_Joystick *joysticks[2]; From 5ecc622524255cdae9af933e99eeb73d8afeeccc Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 18:26:19 +0100 Subject: [PATCH 33/40] Some additional fixes. Build in libxml2 for shader support. --- Makefile.win32 | 13 ++++++++++--- gfx/shader_glsl.c | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Makefile.win32 b/Makefile.win32 index cd56da6e8e..71b786d5e5 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -6,10 +6,11 @@ CXX = g++ HAVE_SRC = 1 HAVE_SDL = 1 +HAVE_XML = 1 libsnes ?= -lsnes LIBS = -DEFINES = -I. -DHAVE_SRC -DHAVE_SDL +DEFINES = -I. LDFLAGS = -L. SRC_LIBS = -lsamplerate-0 @@ -18,13 +19,19 @@ SDL_CFLAGS = -ISDL ifeq ($(HAVE_SRC), 1) LIBS += $(SRC_LIBS) - DEFINES += $(SRC_CFLAGS) + DEFINES += $(SRC_CFLAGS) -DHAVE_SRC endif ifeq ($(HAVE_SDL), 1) OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o LIBS += $(SDL_LIBS) -lopengl32 - DEFINES += $(SDL_CFLAGS) + DEFINES += $(SDL_CFLAGS) -DHAVE_SDL +endif + +ifeq ($(HAVE_XML), 1) + OBJ += gfx/shader_glsl.o + DEFINES += $(XML_CFLAGS) -DHAVE_XML + LIBS += -lxml2 endif LIBS += $(libsnes) diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 1aeab39b92..42f39a2012 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -30,6 +30,7 @@ #include #include #include +#include static PFNGLCREATEPROGRAMPROC glCreateProgram = NULL; static PFNGLUSEPROGRAMPROC glUseProgram = NULL; From 8dad959db7feb8f987030bdb87b9626778580f2b Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 18:28:41 +0100 Subject: [PATCH 34/40] Clear up default config file. --- ssnes.cfg | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ssnes.cfg b/ssnes.cfg index ec4a31fc04..7f52b5961f 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -10,8 +10,8 @@ # video_yscale = 3.0 # Fullscreen resolution - video_fullscreen_x = 1920 - video_fullscreen_y = 1200 +# video_fullscreen_x = 1920 +# video_fullscreen_y = 1200 # Start in fullscreen. Can be changed at runtime. # video_fullscreen = false @@ -43,16 +43,16 @@ # audio_enable = true # Audio output samplerate. - audio_out_rate = 48000 +# audio_out_rate = 48000 # Audio input samplerate from libsnes. # Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled. # Conversely, increase this slightly if you are experiencing good audio, # but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz. - audio_in_rate = 31980 +# audio_in_rate = 31980 -# Audio driver backend. Depending on configuration possible candidates are: alsa, oss, rsound, roar, openal -# audio_driver = sdl +# Audio driver backend. Depending on configuration possible candidates are: alsa, oss, jack, rsound, roar, openal and sdl +# audio_driver = # Override the default audio device the audio_driver uses. # audio_device = @@ -61,7 +61,7 @@ # audio_sync = true # Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency. - audio_latency = 16 +# audio_latency = 64 # libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best. # audio_src_quality = @@ -72,7 +72,7 @@ # input_driver = sdl # Defines axis threshold. Possible values are [0.0, 1.0] -# input_axis_threshold = 0.6 +# input_axis_threshold = 0.5 # Keyboard input. Will recognize normal keypresses and special keys like "left", "right", and so on. # input_player1_a = x From 24bbaa4857eb8b29c38fc5ea562fad16c3f734d2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 18:35:44 +0100 Subject: [PATCH 35/40] Add an additional error message when XML fails. --- gfx/shader_glsl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 42f39a2012..51fae27460 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -129,6 +129,7 @@ static bool get_xml_shaders(const char *path, char **vertex_shader, char **fragm return true; error: + SSNES_ERR("Failed to load XML shader ...\n"); if (doc) xmlFreeDoc(doc); xmlFreeParserCtxt(ctx); From 580bffc218add39f72a529bdf92e6dddb5d8dd74 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 20:37:30 +0100 Subject: [PATCH 36/40] Make latency detection more correct. --- audio/sdl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/audio/sdl.c b/audio/sdl.c index 2c3eb279f3..f5748e339f 100644 --- a/audio/sdl.c +++ b/audio/sdl.c @@ -81,8 +81,8 @@ static void* sdl_audio_init(const char* device, int rate, int latency) if (!sdl) return NULL; - // We have to buffer up some data ourselves, so we let SDL carry approx half of the latency. - int frames = find_num_frames(rate, latency / 2); + // We have to buffer up some data ourselves, so we let SDL carry approx half of the latency. SDL double buffers audio and we do as well. + int frames = find_num_frames(rate, latency / 4); SDL_AudioSpec spec = { .freq = rate, @@ -106,7 +106,7 @@ static void* sdl_audio_init(const char* device, int rate, int latency) sdl->lock = SDL_CreateMutex(); sdl->cond = SDL_CreateCond(); - SSNES_LOG("SDL audio: Requested %d ms latency, got %d ms\n", latency, (int)(out.samples * 3 * 1000 / g_settings.audio.out_rate)); + SSNES_LOG("SDL audio: Requested %d ms latency, got %d ms\n", latency, (int)(out.samples * 4 * 1000 / g_settings.audio.out_rate)); // Create a buffer twice as big as needed and prefill the buffer. size_t bufsize = out.samples * 4 * sizeof(int16_t); From 5ab9b153026ef2c58f306bd7fd6691fb8932e5b2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 20:52:12 +0100 Subject: [PATCH 37/40] Add yet another sys/types.h :) --- file.h | 1 + 1 file changed, 1 insertion(+) diff --git a/file.h b/file.h index d4a2e19d3a..5d4add33a1 100644 --- a/file.h +++ b/file.h @@ -22,6 +22,7 @@ #include #include #include +#include ssize_t read_file(FILE *file, void **buf); From b50d1fa178008ef54fa8edccbe5379387e16e4f3 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 21:02:46 +0100 Subject: [PATCH 38/40] Use more "safe" function pointers. --- gfx/shader_glsl.c | 94 +++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 51fae27460..7f62d40878 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -32,19 +32,19 @@ #include #include -static PFNGLCREATEPROGRAMPROC glCreateProgram = NULL; -static PFNGLUSEPROGRAMPROC glUseProgram = NULL; -static PFNGLCREATESHADERPROC glCreateShader = NULL; -static PFNGLDELETESHADERPROC glDeleteShader = NULL; -static PFNGLSHADERSOURCEPROC glShaderSource = NULL; -static PFNGLCOMPILESHADERPROC glCompileShader = NULL; -static PFNGLATTACHSHADERPROC glAttachShader = NULL; -static PFNGLDETACHSHADERPROC glDetachShader = NULL; -static PFNGLLINKPROGRAMPROC glLinkProgram = NULL; -static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL; -static PFNGLUNIFORM1IPROC glUniform1i = NULL; -static PFNGLUNIFORM2FVPROC glUniform2fv = NULL; -static PFNGLUNIFORM4FVPROC glUniform4fv = NULL; +static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL; +static PFNGLUSEPROGRAMPROC pglUseProgram = NULL; +static PFNGLCREATESHADERPROC pglCreateShader = NULL; +static PFNGLDELETESHADERPROC pglDeleteShader = NULL; +static PFNGLSHADERSOURCEPROC pglShaderSource = NULL; +static PFNGLCOMPILESHADERPROC pglCompileShader = NULL; +static PFNGLATTACHSHADERPROC pglAttachShader = NULL; +static PFNGLDETACHSHADERPROC pglDetachShader = NULL; +static PFNGLLINKPROGRAMPROC pglLinkProgram = NULL; +static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation = NULL; +static PFNGLUNIFORM1IPROC pglUniform1i = NULL; +static PFNGLUNIFORM2FVPROC pglUniform2fv = NULL; +static PFNGLUNIFORM4FVPROC pglUniform4fv = NULL; static bool glsl_enable = false; static GLuint gl_program; @@ -139,25 +139,25 @@ error: bool gl_glsl_init(const char *path) { // Load shader functions. - glCreateProgram = SDL_GL_GetProcAddress("glCreateProgram"); - glUseProgram = SDL_GL_GetProcAddress("glUseProgram"); - glCreateShader = SDL_GL_GetProcAddress("glCreateShader"); - glDeleteShader = SDL_GL_GetProcAddress("glDeleteShader"); - glShaderSource = SDL_GL_GetProcAddress("glShaderSource"); - glCompileShader = SDL_GL_GetProcAddress("glCompileShader"); - glAttachShader = SDL_GL_GetProcAddress("glAttachShader"); - glDetachShader = SDL_GL_GetProcAddress("glDetachShader"); - glLinkProgram = SDL_GL_GetProcAddress("glLinkProgram"); - glGetUniformLocation = SDL_GL_GetProcAddress("glGetUniformLocation"); - glUniform1i = SDL_GL_GetProcAddress("glUniform1i"); - glUniform2fv = SDL_GL_GetProcAddress("glUniform2fv"); - glUniform4fv = SDL_GL_GetProcAddress("glUniform4fv"); + pglCreateProgram = SDL_GL_GetProcAddress("glCreateProgram"); + pglUseProgram = SDL_GL_GetProcAddress("glUseProgram"); + pglCreateShader = SDL_GL_GetProcAddress("glCreateShader"); + pglDeleteShader = SDL_GL_GetProcAddress("glDeleteShader"); + pglShaderSource = SDL_GL_GetProcAddress("glShaderSource"); + pglCompileShader = SDL_GL_GetProcAddress("glCompileShader"); + pglAttachShader = SDL_GL_GetProcAddress("glAttachShader"); + pglDetachShader = SDL_GL_GetProcAddress("glDetachShader"); + pglLinkProgram = SDL_GL_GetProcAddress("glLinkProgram"); + pglGetUniformLocation = SDL_GL_GetProcAddress("glGetUniformLocation"); + pglUniform1i = SDL_GL_GetProcAddress("glUniform1i"); + pglUniform2fv = SDL_GL_GetProcAddress("glUniform2fv"); + pglUniform4fv = SDL_GL_GetProcAddress("glUniform4fv"); SSNES_LOG("Checking GLSL shader support ...\n"); - bool shader_support = glCreateProgram && glUseProgram && glCreateShader - && glDeleteShader && glShaderSource && glCompileShader && glAttachShader - && glDetachShader && glLinkProgram && glGetUniformLocation - && glUniform1i && glUniform2fv && glUniform4fv; + bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader + && pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader + && pglDetachShader && pglLinkProgram && pglGetUniformLocation + && pglUniform1i && pglUniform2fv && pglUniform4fv; if (!shader_support) { @@ -165,7 +165,7 @@ bool gl_glsl_init(const char *path) return false; } - gl_program = glCreateProgram(); + gl_program = pglCreateProgram(); char *vertex_prog = NULL; char *fragment_prog = NULL; @@ -174,25 +174,25 @@ bool gl_glsl_init(const char *path) if (vertex_prog) { - vertex_shader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertex_shader, 1, (const char**)&vertex_prog, 0); - glCompileShader(vertex_shader); - glAttachShader(gl_program, vertex_shader); + vertex_shader = pglCreateShader(GL_VERTEX_SHADER); + pglShaderSource(vertex_shader, 1, (const char**)&vertex_prog, 0); + pglCompileShader(vertex_shader); + pglAttachShader(gl_program, vertex_shader); free(vertex_prog); } if (fragment_prog) { - fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragment_shader, 1, (const char**)&fragment_prog, 0); - glCompileShader(fragment_shader); - glAttachShader(gl_program, fragment_shader); + fragment_shader = pglCreateShader(GL_FRAGMENT_SHADER); + pglShaderSource(fragment_shader, 1, (const char**)&fragment_prog, 0); + pglCompileShader(fragment_shader); + pglAttachShader(gl_program, fragment_shader); free(fragment_prog); } if (vertex_prog || fragment_prog) { - glLinkProgram(gl_program); - glUseProgram(gl_program); + pglLinkProgram(gl_program); + pglUseProgram(gl_program); } glsl_enable = true; @@ -211,16 +211,16 @@ void gl_glsl_set_params(unsigned width, unsigned height, GLint location; float inputSize[2] = {width, height}; - location = glGetUniformLocation(gl_program, "rubyInputSize"); - glUniform2fv(location, 1, inputSize); + location = pglGetUniformLocation(gl_program, "rubyInputSize"); + pglUniform2fv(location, 1, inputSize); float outputSize[2] = {out_width, out_height}; - location = glGetUniformLocation(gl_program, "rubyOutputSize"); - glUniform2fv(location, 1, outputSize); + location = pglGetUniformLocation(gl_program, "rubyOutputSize"); + pglUniform2fv(location, 1, outputSize); float textureSize[2] = {tex_width, tex_height}; - location = glGetUniformLocation(gl_program, "rubyTextureSize"); - glUniform2fv(location, 1, textureSize); + location = pglGetUniformLocation(gl_program, "rubyTextureSize"); + pglUniform2fv(location, 1, textureSize); } } From 9df15e8b7de703c24a82a36872a2d4cf8021f830 Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 21:41:11 +0100 Subject: [PATCH 39/40] Add error message for failed GLSL compiles. Works on Win32. :) --- gfx/shader_glsl.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 7f62d40878..1b96dbd3af 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -45,6 +45,8 @@ static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation = NULL; static PFNGLUNIFORM1IPROC pglUniform1i = NULL; static PFNGLUNIFORM2FVPROC pglUniform2fv = NULL; static PFNGLUNIFORM4FVPROC pglUniform4fv = NULL; +static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL; +static PFNGLGETSHADERINFOLOGPROC pglGetShaderInfoLog = NULL; static bool glsl_enable = false; static GLuint gl_program; @@ -136,6 +138,20 @@ error: return false; } +static void print_shader_log(GLuint obj) +{ + int info_len = 0; + int max_len; + + pglGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len); + + char info_log[max_len]; + pglGetShaderInfoLog(obj, max_len, &info_len, info_log); + + if (info_len > 0) + SSNES_LOG("Shader log: %s\n", info_log); +} + bool gl_glsl_init(const char *path) { // Load shader functions. @@ -152,12 +168,15 @@ bool gl_glsl_init(const char *path) pglUniform1i = SDL_GL_GetProcAddress("glUniform1i"); pglUniform2fv = SDL_GL_GetProcAddress("glUniform2fv"); pglUniform4fv = SDL_GL_GetProcAddress("glUniform4fv"); + pglGetShaderiv = SDL_GL_GetProcAddress("glGetShaderiv"); + pglGetShaderInfoLog = SDL_GL_GetProcAddress("glGetShaderInfoLog"); SSNES_LOG("Checking GLSL shader support ...\n"); bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader && pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader && pglDetachShader && pglLinkProgram && pglGetUniformLocation - && pglUniform1i && pglUniform2fv && pglUniform4fv; + && pglUniform1i && pglUniform2fv && pglUniform4fv + && pglGetShaderiv && pglGetShaderInfoLog; if (!shader_support) { @@ -177,6 +196,8 @@ bool gl_glsl_init(const char *path) vertex_shader = pglCreateShader(GL_VERTEX_SHADER); pglShaderSource(vertex_shader, 1, (const char**)&vertex_prog, 0); pglCompileShader(vertex_shader); + print_shader_log(vertex_shader); + pglAttachShader(gl_program, vertex_shader); free(vertex_prog); } @@ -185,6 +206,8 @@ bool gl_glsl_init(const char *path) fragment_shader = pglCreateShader(GL_FRAGMENT_SHADER); pglShaderSource(fragment_shader, 1, (const char**)&fragment_prog, 0); pglCompileShader(fragment_shader); + print_shader_log(fragment_shader); + pglAttachShader(gl_program, fragment_shader); free(fragment_prog); } From 12ebdacd12003e48626c151710b429d49f9d660e Mon Sep 17 00:00:00 2001 From: Themaister Date: Fri, 7 Jan 2011 22:33:21 +0100 Subject: [PATCH 40/40] Fix some build problems in MinGW Linux. --- Makefile.win32 | 2 +- gfx/shader_glsl.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile.win32 b/Makefile.win32 index 71b786d5e5..d66d1cc925 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -11,7 +11,7 @@ libsnes ?= -lsnes LIBS = DEFINES = -I. -LDFLAGS = -L. +LDFLAGS = -L. -static-libgcc SRC_LIBS = -lsamplerate-0 SDL_LIBS = -lSDLmain -lSDL diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 1b96dbd3af..a9e93a47e6 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -30,6 +30,8 @@ #include #include #include + +#define GL_GLEXT_PROTOTYPES #include static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL;