Merge branch 'master' into ownconf

Conflicts:
	config.mk
This commit is contained in:
Themaister 2010-08-16 21:21:43 +02:00
commit 26654f7645
10 changed files with 250 additions and 18 deletions

View File

@ -16,6 +16,10 @@ ifeq ($(BUILD_ALSA), 1)
OBJ += alsa.o
LIBS += -lasound
endif
ifeq ($(BUILD_ROAR), 1)
OBJ += roar.o
LIBS += -lroar
endif
ifeq ($(BUILD_OPENGL), 1)
OBJ += gl.o
@ -25,7 +29,7 @@ ifeq ($(BUILD_FILTER), 1)
OBJ += hqflt/hq.o
endif
CFLAGS = -Wall -O3 -march=native -std=c99
CFLAGS = -Wall -O3 -march=native -std=gnu99

16
alsa.c
View File

@ -27,6 +27,7 @@
typedef struct alsa
{
snd_pcm_t *pcm;
bool nonblock;
} alsa_t;
static void* __alsa_init(const char* device, int rate, int latency)
@ -125,7 +126,8 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
return size;
}
else if ( alsa->nonblock && frames == -EAGAIN )
return 0;
else if ( frames < 0 )
return -1;
@ -134,11 +136,18 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
static bool __alsa_stop(void *data)
{
/* int *fd = data;
ioctl(*fd, SNDCTL_DSP_RESET, 0);*/
return true;
}
static void __alsa_set_nonblock_state(void *data, bool state)
{
alsa_t *alsa = data;
if (snd_pcm_nonblock(alsa->pcm, state) < 0)
fprintf(stderr, "SSNES [ERROR]: Could not set PCM to non-blocking. Will not be able to fast-forward.\n");
else
alsa->nonblock = state;
}
static bool __alsa_start(void *data)
{
return true;
@ -163,6 +172,7 @@ const audio_driver_t audio_alsa = {
.write = __alsa_write,
.stop = __alsa_stop,
.start = __alsa_start,
.set_nonblock_state = __alsa_set_nonblock_state,
.free = __alsa_free
};

View File

@ -35,6 +35,7 @@
#define AUDIO_RSOUND 1
#define AUDIO_OSS 2
#define AUDIO_ALSA 3
#define AUDIO_ROAR 4
////////////////////////
// Chooses which video and audio subsystem to use. Remember to update config.mk if you change these.
@ -92,7 +93,7 @@ static const char* audio_device = NULL;
static const int out_latency = 64;
// Defines the quality (and cpu reqirements) of samplerate conversion.
#define SAMPLERATE_QUALITY SRC_SINC_FASTEST
#define SAMPLERATE_QUALITY SRC_LINEAR
@ -117,6 +118,7 @@ static const struct snes_keybind snes_keybinds[] = {
{ SNES_DEVICE_ID_JOYPAD_DOWN, GLFW_KEY_DOWN, 11 },
{ SNES_DEVICE_ID_JOYPAD_START, GLFW_KEY_ENTER, 6 },
{ SNES_DEVICE_ID_JOYPAD_SELECT, GLFW_KEY_RSHIFT, 14 },
{ SNES_FAST_FORWARD_KEY, GLFW_KEY_SPACE, 9 },
{ -1 }
};

View File

@ -5,6 +5,7 @@ BUILD_FILTER = 0
BUILD_RSOUND = 1
BUILD_OSS = 0
BUILD_ALSA = 0
BUILD_ROAR = 0
PREFIX = /usr

View File

@ -24,6 +24,9 @@
#include <stdint.h>
#include <unistd.h>
#define SNES_FAST_FORWARD_KEY 0x666 // Hurr, durr
void set_fast_forward_button(bool state);
struct snes_keybind
{
int id;
@ -48,6 +51,7 @@ typedef struct audio_driver
ssize_t (*write)(void* data, const void* buf, size_t size);
bool (*stop)(void* data);
bool (*start)(void* data);
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
void (*free)(void* data);
} audio_driver_t;
@ -64,6 +68,7 @@ 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? :)
bool (*frame)(void* data, const uint16_t* frame, int width, int height);
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
void (*free)(void* data);
} video_driver_t;

71
gl.c
View File

@ -21,6 +21,7 @@
#include <stdint.h>
#include "libsnes.hpp"
#include <stdio.h>
#include <sys/time.h>
static GLuint texture;
static uint8_t *gl_buffer;
@ -29,7 +30,7 @@ static GLuint tex_filter;
typedef struct gl
{
int foo;
bool vsync;
} gl_t;
@ -68,11 +69,26 @@ static int16_t glfw_input_state(void *data, const struct snes_keybind *snes_keyb
glfwGetJoystickButtons(joypad_id, buttons, joypad_buttons);
}
// Finds fast forwarding state.
for ( i = 0; snes_keybinds[i].id != -1; i++ )
{
if ( snes_keybinds[i].id == SNES_FAST_FORWARD_KEY )
{
bool pressed = false;
if ( glfwGetKey(snes_keybinds[i].key) )
pressed = true;
else if ( snes_keybinds[i].joykey < joypad_buttons && buttons[snes_keybinds[i].joykey] == GLFW_PRESS )
pressed = true;
set_fast_forward_button(pressed);
break;
}
}
for ( i = 0; snes_keybinds[i].id != -1; i++ )
{
if ( snes_keybinds[i].id == (int)id )
{
if ( glfwGetKey(snes_keybinds[i].key ))
if ( glfwGetKey(snes_keybinds[i].key) )
return 1;
if ( snes_keybinds[i].joykey < joypad_buttons && buttons[snes_keybinds[i].joykey] == GLFW_PRESS )
@ -128,6 +144,12 @@ static void GLFWCALL resize(int width, int height)
glLoadIdentity();
}
static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, int frames)
{
float time = new_tv->tv_sec - tv->tv_sec + (new_tv->tv_usec - tv->tv_usec)/1000000.0;
return frames/time;
}
static bool gl_frame(void *data, const uint16_t* frame, int width, int height)
{
(void)data;
@ -160,6 +182,31 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height)
glEnd();
// Shows FPS in taskbar.
static int frames = 0;
static struct timeval tv;
struct timeval new_tv;
if (frames == 0)
gettimeofday(&tv, NULL);
if ((frames % 60) == 0 && frames > 0)
{
gettimeofday(&new_tv, NULL);
struct timeval tmp_tv = {
.tv_sec = tv.tv_sec,
.tv_usec = tv.tv_usec
};
gettimeofday(&tv, NULL);
char tmpstr[256] = {0};
float fps = tv_to_fps(&tmp_tv, &new_tv, 60);
snprintf(tmpstr, sizeof(tmpstr) - 1, "SSNES || FPS: %6.1f || Frames: %d", fps, frames);
glfwSetWindowTitle(tmpstr);
}
frames++;
glfwSwapBuffers();
return true;
@ -171,10 +218,22 @@ static void gl_free(void *data)
free(gl_buffer);
}
static void gl_set_nonblock_state(void *data, bool state)
{
gl_t *gl = data;
if (gl->vsync)
{
if (state)
glfwSwapInterval(0);
else
glfwSwapInterval(1);
}
}
static void* gl_init(video_info_t *video, const input_driver_t **input)
{
gl_t *foo = malloc(sizeof(gl_t));
if ( foo == NULL )
gl_t *gl = malloc(sizeof(gl_t));
if ( gl == NULL )
return NULL;
keep_aspect = video->force_aspect;
@ -200,6 +259,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input)
glfwSwapInterval(1); // Force vsync
else
glfwSwapInterval(0);
gl->vsync = video->vsync;
gl_buffer = malloc(256 * 256 * 2 * video->input_scale * video->input_scale);
if ( !gl_buffer )
@ -225,12 +285,13 @@ static void* gl_init(video_info_t *video, const input_driver_t **input)
GL_UNSIGNED_SHORT_1_5_5_5_REV, gl_buffer);
*input = &input_glfw;
return foo;
return gl;
}
const video_driver_t video_gl = {
.init = gl_init,
.frame = gl_frame,
.set_nonblock_state = gl_set_nonblock_state,
.free = gl_free
};

19
oss.c
View File

@ -22,6 +22,8 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
static void* __oss_init(const char* device, int rate, int latency)
{
@ -86,7 +88,11 @@ static ssize_t __oss_write(void* data, const void* buf, size_t size)
ssize_t ret;
if ( (ret = write(*fd, buf, size)) <= 0 )
{
if ( (fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN )
return 0;
return -1;
}
return ret;
}
@ -103,6 +109,18 @@ static bool __oss_start(void *data)
return true;
}
static void __oss_set_nonblock_state(void *data, bool state)
{
int *fd = data;
int rc;
if (state)
rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) | O_NONBLOCK);
else
rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) & (~O_NONBLOCK));
if (rc != 0)
fprintf(stderr, "SSNES [ERROR]: Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
}
static void __oss_free(void *data)
{
int *fd = data;
@ -117,6 +135,7 @@ const audio_driver_t audio_oss = {
.write = __oss_write,
.stop = __oss_stop,
.start = __oss_start,
.set_nonblock_state = __oss_set_nonblock_state,
.free = __oss_free
};

92
roar.c Normal file
View File

@ -0,0 +1,92 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include <stdlib.h>
#include <roaraudio.h>
#include <errno.h>
#include <stdio.h>
static void* __roar_init(const char* device, int rate, int latency)
{
int err;
roar_vs_t *vss;
if ( (vss = roar_vs_new_simple(NULL, NULL, rate, 2, ROAR_CODEC_PCM_S_LE, 16, ROAR_DIR_PLAY, &err)) == NULL )
{
fprintf(stderr, "roar_vs: \"%s\"\n", roar_vs_strerr(err));
return NULL;
}
return vss;
}
static ssize_t __roar_write(void* data, const void* buf, size_t size)
{
roar_vs_t *vss = data;
if ( size == 0 )
return 0;
int err;
if (roar_vs_write(vss, buf, size, &err) < 0)
{
if (err == ROAR_ERROR_NONE)
return 0;
return -1;
}
return size;
}
static bool __roar_stop(void *data)
{
return true;
}
static void __roar_set_nonblock_state(void *data, bool state)
{
roar_vs_t *vss = data;
if (roar_vs_blocking(vss, (state) ? ROAR_VS_FALSE : ROAR_VS_TRUE, NULL) < 0)
fprintf(stderr, "SSNES [ERROR]: Can't set nonblocking. Will not be able to fast-forward.\n");
}
static bool __roar_start(void *data)
{
return true;
}
static void __roar_free(void *data)
{
roar_vs_t *vss = data;
roar_vs_close(vss, ROAR_VS_TRUE, NULL);
}
const audio_driver_t audio_roar = {
.init = __roar_init,
.write = __roar_write,
.stop = __roar_stop,
.start = __roar_start,
.set_nonblock_state = __roar_set_nonblock_state,
.free = __roar_free
};

View File

@ -25,6 +25,7 @@ typedef struct rsd
rsound_t *rd;
int latency;
int rate;
int nonblock;
} rsd_t;
static void* __rsd_init(const char* device, int rate, int latency)
@ -74,6 +75,9 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
{
rsd_t *rsd = data;
if ( rsd_delay_ms(rsd->rd) > rsd->latency && rsd->nonblock )
return 0;
if ( size == 0 )
return 0;
@ -101,6 +105,12 @@ static bool __rsd_stop(void *data)
return true;
}
static void __rsd_set_nonblock_state(void *data, bool state)
{
rsd_t *rsd = data;
rsd->nonblock = state;
}
static bool __rsd_start(void *data)
{
rsd_t *rsd = data;
@ -124,6 +134,7 @@ const audio_driver_t audio_rsound = {
.write = __rsd_write,
.stop = __rsd_stop,
.start = __rsd_start,
.set_nonblock_state = __rsd_set_nonblock_state,
.free = __rsd_free
};

43
ssnes.c
View File

@ -36,6 +36,7 @@ static SRC_STATE* source = NULL;
extern const audio_driver_t audio_rsound;
extern const audio_driver_t audio_oss;
extern const audio_driver_t audio_alsa;
extern const audio_driver_t audio_roar;
extern const video_driver_t video_gl;
////////////////////////////////////////////////
@ -52,6 +53,8 @@ static driver_t driver = {
.audio = &audio_oss,
#elif AUDIO_DRIVER == AUDIO_ALSA
.audio = &audio_alsa,
#elif AUDIO_DRIVER == AUDIO_ROAR
.audio = &audio_roar,
#else
#error "Define a valid audio driver in config.h"
#endif
@ -70,6 +73,29 @@ static void write_file(const char* path, uint8_t* data, size_t size);
static void load_save_file(const char* path, int type);
static void save_file(const char* path, int type);
// To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then.
#define AUDIO_CHUNK_SIZE_BLOCKING 64
#define AUDIO_CHUNK_SIZE_NONBLOCKING 1024 // So we don't get complete line-noise when fast-forwarding audio.
static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
void set_fast_forward_button(bool new_button_state)
{
static bool old_button_state = false;
static bool syncing_state = false;
if (new_button_state && !old_button_state)
{
syncing_state = !syncing_state;
driver.video->set_nonblock_state(driver.video_data, syncing_state);
driver.audio->set_nonblock_state(driver.audio_data, syncing_state);
if (syncing_state)
audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
else
audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
}
old_button_state = new_button_state;
}
static void init_drivers(void)
{
init_video_input();
@ -197,30 +223,28 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
}
#define SRC_SAMPLES 64
static void audio_sample(uint16_t left, uint16_t right)
{
if ( !audio_active )
return;
static float data[SRC_SAMPLES];
static float data[AUDIO_CHUNK_SIZE_NONBLOCKING];
static int data_ptr = 0;
data[data_ptr++] = (float)(*(int16_t*)&left)/0x7FFF;
data[data_ptr++] = (float)(*(int16_t*)&right)/0x7FFF;
if ( data_ptr == SRC_SAMPLES )
if ( data_ptr >= audio_chunk_size )
{
float outsamples[SRC_SAMPLES * 16];
int16_t temp_outsamples[SRC_SAMPLES * 16];
float outsamples[audio_chunk_size * 16];
int16_t temp_outsamples[audio_chunk_size * 16];
SRC_DATA src_data;
src_data.data_in = data;
src_data.data_out = outsamples;
src_data.input_frames = SRC_SAMPLES / 2;
src_data.output_frames = SRC_SAMPLES * 8;
src_data.input_frames = audio_chunk_size / 2;
src_data.output_frames = audio_chunk_size * 8;
src_data.end_of_input = 0;
src_data.src_ratio = (double)out_rate / (double)in_rate;
@ -229,7 +253,10 @@ static void audio_sample(uint16_t left, uint16_t right)
src_float_to_short_array(outsamples, temp_outsamples, src_data.output_frames_gen * 2);
if ( driver.audio->write(driver.audio_data, temp_outsamples, src_data.output_frames_gen * 4) < 0 )
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
audio_active = false;
}
data_ptr = 0;
}