Compare commits

...
19 Commits
Author SHA1 Message Date
Themaister 43dc30c3a5 Updates for 0.2.1 2011-01-30 13:44:30 +01:00
Themaister 98e6613df0 Add in feature check printing for XAudio and Pulse. 2011-01-30 00:30:54 +01:00
Themaister 3ce07891e4 Update Makefile to add in rsound.dll and stuff. 2011-01-30 00:04:14 +01:00
Themaister 2ff47ab7c4 Compile in RSound for Win32 as well. 2011-01-29 23:53:38 +01:00
Themaister 8ba639c7d5 Fix log message. 2011-01-29 23:23:54 +01:00
Themaister e0403e4dae Indentation error. 2011-01-29 19:53:23 +01:00
Themaister 7776525c96 Add a simple README for Win32. 2011-01-29 19:43:05 +01:00
Themaister 25fe9b7f58 Add possibility to alter sound input rate on-the-fly. 2011-01-29 18:42:21 +01:00
Themaister fff7aba8ec Use threaded mainloop for Pulse. Seems to fix most issues. 2011-01-29 02:00:57 +01:00
Themaister e83f3ed444 Initial PulseAudio code. 2011-01-29 01:15:09 +01:00
Themaister 334f11d5bc Add logging when XAudio fails. 2011-01-28 20:57:13 +01:00
Themaister 5bcde44684 Remove some undeeded warning/error messages. 2011-01-27 23:05:20 +01:00
Themaister 2459793f9c Set O_BINARY in Win32 on stdin reads. 2011-01-27 22:43:12 +01:00
Themaister cadd7b9728 Make SDL take priority over XAudio. 2011-01-27 14:43:46 +01:00
Themaister 644f247026 Should fix GL on other cards than nVidia... 2011-01-27 14:17:01 +01:00
Themaister 8c1ef3051d Add XAudio support. Need a separate xaudio-c.dll which actually implements the interface. 2011-01-27 01:57:25 +01:00
Themaister d296e15f26 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-01-27 00:58:01 +01:00
Themaister 5a5cd46f9b Merge branch 'master' of git://github.com/Themaister/SSNES 2011-01-24 22:43:52 +01:00
Themaister 20d9345a4e Do not link in snes.dll when doing dynamic build for win32. 2011-01-19 16:11:45 +01:00
21 changed files with 821 additions and 79 deletions

No files matched your search

+5
View File
@@ -42,6 +42,11 @@ ifeq ($(HAVE_JACK),1)
OBJ += audio/jack.o
LIBS += -ljack
endif
ifeq ($(HAVE_PULSE), 1)
OBJ += audio/pulse.o
LIBS += $(PULSE_LIBS)
DEFINES += $(PULSE_CFLAGS)
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
+14 -1
View File
@@ -10,6 +10,8 @@ HAVE_SRC = 1
HAVE_SDL = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 1
libsnes ?= -lsnes
LIBS =
@@ -31,6 +33,17 @@ ifeq ($(HAVE_SDL), 1)
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
LIBS += -lrsound
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML
@@ -84,7 +97,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll freetype6.dll $(JTARGET)
zip -r ssnes-win32-0.2.1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+3 -1
View File
@@ -26,7 +26,7 @@ SSNES can utilize these libraries if enabled:
- libxml2 (bSNES XML shaders)
- libfreetype2 (TTF font rendering on screen)
SSNES needs one of these audio driver libraries:
SSNES needs at least one of these audio driver libraries:
- ALSA
- OSS
@@ -35,6 +35,8 @@ SSNES needs one of these audio driver libraries:
- OpenAL
- JACK
- SDL
- XAudio2 (Win32)
- PulseAudio
# Building libsnes
+39
View File
@@ -0,0 +1,39 @@
===================================================
SSNES - Simple Super Nintendo Emulator System
===================================================
SSNES is a simplistic emulator frontend designed for use without any GUI.
It is used from command line, but is designed to play well with eventual GUI frontends,
as the configuration file format is very simple.
This allows for great portability and focus on what matters in a frontend, such as video and audio performance.
Its core libraries for video and input are OpenGL and SDL, with many different audio library supported.
On first use you should get accustomed to how SSNES works.
- SSNES emphasizes simplicity. It has no graphical user interface, and is primarily used through the command line.
It is recommended for experienced users.
It is possible though to create a GUI which can use SSNES.
- SSNES isn't dependant on any emulator core, such as bSNES, SNES9x or ZSNES.
SSNES only talks to the libsnes interface,
which was created initially by bSNES but it has an implementation available for SNES9x as well.
- SSNES is configured through a config file. In the Win32 distribution, there should be a ssnes.cfg included.
This config file serves as a base for your custom configuration file.
It is recommended that you go through the config file and alter any configurations you might use.
This is a good way to learn about the features SSNES have.
It is possible to pick which configuration file you want with the '-c' flag.
E.g.: ssnes.exe -c myconfig.cfg rom.sfc.
- When unsure which command line flags to use, use the command: ssnes.exe --help.
- When something is not working as expected, try using verbose logging first to see what is happening:
E.g.: ssnes.exe -v romfile.sfc
If something is not working, and you want to file a bug report,
please include console output with the -v flag turned on.
- SSNES does not support zipped roms directly. It only supports roms with extentions .sfc and .smc.
However, SSNES can read roms from stdin,
which makes it possible to create wrapper programs that can support every zip format under the sun.
This can also be performed by a GUI frontend.
+252
View File
@@ -0,0 +1,252 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include "general.h"
#include <pulse/pulseaudio.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
typedef struct
{
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
bool nonblock;
} pa_t;
static void __pulse_free(void *data)
{
pa_t *pa = data;
if (pa)
{
if (pa->mainloop)
pa_threaded_mainloop_stop(pa->mainloop);
if (pa->stream)
{
pa_stream_disconnect(pa->stream);
pa_stream_unref(pa->stream);
}
if (pa->context)
{
pa_context_disconnect(pa->context);
pa_context_unref(pa->context);
}
if (pa->mainloop)
pa_threaded_mainloop_free(pa->mainloop);
free(pa);
}
}
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t x;
uint8_t y[2];
} u;
u.x = 1;
return u.y[0];
}
static void context_state_cb(pa_context *c, void *data)
{
pa_t *pa = data;
switch (pa_context_get_state(c))
{
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal(pa->mainloop, 0);
break;
default:
break;
}
}
static void stream_state_cb(pa_stream *s, void *data)
{
pa_t *pa = data;
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal(pa->mainloop, 0);
break;
default:
break;
}
}
static void stream_request_cb(pa_stream *s, size_t length, void *data)
{
(void)length;
(void)s;
pa_t *pa = data;
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
static void stream_latency_update_cb(pa_stream *s, void *data)
{
(void)s;
pa_t *pa = data;
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
static void* __pulse_init(const char* device, int rate, int latency)
{
pa_t *pa = calloc(1, sizeof(*pa));
if (!pa)
goto error;
pa->mainloop = pa_threaded_mainloop_new();
if (!pa->mainloop)
goto error;
pa->context = pa_context_new(pa_threaded_mainloop_get_api(pa->mainloop), "SSNES");
if (!pa->context)
goto error;
pa_context_set_state_callback(pa->context, context_state_cb, pa);
if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0)
goto error;
pa_threaded_mainloop_lock(pa->mainloop);
if (pa_threaded_mainloop_start(pa->mainloop) < 0)
goto error;
pa_threaded_mainloop_wait(pa->mainloop);
if (pa_context_get_state(pa->context) != PA_CONTEXT_READY)
goto unlock_error;
pa_sample_spec spec = {
.format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE,
.channels = 2,
.rate = rate
};
pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL);
if (!pa->stream)
goto unlock_error;
pa_stream_set_state_callback(pa->stream, stream_state_cb, pa);
pa_stream_set_write_callback(pa->stream, stream_request_cb, pa);
pa_stream_set_latency_update_callback(pa->stream, stream_latency_update_cb, pa);
pa_buffer_attr buffer_attr = {
.maxlength = -1,
.tlength = pa_usec_to_bytes(latency * PA_USEC_PER_MSEC, &spec),
.prebuf = -1,
.minreq = -1,
.fragsize = -1
};
if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
goto error;
pa_threaded_mainloop_wait(pa->mainloop);
if (pa_stream_get_state(pa->stream) != PA_STREAM_READY)
goto unlock_error;
pa_threaded_mainloop_unlock(pa->mainloop);
return pa;
unlock_error:
pa_threaded_mainloop_unlock(pa->mainloop);
error:
__pulse_free(pa);
return NULL;
}
static ssize_t __pulse_write(void* data, const void* buf, size_t size)
{
pa_t *pa = data;
pa_threaded_mainloop_lock(pa->mainloop);
unsigned length = pa_stream_writable_size(pa->stream);
pa_threaded_mainloop_unlock(pa->mainloop);
while (length < size)
{
pa_threaded_mainloop_wait(pa->mainloop);
pa_threaded_mainloop_lock(pa->mainloop);
length = pa_stream_writable_size(pa->stream);
pa_threaded_mainloop_unlock(pa->mainloop);
if (pa->nonblock)
break;
}
size_t write_size = length < size ? length : size;
pa_threaded_mainloop_lock(pa->mainloop);
pa_stream_write(pa->stream, buf, write_size, NULL, 0LL, PA_SEEK_RELATIVE);
pa_threaded_mainloop_unlock(pa->mainloop);
return write_size;
}
static bool __pulse_stop(void *data)
{
(void)data;
return true;
}
static bool __pulse_start(void *data)
{
(void)data;
return true;
}
static void __pulse_set_nonblock_state(void *data, bool state)
{
pa_t *pa = data;
pa->nonblock = state;
}
static bool __pulse_use_float(void *data)
{
(void)data;
return true;
}
const audio_driver_t audio_pulse = {
.init = __pulse_init,
.write = __pulse_write,
.stop = __pulse_stop,
.start = __pulse_start,
.set_nonblock_state = __pulse_set_nonblock_state,
.use_float = __pulse_use_float,
.free = __pulse_free,
.ident = "pulse"
};
+160
View File
@@ -0,0 +1,160 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
// This will currently only compile with MSVC2k10.
// Should be compilable with some MinGW trickery as well.
#include "xaudio-c.h"
struct xaudio2 : public IXAudio2VoiceCallback
{
xaudio2(unsigned samplerate, unsigned channels, unsigned bits, unsigned size) :
buf(nullptr), pXAudio2(nullptr), pMasterVoice(nullptr), pSourceVoice(nullptr),
buffers(0), bufptr(0)
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
HRESULT hr;
if (FAILED(hr = XAudio2Create(&pXAudio2, 0)))
throw -1;
if (FAILED(hr = pXAudio2->CreateMasteringVoice(
&pMasterVoice, channels, samplerate, 0, 0, nullptr)))
throw -1;
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = channels;
wfx.nSamplesPerSec = samplerate;
wfx.nBlockAlign = channels * bits / 8;
wfx.wBitsPerSample = bits;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if (FAILED(hr = pXAudio2->CreateSourceVoice(
&pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr)))
throw -1;
pSourceVoice->Start();
hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!hEvent)
throw -1;
max_buffers = 8;
bufsize = size / max_buffers;
buf = new uint8_t[bufsize * max_buffers];
write_buffer = 0;
}
~xaudio2()
{
if (pSourceVoice)
{
pSourceVoice->Stop();
pSourceVoice->DestroyVoice();
}
if (pMasterVoice)
pMasterVoice->DestroyVoice();
if (pXAudio2)
pXAudio2->Release();
if (hEvent)
CloseHandle(hEvent);
if (buf)
delete[] buf;
}
size_t write_avail() const
{
return bufsize * (max_buffers - buffers - 1);
}
size_t write(const void *buffer, size_t bytes)
{
size_t written = 0;
while (written < bytes)
{
size_t need = min(bytes - written, bufsize - bufptr);
memcpy((uint8_t*)buf + write_buffer * bufsize + bufptr, (const uint8_t*)buffer + written, need);
written += need;
bufptr += need;
if (bufptr == bufsize)
{
while (buffers == max_buffers - 1)
WaitForSingleObject(hEvent, INFINITE);
XAUDIO2_BUFFER xa2buffer = {0};
xa2buffer.AudioBytes = bufsize;
xa2buffer.pAudioData = buf + write_buffer * bufsize;
xa2buffer.pContext = nullptr;
InterlockedIncrement(&buffers);
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
bufptr = 0;
write_buffer = (write_buffer + 1) % max_buffers;
}
}
return bytes;
}
STDMETHOD_(void, OnBufferStart) (void *) {}
STDMETHOD_(void, OnBufferEnd) (void *)
{
InterlockedDecrement(&buffers);
SetEvent(hEvent);
}
STDMETHOD_(void, OnLoopEnd) (void *) {}
STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) ()
{
SetEvent(hEvent);
}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
uint8_t *buf;
IXAudio2 *pXAudio2;
IXAudio2MasteringVoice *pMasterVoice;
IXAudio2SourceVoice *pSourceVoice;
HANDLE hEvent;
volatile long buffers;
unsigned max_buffers;
unsigned bufsize;
unsigned bufptr;
unsigned write_buffer;
};
XAUDIOC_API xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t size)
{
xaudio2 *handle = nullptr;
try {
handle = new xaudio2(samplerate, channels, bits, size);
} catch(...) { delete handle; return nullptr; }
return handle;
}
XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle)
{
try {
return handle->write_avail();
} catch(...) { return 0; }
}
XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes)
{
try {
return handle->write(buf, bytes);
} catch(...) { return 0; }
}
XAUDIOC_API void xaudio2_free(xaudio2_t *handle)
{
delete handle;
}
+38
View File
@@ -0,0 +1,38 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
#ifndef XAUDIO_C_H
#define XAUDIO_C_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XAUDIOC_EXPORTS
#define XAUDIOC_API __declspec(dllexport)
#else
#define XAUDIOC_API __declspec(dllimport)
#endif
typedef struct xaudio2 xaudio2_t;
XAUDIOC_API xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t bufsize);
XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle);
XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *data, size_t bytes);
XAUDIOC_API void xaudio2_free(xaudio2_t *handle);
typedef xaudio2_t* (*xaudio2_new_t)(unsigned, unsigned, unsigned, size_t);
typedef size_t (*xaudio2_write_avail_t)(xaudio2_t*);
typedef size_t (*xaudio2_write_t)(xaudio2_t*, const void*, size_t);
typedef void (*xaudio2_free_t)(xaudio2_t*);
#ifdef __cplusplus
}
#endif
#endif
+163
View File
@@ -0,0 +1,163 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include <stdlib.h>
#include "xaudio-c.h"
#include "general.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct
{
xaudio2_t *xa;
bool nonblock;
} xa_t;
static xaudio2_new_t pxanew = NULL;
static xaudio2_write_t pxawrite = NULL;
static xaudio2_write_avail_t pxawrite_avail = NULL;
static xaudio2_free_t pxafree = NULL;
static HMODULE lib = NULL;
#define LIB_NAME "xaudio-c.dll"
#define SYM(X) ((void*)GetProcAddress(lib, "xaudio2_" #X))
static void deinit_lib(void)
{
FreeModule(lib);
lib = NULL;
}
static bool init_lib(void)
{
if (lib)
return true;
lib = LoadLibrary(LIB_NAME);
if (!lib)
return false;
pxanew = SYM(new);
pxawrite = SYM(write);
pxawrite_avail = SYM(write_avail);
pxafree = SYM(free);
if (!pxanew || !pxawrite || !pxawrite_avail || !pxafree)
{
deinit_lib();
return false;
}
return true;
}
// 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 void* __xa_init(const char* device, int rate, int latency)
{
if (!init_lib())
return NULL;
xa_t *xa = calloc(1, sizeof(xa_t));
if (xa == NULL)
return NULL;
size_t bufsize = latency * rate / 1000;
bufsize = next_pow2(bufsize);
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize * 1000 / rate);
xa->xa = pxanew(rate, 2, 16, bufsize << 2);
if (!xa->xa)
{
SSNES_ERR("Failed to init XAudio2.\n");
free(xa);
return NULL;
}
return xa;
}
static ssize_t __xa_write(void* data, const void* buf, size_t size)
{
xa_t *xa = data;
if (xa->nonblock)
{
size_t avail = pxawrite_avail(xa->xa);
if (avail < size)
size = avail;
}
return pxawrite(xa->xa, buf, size);
}
static bool __xa_stop(void *data)
{
(void)data;
return true;
}
static void __xa_set_nonblock_state(void *data, bool state)
{
xa_t *xa = data;
xa->nonblock = state;
}
static bool __xa_start(void *data)
{
(void)data;
return true;
}
static void __xa_free(void *data)
{
xa_t *xa = data;
if (xa && xa->xa)
{
if (pxafree)
pxafree(xa->xa);
free(xa);
}
deinit_lib();
}
const audio_driver_t audio_xa = {
.init = __xa_init,
.write = __xa_write,
.stop = __xa_stop,
.start = __xa_start,
.set_nonblock_state = __xa_set_nonblock_state,
.free = __xa_free,
.ident = "xaudio"
};
+23 -10
View File
@@ -53,26 +53,34 @@
#define AUDIO_AL 5
#define AUDIO_JACK 6
#define AUDIO_SDL 8
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
////////////////////////
#define INPUT_SDL 7
////////////////////////
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
#if HAVE_ALSA
#if defined(HAVE_ALSA)
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
#elif HAVE_OSS
#elif defined(HAVE_PULSE)
#define AUDIO_DEFAULT_DRIVER AUDIO_PULSE
#elif defined(HAVE_OSS)
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif HAVE_JACK
#elif defined(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
#elif defined(HAVE_AL)
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif HAVE_SDL
#elif defined(HAVE_SDL)
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#elif defined(HAVE_XAUDIO)
#define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO
#elif defined(HAVE_RSOUND)
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
#elif defined(HAVE_ROAR)
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
#else
#error Need at least one audio driver!
#endif
#define INPUT_DEFAULT_DRIVER INPUT_SDL
@@ -121,7 +129,10 @@ 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 = 31980;
static const float in_rate = 31980.0;
// When changing input rate on-the-fly
static const float audio_rate_step = 0.25;
// Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults.
static const char* audio_device = NULL;
@@ -171,6 +182,8 @@ static const struct snes_keybind snes_keybinds_1[] = {
{ SSNES_QUIT_KEY, SDLK_ESCAPE, NO_BTN, AXIS_NONE },
{ SSNES_STATE_SLOT_MINUS, SDLK_F6, NO_BTN, AXIS_NONE },
{ SSNES_STATE_SLOT_PLUS, SDLK_F7, NO_BTN, AXIS_NONE },
{ SSNES_AUDIO_INPUT_RATE_PLUS, SDLK_KP_PLUS, NO_BTN, AXIS_NONE },
{ SSNES_AUDIO_INPUT_RATE_MINUS, SDLK_KP_MINUS, NO_BTN, AXIS_NONE },
{ -1 }
};
+12
View File
@@ -50,6 +50,18 @@ static const bool _jack_supp = true;
static const bool _jack_supp = false;
#endif
#ifdef HAVE_PULSE
static const bool _pulse_supp = true;
#else
static const bool _pulse_supp = false;
#endif
#ifdef HAVE_XAUDIO
static const bool _xaudio_supp = true;
#else
static const bool _xaudio_supp = false;
#endif
#ifdef HAVE_FILTER
static const bool _filter_supp = true;
#else
+6 -24
View File
@@ -49,6 +49,12 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_SDL
&audio_sdl,
#endif
#ifdef HAVE_XAUDIO
&audio_xa,
#endif
#ifdef HAVE_PULSE
&audio_pulse,
#endif
};
static const video_driver_t *video_drivers[] = {
@@ -268,27 +274,3 @@ void uninit_video_input(void)
driver_t driver;
#if 0
driver_t driver = {
#if VIDEO_DRIVER == VIDEO_GL
.video = &video_gl,
#else
#error "Define a valid video driver in config.h"
#endif
#if AUDIO_DRIVER == AUDIO_ALSA
.audio = &audio_alsa,
#elif AUDIO_DRIVER == AUDIO_RSOUND
.audio = &audio_rsound,
#elif AUDIO_DRIVER == AUDIO_OSS
.audio = &audio_oss,
#elif AUDIO_DRIVER == AUDIO_ROAR
.audio = &audio_roar,
#elif AUDIO_DRIVER == AUDIO_AL
.audio = &audio_openal,
#else
#error "Define a valid audio driver in config.h"
#endif
};
#endif
+4
View File
@@ -34,6 +34,8 @@ enum
SSNES_QUIT_KEY,
SSNES_STATE_SLOT_PLUS,
SSNES_STATE_SLOT_MINUS,
SSNES_AUDIO_INPUT_RATE_PLUS,
SSNES_AUDIO_INPUT_RATE_MINUS,
};
struct snes_keybind
@@ -138,6 +140,8 @@ 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 audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const video_driver_t video_gl;
extern const input_driver_t input_sdl;
////////////////////////////////////////////////
+18 -13
View File
@@ -23,12 +23,21 @@
#include <string.h>
#include "dynamic.h"
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
// Load SNES rom only. Applies a hack for headered ROMs.
static ssize_t read_rom_file(FILE* file, void** buf)
{
ssize_t ret;
if (file == NULL) // stdin
{
#ifdef _WIN32
setmode(0, O_BINARY);
#endif
SSNES_LOG("Reading ROM from stdin ...\n");
size_t buf_size = 0xFFFFF; // Some initial guesstimate.
size_t buf_ptr = 0;
@@ -103,10 +112,7 @@ static ssize_t read_file(const char *path, void **buf)
void *rom_buf = NULL;
FILE *file = fopen(path, "rb");
if (!file)
{
SSNES_ERR("Couldn't open file: \"%s\"\n", path);
goto error;
}
fseek(file, 0, SEEK_END);
long len = ftell(file);
@@ -139,10 +145,7 @@ static bool dump_to_file(const char *path, const void *data, size_t size)
{
FILE *file = fopen(path, "wb");
if (!file)
{
SSNES_ERR("Couldn't dump to file %s\n", path);
return false;
}
else
{
fwrite(data, 1, size, file);
@@ -169,6 +172,8 @@ bool save_state(const char* path)
psnes_serialize(data, size);
bool ret = dump_to_file(path, data, size);
free(data);
if (!ret)
SSNES_ERR("Failed to save state to \"%s\".\n", path);
return ret;
}
@@ -179,7 +184,7 @@ bool load_state(const char* path)
ssize_t size = read_file(path, &buf);
if (size < 0)
{
SSNES_ERR("Failed to load state.\n");
SSNES_ERR("Failed to load state from \"%s\".\n", path);
return false;
}
else
@@ -302,12 +307,12 @@ static bool load_bsx_rom(bool slotted)
if (slotted)
{
if (!psnes_load_cartridge_bsx_slotted(
NULL, rom_buf, rom_len,
NULL, extra_rom_buf, extra_rom_len))
{
SSNES_ERR("Cannot load BSX slotted rom.\n");
goto error;
}
NULL, rom_buf, rom_len,
NULL, extra_rom_buf, extra_rom_len))
{
SSNES_ERR("Cannot load BSX slotted rom.\n");
goto error;
}
}
else
+3 -2
View File
@@ -35,7 +35,7 @@
#define MAX_PLAYERS 5
#define MAX_BINDS 20 // Needs to be increased every time there are new binds added.
#define MAX_BINDS 22 // Needs to be increased every time there are new binds added.
#define SSNES_NO_JOYPAD 0xFFFF
struct settings
{
@@ -67,7 +67,8 @@ struct settings
char driver[32];
bool enable;
unsigned out_rate;
unsigned in_rate;
float in_rate;
float rate_step;
char device[256];
unsigned latency;
bool sync;
-5
View File
@@ -424,14 +424,10 @@ 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;
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);
@@ -446,7 +442,6 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
if (!SDL_SetVideoMode(video->width, video->height, 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0)))
return NULL;
-5
View File
@@ -59,11 +59,6 @@ static void* sdl_input_init(void)
sdl->num_buttons[i] = SDL_JoystickNumButtons(sdl->joysticks[i]);
sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]);
}
else
{
SSNES_WARN("Desired SDL joystick #%u on port %u, but SDL can only detect %u joysticks ...\n",
g_settings.input.joypad_map[i], i + 1, sdl->num_joysticks);
}
}
return sdl;
+2 -1
View File
@@ -21,6 +21,7 @@ check_lib AL -lopenal alcOpenDevice
check_lib RSOUND -lrsound rsd_init
check_lib ROAR -lroar roar_vs_new
check_lib JACK -ljack jack_client_open
check_pkgconf PULSE libpulse
check_pkgconf SDL sdl 1.2.10
check_critical SDL "Cannot find SDL library."
@@ -46,7 +47,7 @@ check_lib DYNAMIC -ldl dlopen
check_pkgconf FREETYPE freetype2
# Creates config.mk and config.h.
VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE"
VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE"
create_config_make config.mk $VARS
create_config_header config.h $VARS
+2 -1
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.2
PACKAGE_VERSION=0.2.1
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -20,4 +20,5 @@ add_command_line_enable RSOUND "Enable RSound support" auto
add_command_line_enable ROAR "Enable RoarAudio support" auto
add_command_line_enable AL "Enable OpenAL support" auto
add_command_line_enable JACK "Enable JACK support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
+25 -5
View File
@@ -69,6 +69,12 @@ static void set_defaults(void)
case AUDIO_SDL:
def_audio = "sdl";
break;
case AUDIO_XAUDIO:
def_audio = "xaudio";
break;
case AUDIO_PULSE:
def_audio = "pulse";
break;
default:
break;
}
@@ -111,6 +117,7 @@ static void set_defaults(void)
g_settings.audio.enable = audio_enable;
g_settings.audio.out_rate = out_rate;
g_settings.audio.in_rate = in_rate;
g_settings.audio.rate_step = audio_rate_step;
if (audio_device)
strncpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device));
g_settings.audio.latency = out_latency;
@@ -129,10 +136,8 @@ static void set_defaults(void)
memcpy(g_settings.input.binds[4], snes_keybinds_5, sizeof(snes_keybinds_5));
g_settings.input.axis_threshold = AXIS_THRESHOLD;
for (int i = 0; i < 2; i++)
for (int i = 0; i < MAX_PLAYERS; i++)
g_settings.input.joypad_map[i] = i;
for (int i = 2; i < MAX_PLAYERS; i++)
g_settings.input.joypad_map[i] = SSNES_NO_JOYPAD;
}
#ifdef HAVE_CONFIGFILE
@@ -319,8 +324,11 @@ static void parse_config_file(void)
if (config_get_int(conf, "audio_out_rate", &tmp_int))
g_settings.audio.out_rate = tmp_int;
if (config_get_int(conf, "audio_in_rate", &tmp_int))
g_settings.audio.in_rate = tmp_int;
if (config_get_double(conf, "audio_in_rate", &tmp_double))
g_settings.audio.in_rate = tmp_double;
if (config_get_double(conf, "audio_rate_step", &tmp_double))
g_settings.audio.rate_step = tmp_double;
if (config_get_string(conf, "audio_device", &tmp_str))
{
@@ -401,6 +409,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
},
{
DECLARE_BIND(player2_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -422,6 +432,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
},
{
DECLARE_BIND(player3_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -443,6 +455,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
},
{
DECLARE_BIND(player4_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -464,6 +478,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
},
{
DECLARE_BIND(player5_a, SNES_DEVICE_ID_JOYPAD_A)
@@ -485,6 +501,8 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
},
};
@@ -511,6 +529,8 @@ static const struct key_map sdlk_map[] = {
{ "alt", SDLK_LALT },
{ "space", SDLK_SPACE },
{ "escape", SDLK_ESCAPE },
{ "kp_plus", SDLK_KP_PLUS },
{ "kp_minus", SDLK_KP_MINUS },
{ "f1", SDLK_F1 },
{ "f2", SDLK_F2 },
{ "f3", SDLK_F3 },
+29 -1
View File
@@ -241,7 +241,7 @@ static void fill_pathname(char *out_path, char *in_path, const char *replace)
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.2"
#define PACKAGE_VERSION "0.2.1"
#endif
#include "config.features.h"
@@ -257,6 +257,8 @@ static void print_features(void)
_PSUPP(jack, "Jack", "audio driver");
_PSUPP(rsound, "RSound", "audio driver");
_PSUPP(roar, "RoarAudio", "audio driver");
_PSUPP(pulse, "PulseAudio", "audio driver");
_PSUPP(xaudio, "XAudio2", "audio driver");
_PSUPP(al, "OpenAL", "audio driver");
_PSUPP(filter, "Filter", "CPU based video filters");
_PSUPP(cg, "Cg", "Cg pixel shaders");
@@ -746,6 +748,31 @@ static void check_stateslots(void)
old_should_slot_decrease = should_slot_decrease;
}
static void check_input_rate(void)
{
bool display = false;
if (driver.input->key_pressed(driver.input_data, SSNES_AUDIO_INPUT_RATE_PLUS))
{
g_settings.audio.in_rate += g_settings.audio.rate_step;
display = true;
}
else if (driver.input->key_pressed(driver.input_data, SSNES_AUDIO_INPUT_RATE_MINUS))
{
g_settings.audio.in_rate -= g_settings.audio.rate_step;
display = true;
}
if (display)
{
char msg[256];
snprintf(msg, sizeof(msg), "Audio input rate: %.2f Hz", g_settings.audio.in_rate);
msg_queue_clear(g_extern.msg_queue);
msg_queue_push(g_extern.msg_queue, msg, 0, 180);
SSNES_LOG("%s\n", msg);
}
}
static void do_state_checks(void)
{
set_fast_forward_button(driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY));
@@ -753,6 +780,7 @@ static void do_state_checks(void)
check_stateslots();
check_savestates();
check_fullscreen();
check_input_rate();
}
+23 -10
View File
@@ -19,7 +19,7 @@
# Video vsync.
# video_vsync = true
# Smoothens picture with bilinear filtering. Should be disabled if using Cg shaders.
# Smoothens picture with bilinear filtering. Should be disabled if using pixel shaders.
# video_smooth = true
# Forces rendering area to stay equal to SNES aspect ratio 4:3 or as defined in video_aspect_ratio.
@@ -28,10 +28,10 @@
# A floating point value for video aspect ratio (width / height)
# video_aspect_ratio = 1.333
# Path to Cg shader. If enabled
# Path to Cg shader.
# 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.
# Path to bSNES-style XML shader (GLSL only). 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.
@@ -43,7 +43,8 @@
# Size of the TTF font rendered.
# video_font_size = 48
# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
# [0.0, 0.0] maps to the lower left corner of the screen.
# video_message_pos_x = 0.05
# video_message_pox_y = 0.05
@@ -61,10 +62,13 @@
# but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz.
# audio_in_rate = 31980
# Audio driver backend. Depending on configuration possible candidates are: alsa, oss, jack, rsound, roar, openal and sdl
# When altering audio_in_rate on-the-fly, define by how much each time.
# audio_rate_step = 0.25
# Audio driver backend. Depending on configuration possible candidates are: alsa, pulse, oss, jack, rsound, roar, openal, sdl and xaudio
# audio_driver =
# Override the default audio device the audio_driver uses.
# Override the default audio device the audio_driver uses. This is driver dependant. E.g. ALSA wants a PCM device, OSS wants a path (e.g. /dev/dsp), Jack wants portnames (e.g. system:playback1,system:playback_2), and so on ...
# audio_device =
# Will sync (block) on audio. Recommended.
@@ -73,7 +77,7 @@
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
# 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.
# libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best respectively.
# audio_src_quality =
### Input
@@ -98,11 +102,14 @@
# input_player1_up = up
# input_player1_down = down
# If desired, it is possible to override which joypads are being used for player 1 and 2. First joypad available is 0.
# If desired, it is possible to override which joypads are being used for player 1 through 5. First joypad available is 0.
# input_player1_joypad_index = 0
# input_player2_joypad_index = 1
# input_player3_joypad_index = 2
# input_player4_joypad_index = 3
# input_player5_joypad_index = 4
# Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output.
# Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output, or use ssnes-joyconfig.
# You can use joypad hats with hnxx, where n is the hat, and xx is a string representing direction.
# E.g. "h0up"
# input_player1_a_btn = 1
@@ -118,7 +125,7 @@
# input_player1_up_btn = 13
# input_player1_down_btn = 14
# Axis for DPAD.
# Axis for SNES DPAD.
# Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number.
# Do note that every other input option has the corresponding _btn and _axis binds as well; they are omitted here for clarity.
# input_player1_left_axis = -0
@@ -159,6 +166,8 @@
# input_player2_up_axis = -1
# input_player2_down_axis = +1
# This goes all the way to player 5, but again omitted for clarity.
# Toggles fullscreen.
# input_toggle_fullscreen = f
# Saves state.
@@ -177,3 +186,7 @@
# Key to exit emulator cleanly.
# Killing it in any hard way (SIGTERM, SIGKILL, etc, will terminate emulator without saving RAM, etc.)
# input_exit_emulator = escape
# Decrease/increase input sample rate on the fly. Amount to decrease/increase is defined by audio_rate_step.
# input_rate_step_up = kp_plus
# input_rate_step_down = kp_minus