Compare commits

...
54 Commits
Author SHA1 Message Date
Themaister d2b06a92c5 0.7 baby! 2011-08-14 23:17:02 +02:00
Themaister 3901934d97 Add an ugly hack to try and remedy things ... 2011-08-14 23:03:48 +02:00
Themaister a76b25aff9 Fix ugly Jack crash with race condition. 2011-08-14 16:29:31 +02:00
Themaister 343852af26 Allocate before activation. 2011-08-14 16:24:42 +02:00
Themaister 4395f124ca Slight fixups. 2011-08-14 04:50:16 +02:00
Themaister b16ff3024c Add new DSP plugin API, events. 2011-08-13 04:09:08 +02:00
Themaister bc23b421e1 New resize checking code. 2011-08-12 19:02:12 +02:00
Themaister d9cb85a8d5 Add new possible workaround for tiling WMs ... 2011-08-11 15:19:30 +02:00
Themaister 7d1cc23d16 Fix build. 2011-08-11 06:02:05 +02:00
Themaister 4496ed3c13 Should make sizes right after startup ... 2011-08-11 05:59:32 +02:00
Themaister 3f5b6a450e Should fix behavior for tiled WMs. 2011-08-11 05:32:03 +02:00
Themaister 9e24474047 Can record hi-res and filtered output. 2011-08-11 05:25:31 +02:00
Themaister 7c17ede1ef Updates for 0.7beta1. 2011-08-09 23:04:13 +02:00
Themaister 5d9805c3e5 Small fix. 2011-08-09 22:07:11 +02:00
Themaister b17617bed5 Updated copyrights. 2011-08-09 12:58:13 +02:00
Themaister a405708f1c Huzzah, ordering matters. 2011-08-09 00:11:09 +02:00
Themaister 594e66704d Allow disabling composition in SDL as well. 2011-08-08 23:38:11 +02:00
Themaister 50579aaeea Floating point. 2011-08-08 17:45:49 +02:00
Themaister 38eb855b48 Works! :D 2011-08-08 17:41:55 +02:00
Themaister 12cc85c8a5 CoreAudio builds. 2011-08-08 17:31:03 +02:00
Themaister 5b12595c85 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-08-08 17:29:06 +02:00
Themaister 3753f0bfca Merge branch 'master' of github.com:Themaister/SSNES 2011-08-08 17:28:32 +02:00
Themaister 14ae5e397a Add CoreAudio driver. Untested. 2011-08-08 17:27:52 +02:00
Themaister 657b4ce8c7 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-08-08 14:47:47 +02:00
Themaister 777e6a5334 Better shader path semantics. 2011-08-07 23:35:54 +02:00
Themaister a2ec78320b Improve behavior a bit. 2011-08-07 22:31:59 +02:00
Themaister 1fb53627ac Make sure to free lib. 2011-08-07 21:18:51 +02:00
Themaister fc2a39cd58 More appropriate renaming. 2011-08-07 21:15:50 +02:00
Themaister 7425e1f879 Allow disabling composition in Win Vista/7Allow disabling composition in Win Vista/7. 2011-08-07 15:00:34 +02:00
Themaister 6a260a57dd Reset frame counter properly. 2011-08-06 03:28:07 +02:00
Themaister dadd794ece Remove useless define. 2011-08-05 19:18:43 +02:00
Themaister 706a3738b5 Abandon POT buffer sizes only for dsound.
Allows more fine tuned latencies.
2011-08-05 19:17:33 +02:00
Themaister 9aef33aabd More fixups in DSound. 2011-08-05 14:01:58 +02:00
Themaister ac4219bcde Small clarification. 2011-08-05 13:39:27 +02:00
Themaister b337856f80 More extensive error checking in dsound. 2011-08-05 13:35:02 +02:00
Themaister 079a0100c0 Remove unneeded call. 2011-08-05 03:06:48 +02:00
Themaister 014c3c90e0 Fix possible race condition. 2011-08-05 02:57:30 +02:00
Themaister 4759e3411f Fix DSound up some more in Windows ... :D 2011-08-05 02:05:32 +02:00
Themaister 4a05e50636 Minor progress. But no audio, hrm. 2011-08-04 19:11:14 +02:00
Themaister 72c070f338 Alter default ordering of audio drivers. 2011-08-04 18:52:00 +02:00
Themaister 776291a137 Start working on DirectSound driver. Untested so far. 2011-08-04 18:45:40 +02:00
Themaister f3cdb95b04 Fix endian issues in OSS driver. 2011-07-26 00:53:24 +02:00
Themaister 033860ceb9 Add -f/--fullscreen command line option. 2011-07-09 08:37:08 +02:00
Themaister ab601ee3dc v0.6.2 2011-07-06 16:54:54 +02:00
Themaister 783ab7acd5 Improve error handling. 2011-07-04 13:33:09 +02:00
Themaister 19a46b1f8b Seems to work in Cg as well. 2011-07-03 15:53:56 +02:00
Themaister 508cfdbda7 Start implementing access to previous texture. 2011-07-03 15:39:35 +02:00
Themaister d81b9ee0a7 Print full compile error in Cg. 2011-07-03 13:41:24 +02:00
Themaister 36c6c21194 Use RSOUND_CFLAGS, etc. 2011-06-29 04:54:30 +02:00
Themaister 1631e940f6 Fix up VSync calls for OSX. 2011-06-29 03:45:52 +02:00
Themaister 2eeb1fa6d1 Fix up VSync calls for OSX. 2011-06-29 03:45:17 +02:00
Themaister 26ac00ad76 Fix incompatible type. 2011-06-29 03:26:21 +02:00
Themaister bafc4f596f Add nonused VSync code for OSX. 2011-06-28 03:20:36 +02:00
Themaister eee89dddb1 Check for SDL 1.3 API and report it as an error. 2011-06-28 02:31:41 +02:00
34 changed files with 1210 additions and 179 deletions

No files matched your search

+3
View File
@@ -3,3 +3,6 @@ Hans-Kristian Arntzen - <[email protected]>
Devin J. Pohly - <[email protected]>
- Joypad axis support
Chris Moeller - <[email protected]>
- CoreAudio audio driver
+7 -1
View File
@@ -40,7 +40,8 @@ endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
LIBS += -lrsound
LIBS += $(RSOUND_LIBS)
DEFINES += $(RSOUND_CFLAGS)
endif
ifeq ($(HAVE_OSS), 1)
@@ -82,6 +83,11 @@ ifeq ($(HAVE_PULSE), 1)
DEFINES += $(PULSE_CFLAGS)
endif
ifeq ($(HAVE_COREAUDIO), 1)
OBJ += audio/coreaudio.o
LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
+8 -1
View File
@@ -13,6 +13,7 @@ HAVE_SDL_IMAGE = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_DSOUND = 1
HAVE_RSOUND = 1
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
@@ -59,6 +60,12 @@ ifeq ($(HAVE_XAUDIO), 1)
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_DSOUND), 1)
OBJ += audio/dsound.o
DEFINES += -DHAVE_DSOUND
LIBS += -ldxguid -ldsound
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
@@ -155,7 +162,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.6.1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
zip -r ssnes-win32-0.7.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+8 -1
View File
@@ -12,6 +12,7 @@ HAVE_SDL = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_DSOUND = 1
HAVE_RSOUND = 0
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
@@ -52,6 +53,12 @@ ifeq ($(HAVE_XAUDIO), 1)
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_DSOUND), 1)
OBJ += audio/dsound.o
DEFINES += -DHAVE_DSOUND
LIBS += -ldxguid -ldsound
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
@@ -142,7 +149,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.6.1.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
zip -r ssnes-win64-0.7.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate
+281
View File
@@ -0,0 +1,281 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
* Copyright (C) 2011 - Chris Moeller
*
* 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 "fifo_buffer.h"
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AUComponent.h>
typedef struct coreaudio
{
pthread_mutex_t lock;
pthread_cond_t cond;
ComponentInstance dev;
bool dev_alive;
fifo_buffer_t *buffer;
bool nonblock;
} coreaudio_t;
static void coreaudio_free(void *data)
{
coreaudio_t *dev = data;
if (!dev)
return;
if (dev->dev_alive)
{
AudioOutputUnitStop(dev->dev);
CloseComponent(dev->dev);
}
if (dev->buffer)
fifo_free(dev->buffer);
pthread_mutex_destroy(&dev->lock);
pthread_cond_destroy(&dev->cond);
free(dev);
}
static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flags,
const AudioTimeStamp *time_stamp, UInt32 bus_number,
UInt32 number_frames, AudioBufferList *io_data)
{
coreaudio_t *dev = userdata;
(void)time_stamp;
(void)bus_number;
(void)number_frames;
if (!io_data)
return noErr;
if (io_data->mNumberBuffers != 1)
return noErr;
unsigned write_avail = io_data->mBuffers[0].mDataByteSize;
void *outbuf = io_data->mBuffers[0].mData;
pthread_mutex_lock(&dev->lock);
if (fifo_read_avail(dev->buffer) < write_avail)
{
*action_flags = kAudioUnitRenderAction_OutputIsSilence;
memset(outbuf, 0, write_avail); // Seems to be needed.
pthread_mutex_unlock(&dev->lock);
pthread_cond_signal(&dev->cond); // Technically possible to deadlock without.
return noErr;
}
fifo_read(dev->buffer, outbuf, write_avail);
pthread_mutex_unlock(&dev->lock);
pthread_cond_signal(&dev->cond);
return noErr;
}
static void* coreaudio_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
coreaudio_t *dev = calloc(1, sizeof(*dev));
if (!dev)
return NULL;
CFRunLoopRef run_loop = NULL;
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
pthread_mutex_init(&dev->lock, NULL);
pthread_cond_init(&dev->cond, NULL);
AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
sizeof(CFRunLoopRef), &run_loop);
ComponentDescription desc = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_HALOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple,
};
Component comp = FindNextComponent(NULL, &desc);
if (comp == NULL)
goto error;
OSStatus res = OpenAComponent(comp, &dev->dev);
if (res != noErr)
goto error;
dev->dev_alive = true;
AudioStreamBasicDescription stream_desc = {
.mSampleRate = rate,
.mBitsPerChannel = sizeof(float) * CHAR_BIT,
.mChannelsPerFrame = 2,
.mBytesPerPacket = 2 * sizeof(float),
.mBytesPerFrame = 2 * sizeof(float),
.mFramesPerPacket = 1,
.mFormatID = kAudioFormatLinearPCM,
.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagIsBigEndian),
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc));
if (res != noErr)
goto error;
AudioStreamBasicDescription real_desc;
UInt32 i_size = sizeof(real_desc);
res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
if (res != noErr)
goto error;
SSNES_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n", (float)real_desc.mSampleRate);
g_settings.audio.out_rate = real_desc.mSampleRate;
if (real_desc.mChannelsPerFrame != stream_desc.mChannelsPerFrame)
goto error;
if (real_desc.mBitsPerChannel != stream_desc.mBitsPerChannel)
goto error;
if (real_desc.mFormatFlags != stream_desc.mFormatFlags)
goto error;
if (real_desc.mFormatID != stream_desc.mFormatID)
goto error;
AudioChannelLayout layout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
.mChannelBitmap = (1 << 2) - 1,
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input, 0, &layout, sizeof(layout));
if (res != noErr)
goto error;
AURenderCallbackStruct cb = {
.inputProc = audio_cb,
.inputProcRefCon = dev,
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &cb, sizeof(cb));
if (res != noErr)
goto error;
res = AudioUnitInitialize(dev->dev);
if (res != noErr)
goto error;
size_t fifo_size = (latency * g_settings.audio.out_rate) / 1000;
fifo_size *= 2 * sizeof(float);
dev->buffer = fifo_new(fifo_size);
if (!dev->buffer)
goto error;
SSNES_LOG("[CoreAudio]: Using buffer size of %u bytes: (latency = %u ms)\n", (unsigned)fifo_size, latency);
res = AudioOutputUnitStart(dev->dev);
if (res != noErr)
goto error;
return dev;
error:
SSNES_ERR("[CoreAudio]: Failed to initialize driver ...\n");
coreaudio_free(dev);
return NULL;
}
static ssize_t coreaudio_write(void* data, const void* buf_, size_t size)
{
coreaudio_t *dev = data;
const uint8_t *buf = buf_;
size_t written = 0;
while (size > 0)
{
pthread_mutex_lock(&dev->lock);
size_t write_avail = fifo_write_avail(dev->buffer);
if (write_avail > size)
write_avail = size;
fifo_write(dev->buffer, buf, write_avail);
buf += write_avail;
written += write_avail;
size -= write_avail;
if (dev->nonblock)
{
pthread_mutex_unlock(&dev->lock);
break;
}
if (write_avail == 0)
pthread_cond_wait(&dev->cond, &dev->lock);
pthread_mutex_unlock(&dev->lock);
}
return written;
}
static bool coreaudio_stop(void *data)
{
coreaudio_t *dev = data;
return AudioOutputUnitStop(dev->dev) == noErr;
}
static void coreaudio_set_nonblock_state(void *data, bool state)
{
coreaudio_t *dev = data;
dev->nonblock = state;
}
static bool coreaudio_start(void *data)
{
coreaudio_t *dev = data;
return AudioOutputUnitStart(dev->dev) == noErr;
}
static bool coreaudio_use_float(void *data)
{
(void)data;
return true;
}
const audio_driver_t audio_coreaudio = {
.init = coreaudio_init,
.write = coreaudio_write,
.stop = coreaudio_stop,
.start = coreaudio_start,
.set_nonblock_state = coreaudio_set_nonblock_state,
.free = coreaudio_free,
.use_float = coreaudio_use_float,
.ident = "coreaudio"
};
+387
View File
@@ -0,0 +1,387 @@
/* 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 <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <dsound.h>
#include "fifo_buffer.h"
#include "general.h"
typedef struct dsound
{
LPDIRECTSOUND ds;
LPDIRECTSOUNDBUFFER dsb;
HANDLE event;
bool nonblock;
fifo_buffer_t *buffer;
CRITICAL_SECTION crit;
volatile bool thread_alive;
HANDLE thread;
unsigned buffer_size;
} dsound_t;
static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_size)
{
return (read_ptr + buffer_size - write_ptr) % buffer_size;
}
static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr)
{
IDirectSoundBuffer_GetCurrentPosition(ds->dsb, read_ptr, write_ptr);
}
#define CHUNK_SIZE 256
struct audio_lock
{
void *chunk1;
DWORD size1;
void *chunk2;
DWORD size2;
};
static inline bool grab_region(dsound_t *ds, DWORD write_ptr, struct audio_lock *region)
{
HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &region->chunk1, &region->size1, &region->chunk2, &region->size2, 0);
if (res == DSERR_BUFFERLOST)
{
res = IDirectSoundBuffer_Restore(ds->dsb);
if (res != DS_OK)
return false;
res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &region->chunk1, &region->size1, &region->chunk2, &region->size2, 0);
if (res != DS_OK)
return false;
}
const char *err;
switch (res)
{
case DSERR_BUFFERLOST:
err = "DSERR_BUFFERLOST";
break;
case DSERR_INVALIDCALL:
err = "DSERR_INVALIDCALL";
break;
case DSERR_INVALIDPARAM:
err = "DSERR_INVALIDPARAM";
break;
case DSERR_PRIOLEVELNEEDED:
err = "DSERR_PRIOLEVELNEEDED";
break;
default:
err = NULL;
}
if (err)
{
SSNES_WARN("[DirectSound error]: %s\n", err);
return false;
}
return true;
}
static inline void release_region(dsound_t *ds, const struct audio_lock *region)
{
IDirectSoundBuffer_Unlock(ds->dsb, region->chunk1, region->size1, region->chunk2, region->size2);
}
static DWORD CALLBACK dsound_thread(PVOID data)
{
dsound_t *ds = data;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
DWORD write_ptr;
get_positions(ds, NULL, &write_ptr);
write_ptr = (write_ptr + ds->buffer_size / 2) % ds->buffer_size;
while (ds->thread_alive)
{
DWORD read_ptr;
get_positions(ds, &read_ptr, NULL);
DWORD avail = write_avail(read_ptr, write_ptr, ds->buffer_size);
EnterCriticalSection(&ds->crit);
DWORD fifo_avail = fifo_read_avail(ds->buffer);
LeaveCriticalSection(&ds->crit);
// No space to write, or we don't have data in our fifo, but we can wait some time before it underruns ...
if (avail < CHUNK_SIZE || ((fifo_avail < CHUNK_SIZE) && (avail < ds->buffer_size / 2)))
{
Sleep(1);
// We could opt for using the notification interface,
// but it is not guaranteed to work, so use high priority sleeping patterns. :(
}
else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence.
{
struct audio_lock region;
if (!grab_region(ds, write_ptr, &region))
{
ds->thread_alive = false;
SetEvent(ds->event);
break;
}
memset(region.chunk1, 0, region.size1);
memset(region.chunk2, 0, region.size2);
release_region(ds, &region);
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
}
else // All is good. Pull from it and notify FIFO :D
{
struct audio_lock region;
if (!grab_region(ds, write_ptr, &region))
{
ds->thread_alive = false;
SetEvent(ds->event);
break;
}
EnterCriticalSection(&ds->crit);
if (region.chunk1)
fifo_read(ds->buffer, region.chunk1, region.size1);
if (region.chunk2)
fifo_read(ds->buffer, region.chunk2, region.size2);
LeaveCriticalSection(&ds->crit);
release_region(ds, &region);
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
SetEvent(ds->event);
}
}
ExitThread(0);
}
static void dsound_stop_thread(dsound_t *ds)
{
if (ds->thread)
{
ds->thread_alive = false;
WaitForSingleObject(ds->thread, INFINITE);
CloseHandle(ds->thread);
ds->thread = NULL;
}
}
static bool dsound_start_thread(dsound_t *ds)
{
if (!ds->thread)
{
ds->thread_alive = true;
ds->thread = CreateThread(NULL, 0, dsound_thread, ds, 0, NULL);
if (ds->thread == NULL)
return false;
}
return true;
}
static void dsound_clear_buffer(dsound_t *ds)
{
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
void *ptr;
DWORD size;
if (IDirectSoundBuffer_Lock(ds->dsb, 0, 0, &ptr, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER) == DS_OK)
{
memset(ptr, 0, size);
IDirectSoundBuffer_Unlock(ds->dsb, ptr, size, NULL, 0);
}
}
static void dsound_free(void *data)
{
dsound_t *ds = data;
if (ds)
{
if (ds->thread)
{
ds->thread_alive = false;
WaitForSingleObject(ds->thread, INFINITE);
CloseHandle(ds->thread);
}
DeleteCriticalSection(&ds->crit);
if (ds->dsb)
{
IDirectSoundBuffer_Stop(ds->dsb);
IDirectSoundBuffer_Release(ds->dsb);
}
if (ds)
IDirectSound_Release(ds->ds);
if (ds->event)
CloseHandle(ds->event);
if (ds->buffer)
fifo_free(ds->buffer);
free(ds);
}
}
static void* dsound_init(const char *device, unsigned rate, unsigned latency)
{
dsound_t *ds = calloc(1, sizeof(*ds));
if (!ds)
goto error;
InitializeCriticalSection(&ds->crit);
if (DirectSoundCreate(NULL, &ds->ds, NULL) != DS_OK)
goto error;
if (IDirectSound_SetCooperativeLevel(ds->ds, GetDesktopWindow(), DSSCL_PRIORITY) != DS_OK)
goto error;
WAVEFORMATEX wfx = {
.wFormatTag = WAVE_FORMAT_PCM,
.nChannels = 2,
.nSamplesPerSec = rate,
.wBitsPerSample = 16,
.nBlockAlign = 2 * sizeof(int16_t),
.nAvgBytesPerSec = rate * 2 * sizeof(int16_t),
};
ds->buffer_size = (latency * wfx.nAvgBytesPerSec) / 1000;
ds->buffer_size /= CHUNK_SIZE;
ds->buffer_size *= CHUNK_SIZE;
if (ds->buffer_size < 4 * CHUNK_SIZE)
ds->buffer_size = 4 * CHUNK_SIZE;
SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size);
SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec));
DSBUFFERDESC bufdesc = {
.dwSize = sizeof(DSBUFFERDESC),
.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS,
.dwBufferBytes = ds->buffer_size,
.lpwfxFormat = &wfx,
};
ds->event = CreateEvent(NULL, false, false, NULL);
if (!ds->event)
goto error;
ds->buffer = fifo_new(4 * 1024);
if (!ds->buffer)
goto error;
if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK)
goto error;
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
dsound_clear_buffer(ds);
if (IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) != DS_OK)
goto error;
if (!dsound_start_thread(ds))
goto error;
return ds;
error:
SSNES_ERR("[DirectSound] Error occured in init!\n");
dsound_free(ds);
return NULL;
}
static bool dsound_stop(void *data)
{
dsound_t *ds = data;
dsound_stop_thread(ds);
return IDirectSoundBuffer_Stop(ds->dsb) == DS_OK;
}
static bool dsound_start(void *data)
{
dsound_t *ds = data;
dsound_clear_buffer(ds);
if (!dsound_start_thread(ds))
return false;
return IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) == DS_OK;
}
static void dsound_set_nonblock_state(void *data, bool state)
{
dsound_t *ds = data;
ds->nonblock = state;
}
static ssize_t dsound_write(void *data, const void *buf_, size_t size)
{
dsound_t *ds = data;
const uint8_t *buf = buf_;
if (!ds->thread_alive)
return -1;
size_t written = 0;
while (size > 0)
{
EnterCriticalSection(&ds->crit);
size_t avail = fifo_write_avail(ds->buffer);
if (avail > size)
avail = size;
fifo_write(ds->buffer, buf, avail);
LeaveCriticalSection(&ds->crit);
buf += avail;
size -= avail;
written += avail;
if (ds->nonblock || !ds->thread_alive)
break;
if (avail == 0)
WaitForSingleObject(ds->event, INFINITE);
}
return written;
}
const audio_driver_t audio_dsound = {
.init = dsound_init,
.write = dsound_write,
.stop = dsound_stop,
.start = dsound_start,
.set_nonblock_state = dsound_set_nonblock_state,
.free = dsound_free,
.ident = "dsound"
};
+7 -2
View File
@@ -30,7 +30,7 @@ extern "C" {
#define SSNES_TRUE 1
#endif
#define SSNES_DSP_API_VERSION 2
#define SSNES_DSP_API_VERSION 3
typedef struct ssnes_dsp_info
{
@@ -94,7 +94,7 @@ typedef struct ssnes_dsp_plugin
void (*process)(void *data, ssnes_dsp_output_t *output,
const ssnes_dsp_input_t *input);
// Frees the handle.
// Frees the handle.
void (*free)(void *data);
// API version used to compile the plugin.
@@ -110,6 +110,11 @@ typedef struct ssnes_dsp_plugin
// Human readable identification string.
const char *ident;
// Called every frame, allows creating a GUI main loop in the main thread.
// GUI events can be processed here in a non-blocking fashion.
// Can be set to NULL to ignore it.
void (*events)(void *data);
} ssnes_dsp_plugin_t;
// Called by SSNES at startup to get the callback struct.
+17 -18
View File
@@ -127,9 +127,12 @@ static size_t find_buffersize(jack_t *jd, int latency)
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
{
jack_t *jd = calloc(1, sizeof(jack_t));
if ( jd == NULL )
if (!jd)
return NULL;
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
const char **jports = NULL;
jd->client = jack_client_open("SSNES", JackNullOption, NULL);
@@ -148,7 +151,6 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
SSNES_ERR("Failed to register ports.\n");
goto error;
}
char *dest_ports[2];
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
@@ -158,6 +160,18 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
goto error;
}
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
int parsed = parse_ports(dest_ports, jports);
if (jack_activate(jd->client) < 0)
@@ -178,22 +192,7 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
for (int i = 0; i < parsed; i++)
free(dest_ports[i]);
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
jack_free(jports);
return jd;
+3 -1
View File
@@ -20,6 +20,7 @@
#endif
#include "driver.h"
#include "general.h"
#include <stdlib.h>
#ifdef HAVE_OSS_BSD
@@ -68,7 +69,8 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency)
}
int channels = 2;
int format = AFMT_S16_LE;
int format = is_little_endian() ?
AFMT_S16_LE : AFMT_S16_BE;
if (ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
{
-12
View File
@@ -59,18 +59,6 @@ static void __pulse_free(void *data)
}
}
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;
-5
View File
@@ -196,9 +196,4 @@ const audio_driver_t audio_sdl = {
.free = sdl_audio_free,
.ident = "sdl"
};
+15
View File
@@ -57,6 +57,8 @@
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
#define AUDIO_EXT 15
#define AUDIO_DSOUND 16
#define AUDIO_COREAUDIO 17
////////////////////////
#define INPUT_SDL 7
#define INPUT_X 12
@@ -82,8 +84,12 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif defined(HAVE_JACK)
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
#elif defined(HAVE_COREAUDIO)
#define AUDIO_DEFAULT_DRIVER AUDIO_COREAUDIO
#elif defined(HAVE_AL)
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif defined(HAVE_DSOUND)
#define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND
#elif defined(HAVE_SDL)
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#elif defined(HAVE_XAUDIO)
@@ -123,6 +129,9 @@ static const unsigned fullscreen_y = 0;
// Force 16-bit colors.
static const bool force_16bit = false;
// Forcibly disable composition. Only valid on Windows Vista/7 for now.
static const bool disable_composition = false;
// Video VSYNC (recommended)
static const bool vsync = true;
@@ -148,6 +157,12 @@ static const float fbo_scale_x = 2.0;
static const float fbo_scale_y = 2.0;
static const bool second_pass_smooth = true;
// Record video assuming game runs hi-res.
static const bool hires_record = false;
// Record post-filtered (CPU filter) video rather than raw SNES output.
static const bool post_filter_record = false;
#define SNES_ASPECT_RATIO (4.0/3)
////////////////
+4
View File
@@ -40,6 +40,10 @@ Without this flag, the save state path will be inferred from the rom path name,
When rom is loaded from \fBstdin\fR, this flag is mandatory to define as no path can be inferred.
Do note that save states are bound to the libsnes implementation being used. Using a different libsnes could invalidate the save state file.
.TP
\fB--fullscreen, -f\fR
Always starts SSNES in fullscreen. Disregards settings in configuration file.
.TP
\fB--config PATH, -c PATH\fR
Sets the configuration file path. \fBssnes\fR will use this path to load the configuration file.
+11 -5
View File
@@ -38,6 +38,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_RSOUND
&audio_rsound,
#endif
#ifdef HAVE_COREAUDIO
&audio_coreaudio,
#endif
#ifdef HAVE_AL
&audio_openal,
#endif
@@ -53,6 +56,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_XAUDIO
&audio_xa,
#endif
#ifdef HAVE_DSOUND
&audio_dsound,
#endif
#ifdef HAVE_PULSE
&audio_pulse,
#endif
@@ -162,7 +168,7 @@ static void init_dsp_plugin(void)
}
const ssnes_dsp_plugin_t* (*SSNES_API_CALLTYPE plugin_init)(void) =
(const ssnes_dsp_plugin_t *(*)(void))dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
(const ssnes_dsp_plugin_t *(SSNES_API_CALLTYPE*)(void))dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
if (!plugin_init)
{
SSNES_ERR("Failed to find symbol \"ssnes_dsp_plugin_init\" in DSP plugin.\n");
@@ -184,7 +190,7 @@ static void init_dsp_plugin(void)
SSNES_LOG("Loaded DSP plugin: \"%s\"\n", g_extern.audio_data.dsp_plugin->ident ? g_extern.audio_data.dsp_plugin->ident : "Unknown");
ssnes_dsp_info_t info = {
const ssnes_dsp_info_t info = {
.input_rate = g_settings.audio.in_rate,
.output_rate = g_settings.audio.out_rate
};
@@ -479,12 +485,12 @@ void init_video_input(void)
void uninit_video_input(void)
{
if ( driver.video_data && driver.video )
driver.video->free(driver.video_data);
if ( driver.input_data != driver.video_data && driver.input )
driver.input->free(driver.input_data);
if ( driver.video_data && driver.video )
driver.video->free(driver.video_data);
deinit_filter();
#ifdef HAVE_XML
+2
View File
@@ -158,6 +158,8 @@ extern const audio_driver_t audio_sdl;
extern const audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const audio_driver_t audio_ext;
extern const audio_driver_t audio_dsound;
extern const audio_driver_t audio_coreaudio;
extern const video_driver_t video_gl;
extern const video_driver_t video_xvideo;
extern const video_driver_t video_sdl;
+17
View File
@@ -89,6 +89,10 @@ struct settings
float msg_pos_y;
bool force_16bit;
bool disable_composition;
bool hires_record;
bool post_filter_record;
char external_driver[256];
} video;
@@ -147,6 +151,7 @@ struct global
bool verbose;
bool audio_active;
bool video_active;
bool force_fullscreen;
bool has_mouse[2];
bool has_scope[2];
@@ -304,6 +309,18 @@ static inline uint32_t next_pow2(uint32_t v)
return v;
}
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t x;
uint8_t y[2];
} u;
u.x = 1;
return u.y[0];
}
#endif
+11 -2
View File
@@ -224,6 +224,15 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
return false;
}
const char *cg_shader = NULL;
const char *xml_shader = NULL;
enum ssnes_shader_type type = g_settings.video.shader_type;
if ((type == SSNES_SHADER_CG || type == SSNES_SHADER_AUTO) && *g_settings.video.cg_shader_path)
cg_shader = g_settings.video.cg_shader_path;
else if ((type == SSNES_SHADER_BSNES || type == SSNES_SHADER_AUTO) && *g_settings.video.bsnes_shader_path)
xml_shader = g_settings.video.bsnes_shader_path;
ssnes_video_info_t info = {
.width = video->width,
.height = video->height,
@@ -234,8 +243,8 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
.smooth = video->smooth,
.input_scale = video->input_scale,
.color_format = video->rgb32 ? SSNES_COLOR_FORMAT_ARGB8888 : SSNES_COLOR_FORMAT_XRGB1555,
.xml_shader = g_settings.video.bsnes_shader_path,
.cg_shader = g_settings.video.cg_shader_path,
.xml_shader = xml_shader,
.cg_shader = cg_shader,
.ttf_font = *g_settings.video.font_path ? g_settings.video.font_path : NULL,
.ttf_font_size = g_settings.video.font_size
};
+91 -6
View File
@@ -25,21 +25,26 @@ static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, i
return frames/time;
}
static unsigned gl_frames = 0;
void gfx_window_title_reset(void)
{
gl_frames = 0;
}
bool gfx_window_title(char *buf, size_t size)
{
static int frames = 0;
static struct timeval tv;
struct timeval new_tv;
bool ret = false;
if (frames == 0)
if (gl_frames == 0)
{
gettimeofday(&tv, NULL);
snprintf(buf, size, "%s", g_extern.title_buf);
ret = true;
}
if ((frames % 180) == 0 && frames > 0)
else if ((gl_frames % 180) == 0)
{
gettimeofday(&new_tv, NULL);
struct timeval tmp_tv = tv;
@@ -47,12 +52,92 @@ bool gfx_window_title(char *buf, size_t size)
float fps = tv_to_fps(&tmp_tv, &new_tv, 180);
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, frames);
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, gl_frames);
ret = true;
}
frames++;
gl_frames++;
return ret;
}
#ifdef _WIN32
#include <windows.h>
#include "dynamic.h"
// We only load this library once, so we let it be unloaded at application shutdown,
// since unloading it early seems to cause issues on some systems.
static dylib_t dwmlib = NULL;
static void gfx_dwm_shutdown(void)
{
if (dwmlib)
dylib_close(dwmlib);
}
void gfx_set_dwm(void)
{
static bool inited = false;
if (inited)
return;
inited = true;
dwmlib = dylib_load("dwmapi.dll");
if (!dwmlib)
{
SSNES_LOG("Did not find dwmapi.dll");
return;
}
atexit(gfx_dwm_shutdown);
HRESULT (WINAPI *mmcss)(BOOL) = (HRESULT (WINAPI*)(BOOL))dylib_proc(dwmlib, "DwmEnableMMCSS");
if (mmcss)
{
SSNES_LOG("Setting multimedia scheduling for DWM.\n");
mmcss(TRUE);
}
if (!g_settings.video.disable_composition)
return;
HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(dwmlib, "DwmEnableComposition");
if (!composition_enable)
{
SSNES_ERR("Did not find DwmEnableComposition ...\n");
return;
}
HRESULT ret = composition_enable(0);
if (FAILED(ret))
SSNES_ERR("Failed to set composition state ...\n");
}
#endif
#include "SDL_syswm.h"
#include "SDL.h"
void gfx_get_window_size(unsigned *width, unsigned *height)
{
#if defined(__APPLE__) || defined(_WIN32)
SDL_Event evnt;
while (SDL_PollEvent(&evnt));
const SDL_VideoInfo *info = SDL_GetVideoInfo();
*width = info->current_w;
*height = info->current_h;
#else
// It seems that we need to go hardcore to get the actual
// window sizes properly right after startup ... :D
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWMInfo(&info);
XWindowAttributes target;
info.info.x11.lock_func();
XGetWindowAttributes(info.info.x11.display, info.info.x11.window,
&target);
info.info.x11.unlock_func();
*width = target.width;
*height = target.height;
#endif
}
+7
View File
@@ -22,5 +22,12 @@
#include <stdbool.h>
bool gfx_window_title(char *buf, size_t size);
void gfx_window_title_reset(void);
#ifdef _WIN32
void gfx_set_dwm(void);
#endif
void gfx_get_window_size(unsigned *width, unsigned *height);
#endif
+97 -42
View File
@@ -125,7 +125,9 @@ static inline bool load_gl_proc(void) { return true; }
typedef struct gl
{
bool vsync;
GLuint texture;
GLuint texture[2];
unsigned tex_index; // For use with PREV.
struct gl_tex_info prev_info;
GLuint tex_filter;
void *empty_buf;
@@ -153,8 +155,8 @@ typedef struct gl
unsigned win_height;
unsigned vp_width, vp_out_width;
unsigned vp_height, vp_out_height;
unsigned last_width;
unsigned last_height;
unsigned last_width[2];
unsigned last_height[2];
unsigned tex_w, tex_h;
GLfloat tex_coords[8];
#ifdef HAVE_FBO
@@ -255,20 +257,21 @@ static void gl_shader_set_params(unsigned width, unsigned height,
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt)
{
#ifdef HAVE_CG
gl_cg_set_params(width, height,
tex_width, tex_height,
out_width, out_height,
frame_count, info, fbo_info, fbo_info_cnt);
frame_count, info, prev_info, fbo_info, fbo_info_cnt);
#endif
#ifdef HAVE_XML
gl_glsl_set_params(width, height,
tex_width, tex_height,
out_width, out_height,
frame_count, info, fbo_info, fbo_info_cnt);
frame_count, info, prev_info, fbo_info, fbo_info_cnt);
#endif
}
@@ -338,7 +341,7 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
}
else
SSNES_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path);
@@ -653,7 +656,7 @@ static void gl_render_msg(gl_t *gl, const char *msg)
// Go back to old rendering path.
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
glDisable(GL_BLEND);
#endif
}
@@ -702,6 +705,19 @@ static void check_window(gl_t *gl)
break;
}
}
if (!gl->should_resize)
{
unsigned new_width, new_height;
gfx_get_window_size(&new_width, &new_height);
if ((new_width != gl->win_width || new_height != gl->win_height) || (gl->frame_count == 10)) // Ugly hack :D
{
gl->should_resize = true;
gl->win_width = new_width;
gl->win_height = new_height;
SSNES_LOG("GL: Verified window size: %u x %u\n", gl->win_width, gl->win_height);
}
}
}
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
@@ -712,6 +728,10 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
gl_shader_use(1);
gl->frame_count++;
#if defined(HAVE_XML) || defined(HAVE_CG)
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
#endif
#ifdef HAVE_FBO
// Render to texture in first pass.
if (gl->fbo_inited)
@@ -767,7 +787,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
last_max_height = gl->fbo_rect[i].max_img_height;
}
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]);
gl->render_to_tex = true;
set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true);
@@ -814,7 +834,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
}
// Go back to what we're supposed to do, render to FBO #0 :D
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]);
set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true);
}
@@ -823,10 +843,10 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
#endif
}
if ((width != gl->last_width || height != gl->last_height) && gl->empty_buf) // Res change. need to clear out texture.
if ((width != gl->last_width[gl->tex_index] || height != gl->last_height[gl->tex_index]) && gl->empty_buf) // Res change. need to clear out texture.
{
gl->last_width = width;
gl->last_height = height;
gl->last_width[gl->tex_index] = width;
gl->last_height[gl->tex_index] = height;
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(pitch));
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w);
@@ -839,13 +859,13 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
set_texture_coords(gl->tex_coords, xamt, yamt);
}
// Work around a certain issue a Cg where not using TEXUNIT0
// in shader causes cgGLEnableTextureParameter() causes it
// to bind to TEXUNIT0, to avoid really funny bugs, rebind
// our texture.
#ifdef HAVE_CG
glBindTexture(GL_TEXTURE_2D, gl->texture);
#if defined(HAVE_XML) || defined(HAVE_CG)
else if (width != gl->last_width[1 - gl->tex_index] || height != gl->last_height[1 - gl->tex_index])
{
GLfloat xamt = (GLfloat)width / gl->tex_w;
GLfloat yamt = (GLfloat)height / gl->tex_h;
set_texture_coords(gl->tex_coords, xamt, yamt);
}
#endif
#ifdef HAVE_FBO
@@ -861,7 +881,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
gl->texture_fmt, frame);
struct gl_tex_info tex_info = {
.tex = gl->texture,
.tex = gl->texture[gl->tex_index],
.input_size = {width, height},
.tex_size = {gl->tex_w, gl->tex_h}
};
@@ -871,7 +891,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
glClear(GL_COLOR_BUFFER_BIT);
gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height, gl->frame_count,
&tex_info, fbo_tex_info, fbo_tex_info_cnt);
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
glDrawArrays(GL_QUADS, 0, 4);
@@ -916,7 +936,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height,
prev_rect->width, prev_rect->height,
gl->vp_width, gl->vp_height, gl->frame_count,
&tex_info, fbo_tex_info, fbo_tex_info_cnt);
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
glDrawArrays(GL_QUADS, 0, 4);
@@ -942,7 +962,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height,
prev_rect->width, prev_rect->height,
gl->vp_width, gl->vp_height, gl->frame_count,
&tex_info, fbo_tex_info, fbo_tex_info_cnt);
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glDrawArrays(GL_QUADS, 0, 4);
@@ -951,6 +971,11 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
}
#endif
#if defined(HAVE_XML) || defined(HAVE_CG)
memcpy(&gl->prev_info, &tex_info, sizeof(tex_info));
gl->tex_index = 1 - gl->tex_index;
#endif
if (msg)
gl_render_msg(gl, msg);
@@ -970,7 +995,7 @@ static void gl_free(void *data)
gl_shader_deinit();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDeleteTextures(1, &gl->texture);
glDeleteTextures(2, gl->texture);
#ifdef HAVE_FBO
if (gl->fbo_inited)
@@ -987,19 +1012,26 @@ static void gl_free(void *data)
free(gl);
}
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#endif
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");
#ifdef _WIN32
#if defined(_WIN32)
static BOOL (APIENTRY *wgl_swap_interval)(int) = NULL;
if (!wgl_swap_interval)
{
SDL_SYM_WRAP(wgl_swap_interval, "wglSwapIntervalEXT");
}
if (wgl_swap_interval) wgl_swap_interval(state ? 0 : 1);
#elif defined(__APPLE__)
GLint val = state ? 0 : 1;
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &val);
#else
static int (*glx_swap_interval)(int) = NULL;
if (!glx_swap_interval)
@@ -1017,6 +1049,10 @@ static void gl_set_nonblock_state(void *data, bool state)
static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
#ifdef _WIN32
gfx_set_dwm();
#endif
if (SDL_Init(SDL_INIT_VIDEO) < 0)
return NULL;
@@ -1032,6 +1068,11 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
if (!SDL_SetVideoMode(video->width, video->height, g_settings.video.force_16bit ? 16 : 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0)))
return NULL;
gfx_window_title_reset();
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
// Remove that ugly mouse :D
SDL_ShowCursor(SDL_DISABLE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -1119,22 +1160,21 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
glColor4f(1, 1, 1, 1);
glClearColor(0, 0, 0, 1);
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGenTextures(1, &gl->texture);
glGenTextures(2, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture);
for (unsigned i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
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_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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -1148,15 +1188,30 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
gl->tex_w = 256 * video->input_scale;
gl->tex_h = 256 * video->input_scale;
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type,
gl->texture_fmt, NULL);
// Empty buffer that we use to clear out the texture with on res change.
gl->empty_buf = calloc(gl->base_size, gl->tex_w * gl->tex_h);
gl->last_width = gl->tex_w;
gl->last_height = gl->tex_h;
for (unsigned i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type,
gl->texture_fmt, gl->empty_buf ? gl->empty_buf : NULL);
}
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
for (unsigned i = 0; i < 2; i++)
{
gl->last_width[i] = gl->tex_w;
gl->last_height[i] = gl->tex_h;
}
gl->prev_info.tex = gl->texture[1 - gl->tex_index];
gl->prev_info.input_size[0] = gl->tex_w;
gl->prev_info.tex_size[0] = gl->tex_w;
gl->prev_info.input_size[1] = gl->tex_h;
gl->prev_info.tex_size[1] = gl->tex_h;
memcpy(gl->prev_info.coord, tex_coords, sizeof(tex_coords));
// Hook up SDL input driver to get SDL_QUIT events and RESIZE.
sdl_input_t *sdl_input = input_sdl.init();
@@ -1215,7 +1270,7 @@ static bool gl_xml_shader(void *data, const char *path)
gl->render_to_tex = false;
gl->fbo_pass = 0;
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
}
#endif
+3 -3
View File
@@ -101,9 +101,9 @@ struct gl_fbo_scale
struct gl_tex_info
{
GLuint tex;
float input_size[2];
float tex_size[2];
float coord[8];
GLfloat input_size[2];
GLfloat tex_size[2];
GLfloat coord[8];
};
// Not legal to cast void* to fn-pointer. Need dirty hack to be compilant.
+4
View File
@@ -213,6 +213,10 @@ static void sdl_render_msg_32(sdl_video_t *vid, SDL_Surface *buffer, const char
static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
#ifdef _WIN32
gfx_set_dwm();
#endif
SDL_InitSubSystem(SDL_INIT_VIDEO);
sdl_video_t *vid = calloc(1, sizeof(*vid));
+58 -8
View File
@@ -36,18 +36,15 @@ static const char* stock_cg_program =
"void main_vertex"
"("
" float4 position : POSITION,"
" float4 color : COLOR,"
" float2 texCoord : TEXCOORD0,"
""
" uniform float4x4 modelViewProj,"
""
" out float4 oPosition : POSITION,"
" out float4 oColor : COLOR,"
" out float2 otexCoord : TEXCOORD0"
")"
"{"
" oPosition = mul(modelViewProj, position);"
" oColor = color;"
" otexCoord = texCoord;"
"}"
""
@@ -112,6 +109,7 @@ struct cg_program
struct cg_fbo_params fbo[MAX_SHADERS];
struct cg_fbo_params orig;
struct cg_fbo_params prev;
};
#define FILTER_UNSPEC 0
@@ -148,6 +146,7 @@ void gl_cg_set_params(unsigned width, unsigned height,
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info,
unsigned fbo_info_cnt)
{
@@ -169,7 +168,6 @@ void gl_cg_set_params(unsigned width, unsigned height,
if (param)
{
cgGLSetTextureParameter(param, info->tex);
//fprintf(stderr, "ORIGtex = (%d) %d\n", cgGLGetTextureParameter(param), cgGLGetTextureEnum(param) - GL_TEXTURE0);
cgGLEnableTextureParameter(param);
}
@@ -183,6 +181,24 @@ void gl_cg_set_params(unsigned width, unsigned height,
cgGLEnableClientState(prg[active_index].orig.coord);
}
// Set prev texture
param = prg[active_index].prev.tex;
if (param)
{
cgGLSetTextureParameter(param, prev_info->tex);
cgGLEnableTextureParameter(param);
}
set_param_2f(prg[active_index].prev.vid_size_v, prev_info->input_size[0], prev_info->input_size[1]);
set_param_2f(prg[active_index].prev.vid_size_f, prev_info->input_size[0], prev_info->input_size[1]);
set_param_2f(prg[active_index].prev.tex_size_v, prev_info->tex_size[0], prev_info->tex_size[1]);
set_param_2f(prg[active_index].prev.tex_size_f, prev_info->tex_size[0], prev_info->tex_size[1]);
if (prg[active_index].prev.coord)
{
cgGLSetParameterPointer(prg[active_index].prev.coord, 2, GL_FLOAT, 0, prev_info->coord);
cgGLEnableClientState(prg[active_index].prev.coord);
}
// Set lookup textures.
for (unsigned i = 0; i < lut_textures_num; i++)
{
@@ -191,7 +207,6 @@ void gl_cg_set_params(unsigned width, unsigned height,
{
cgGLSetTextureParameter(param, lut_textures[i]);
cgGLEnableTextureParameter(param);
//fprintf(stderr, "LUTtex = (%d) %d\n", cgGLGetTextureParameter(param), cgGLGetTextureEnum(param) - GL_TEXTURE0);
}
}
@@ -273,16 +288,31 @@ static bool load_plain(const char *path)
if (strlen(g_settings.video.second_pass_shader) > 0)
SSNES_LOG("Loading 2nd pass: %s\n", g_settings.video.second_pass_shader);
char *listing[3] = {NULL};
const char *list = NULL;
prg[0].fprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgFProf, "main_fragment", 0);
prg[0].vprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgVProf, "main_vertex", 0);
list = cgGetLastListing(cgCtx);
if (list)
listing[0] = strdup(list);
prg[1].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgFProf, "main_fragment", 0);
prg[1].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgVProf, "main_vertex", 0);
list = cgGetLastListing(cgCtx);
if (list)
listing[1] = strdup(list);
if (strlen(g_settings.video.second_pass_shader) > 0)
{
prg[2].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgFProf, "main_fragment", 0);
prg[2].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgVProf, "main_vertex", 0);
list = cgGetLastListing(cgCtx);
if (list)
listing[2] = strdup(list);
cg_shader_num = 2;
}
else
@@ -291,13 +321,15 @@ static bool load_plain(const char *path)
cg_shader_num = 1;
}
for (int i = 0; i < cg_shader_num + 1; i++)
for (unsigned i = 0; i <= cg_shader_num; i++)
{
if (!prg[i].fprg || !prg[i].vprg)
{
CGerror err = cgGetError();
SSNES_ERR("CG error: %s\n", cgGetErrorString(err));
return false;
if (listing[i])
SSNES_ERR("%s\n", listing[i]);
goto error;
}
cgGLLoadProgram(prg[i].fprg);
@@ -305,6 +337,14 @@ static bool load_plain(const char *path)
}
return true;
error:
for (unsigned i = 0; i < 3; i++)
{
if (listing[i])
free(listing[i]);
}
return false;
}
#define print_buf(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
@@ -802,8 +842,11 @@ static bool load_preset(const char *path)
if (!prog->fprg || !prog->vprg)
{
const char *listing = cgGetLastListing(cgCtx);
CGerror err = cgGetError();
SSNES_ERR("CG error: %s\n", cgGetErrorString(err));
if (listing)
SSNES_ERR("%s\n", listing);
goto error;
}
@@ -878,7 +921,7 @@ bool gl_cg_init(const char *path)
if (prg[0].mvp)
cgGLSetStateMatrixParameter(prg[0].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
for (unsigned i = 1; i < cg_shader_num + 1; i++)
for (unsigned i = 1; i <= cg_shader_num; i++)
{
cgGLBindProgram(prg[i].fprg);
cgGLBindProgram(prg[i].vprg);
@@ -902,6 +945,13 @@ bool gl_cg_init(const char *path)
prg[i].orig.tex_size_f = cgGetNamedParameter(prg[i].fprg, "ORIG.texture_size");
prg[i].orig.coord = cgGetNamedParameter(prg[i].vprg, "ORIG.tex_coord");
prg[i].prev.tex = cgGetNamedParameter(prg[i].fprg, "PREV.texture");
prg[i].prev.vid_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.video_size");
prg[i].prev.vid_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.video_size");
prg[i].prev.tex_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.texture_size");
prg[i].prev.tex_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.texture_size");
prg[i].prev.coord = cgGetNamedParameter(prg[i].vprg, "PREV.tex_coord");
for (unsigned j = 0; j < i - 1; j++)
{
char attr_buf[64];
+1
View File
@@ -33,6 +33,7 @@ void gl_cg_set_params(unsigned width, unsigned height,
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt);
void gl_cg_use(unsigned index);
+38 -10
View File
@@ -712,8 +712,8 @@ error:
static void print_shader_log(GLuint obj)
{
int info_len = 0;
int max_len;
GLint info_len = 0;
GLint max_len;
pglGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len);
@@ -726,8 +726,8 @@ static void print_shader_log(GLuint obj)
static void print_linker_log(GLuint obj)
{
int info_len = 0;
int max_len;
GLint info_len = 0;
GLint max_len;
pglGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len);
@@ -1005,8 +1005,16 @@ void gl_glsl_set_params(unsigned width, unsigned height,
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt)
{
// We enforce a certain layout for our various texture types in the texunits.
// Unit 0: Regular SNES frame (rubyTexture).
// Unit 1-A: LUT textures.
// Unit A+1: Previous texture.
// Unit A+2: Original texture.
// Unit A+3-B: FBO textures.
if (glsl_enable && gl_program[active_index] > 0)
{
GLint location;
@@ -1032,15 +1040,34 @@ void gl_glsl_set_params(unsigned width, unsigned height,
pglUniform1i(location, i + 1);
}
// Set previous texture.
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
glBindTexture(GL_TEXTURE_2D, prev_info->tex);
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTexture");
pglUniform1i(location, gl_teximage_cnt + 1);
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTextureSize");
pglUniform2fv(location, 1, prev_info->tex_size);
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevInputSize");
pglUniform2fv(location, 1, prev_info->input_size);
// Pass texture coordinates.
location = pglGetAttribLocation(gl_program[active_index], "rubyPrevTexCoord");
if (location >= 0)
{
pglEnableVertexAttribArray(location);
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info->coord);
}
// Set original texture unless we're in first pass (pointless).
if (active_index > 1)
{
// Bind original texture.
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
glBindTexture(GL_TEXTURE_2D, info->tex);
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture");
pglUniform1i(location, gl_teximage_cnt + 1);
pglUniform1i(location, gl_teximage_cnt + 2);
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTextureSize");
pglUniform2fv(location, 1, info->tex_size);
@@ -1055,7 +1082,7 @@ void gl_glsl_set_params(unsigned width, unsigned height,
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, info->coord);
}
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 2;
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3;
// Bind new texture in the chain.
if (fbo_info_cnt > 0)
@@ -1093,14 +1120,15 @@ void gl_glsl_set_params(unsigned width, unsigned height,
else
{
// First pass, so unbind everything to avoid collitions.
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
// Unbind ORIG.
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 2;
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3;
// Unbind any lurking FBO passes.
// Rendering to a texture that is bound to a texture unit
// sounds very shaky ... ;)
for (int i = 0; i < gl_num_programs; i++)
for (unsigned i = 0; i < gl_num_programs; i++)
{
pglActiveTexture(GL_TEXTURE0 + base_tex + i);
glBindTexture(GL_TEXTURE_2D, 0);
+1
View File
@@ -33,6 +33,7 @@ void gl_glsl_set_params(unsigned width, unsigned height,
unsigned out_width, unsigned out_height,
unsigned frame_counter,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt);
void gl_glsl_use(unsigned index);
-12
View File
@@ -107,18 +107,6 @@ struct bsv_movie
#define CRC_INDEX 2
#define STATE_SIZE_INDEX 3
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t u16;
uint8_t u8[2];
} u;
u.u16 = 1;
return u.u8[0];
}
// Convert to big-endian if needed
static inline uint32_t swap_if_big32(uint32_t val)
{
+9 -2
View File
@@ -46,8 +46,15 @@ check_pkgconf ROAR libroar
check_pkgconf JACK jack 0.120.1
check_pkgconf PULSE libpulse
check_lib COREAUDIO "-framework AudioUnit" AudioUnitInitialize
check_pkgconf SDL sdl 1.2.10
check_critical SDL "Cannot find SDL library."
check_critical SDL "Cannot find SDL 1.2 library."
check_pkgconf SDL_NEW sdl 1.3
if [ $HAVE_SDL_NEW = yes ]; then
echo "SSNES is not compatible with SDL 1.3 API, please use a recent 1.2 release."
exit 1
fi
check_lib CG -lCg cgCreateContext
check_pkgconf XML libxml-2.0
@@ -76,7 +83,7 @@ check_pkgconf PYTHON python3
add_define_make OS $OS
# Creates config.mk and config.h.
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
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.6.1
PACKAGE_VERSION=0.7
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -23,6 +23,7 @@ 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 COREAUDIO "Enable CoreAudio support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
add_command_line_enable XVIDEO "Enable XVideo support" auto
+30 -11
View File
@@ -5,6 +5,7 @@
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avconfig.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
@@ -25,6 +26,9 @@ struct video_info
uint8_t *outbuf;
size_t outbuf_size;
int fmt;
int pix_size;
AVFormatContext *format;
struct SwsContext *sws_ctx;
@@ -41,8 +45,6 @@ struct audio_info
void *outbuf;
size_t outbuf_size;
int pix_fmt;
} audio;
struct muxer_info
@@ -91,7 +93,7 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
if (!audio->buffer)
return false;
audio->outbuf_size = 200000;
audio->outbuf_size = 2000000;
audio->outbuf = av_malloc(audio->outbuf_size);
if (!audio->outbuf)
return false;
@@ -105,6 +107,18 @@ static bool init_video(struct video_info *video, struct ffemu_params *param)
if (!codec)
return false;
#if AV_HAVE_BIGENDIAN
video->fmt = PIX_FMT_RGB555BE;
#else
video->fmt = PIX_FMT_RGB555LE;
#endif
video->pix_size = sizeof(uint16_t);
if (param->rgb32)
{
video->fmt = PIX_FMT_RGB32;
video->pix_size = sizeof(uint32_t);
}
video->codec = avcodec_alloc_context();
video->codec->width = param->out_width;
video->codec->height = param->out_height;
@@ -116,7 +130,7 @@ static bool init_video(struct video_info *video, struct ffemu_params *param)
return false;
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
video->outbuf_size = 2000000;
video->outbuf_size = 1 << 23;
video->outbuf = av_malloc(video->outbuf_size);
// Just to make sure we can handle the biggest frames. Seemed to crash with just 256 * 224.
@@ -177,7 +191,8 @@ static bool init_thread(ffemu_t *handle)
assert(handle->cond = SDL_CreateCond());
assert(handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) * handle->params.channels * MAX_FRAMES / 60));
assert(handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height * sizeof(int16_t) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height *
handle->video.pix_size * MAX_FRAMES));
handle->alive = true;
handle->can_sleep = true;
@@ -365,18 +380,20 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
{
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, PIX_FMT_RGB555LE,
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, handle->video.fmt,
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
NULL, NULL, NULL);
int linesize = data->pitch;
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
handle->video.codec->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
handle->video.conv_frame->pts = handle->video.frame_cnt;
handle->video.conv_frame->display_picture_number = handle->video.frame_cnt;
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame);
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf,
handle->video.outbuf_size, handle->video.conv_frame);
if (outsize < 0)
return -1;
@@ -387,7 +404,8 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
pkt.data = handle->video.outbuf;
pkt.size = outsize;
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base);
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base,
handle->muxer.vstream->time_base);
if (handle->video.codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
@@ -454,7 +472,7 @@ int ffemu_finalize(ffemu_t *handle)
deinit_thread(handle);
// Push out frames still stuck in queue.
uint16_t *video_buf = av_malloc(handle->params.fb_width * handle->params.fb_height * sizeof(uint16_t));
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
if (video_buf)
{
struct ffemu_video_data attr_buf;
@@ -524,7 +542,8 @@ static int SDLCALL ffemu_thread(void *data)
{
ffemu_t *ff = data;
uint16_t *video_buf = av_malloc(ff->params.fb_width * ff->params.fb_height * sizeof(uint16_t));
// For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. :s
void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size);
assert(video_buf);
size_t audio_buf_size = 128 * ff->params.channels * sizeof(int16_t);
+3 -27
View File
@@ -8,29 +8,6 @@
extern "C" {
#endif
// Available video codecs
typedef enum ffemu_video_codec
{
FFEMU_VIDEO_FFV1,
} ffemu_video_codec;
// Available audio codecs
typedef enum ffemu_audio_codec
{
FFEMU_AUDIO_VORBIS,
} ffemu_audio_codec;
// Available pixel formats
typedef enum ffemu_pixel_format
{
FFEMU_FMT_XBGR1555,
} ffemu_pixel_format;
typedef enum ffemu_rescaler
{
FFEMU_RESCALER_POINT
} ffemu_rescaler;
struct ffemu_rational
{
unsigned num;
@@ -40,9 +17,6 @@ struct ffemu_rational
// Parameters passed to ffemu_new()
struct ffemu_params
{
// Video codec to use. If not recording video, select FFEMU_VIDEO_NONE.
ffemu_video_codec vcodec;
// Desired output resolution.
unsigned out_width;
unsigned out_height;
@@ -68,6 +42,9 @@ struct ffemu_params
// Audio channels.
unsigned channels;
// If input is ARGB or XRGB1555.
bool rgb32;
// Audio bits. Sample format is always signed PCM in native byte order.
//unsigned bits;
@@ -107,7 +84,6 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
int ffemu_finalize(ffemu_t *handle);
#ifdef __cplusplus
}
#endif
+21 -2
View File
@@ -71,12 +71,18 @@ static void set_defaults(void)
case AUDIO_ROAR:
def_audio = "roar";
break;
case AUDIO_COREAUDIO:
def_audio = "coreaudio";
break;
case AUDIO_AL:
def_audio = "openal";
break;
case AUDIO_SDL:
def_audio = "sdl";
break;
case AUDIO_DSOUND:
def_audio = "dsound";
break;
case AUDIO_XAUDIO:
def_audio = "xaudio";
break;
@@ -111,10 +117,11 @@ static void set_defaults(void)
g_settings.video.xscale = xscale;
g_settings.video.yscale = yscale;
g_settings.video.fullscreen = fullscreen;
g_settings.video.fullscreen = g_extern.force_fullscreen ? true : fullscreen;
g_settings.video.fullscreen_x = fullscreen_x;
g_settings.video.fullscreen_y = fullscreen_y;
g_settings.video.force_16bit = force_16bit;
g_settings.video.disable_composition = disable_composition;
g_settings.video.vsync = vsync;
g_settings.video.smooth = video_smooth;
g_settings.video.force_aspect = force_aspect;
@@ -135,6 +142,9 @@ static void set_defaults(void)
g_settings.video.second_pass_smooth = second_pass_smooth;
#endif
g_settings.video.hires_record = hires_record;
g_settings.video.post_filter_record = post_filter_record;
g_settings.audio.enable = audio_enable;
g_settings.audio.out_rate = out_rate;
g_settings.audio.in_rate = in_rate;
@@ -289,8 +299,14 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.yscale, "video_yscale");
CONFIG_GET_INT(video.fullscreen_x, "video_fullscreen_x");
CONFIG_GET_INT(video.fullscreen_y, "video_fullscreen_y");
CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen");
if (!g_extern.force_fullscreen)
{
CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen");
}
CONFIG_GET_BOOL(video.force_16bit, "video_force_16bit");
CONFIG_GET_BOOL(video.disable_composition, "video_disable_composition");
CONFIG_GET_BOOL(video.vsync, "video_vsync");
CONFIG_GET_BOOL(video.smooth, "video_smooth");
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
@@ -312,6 +328,9 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
#endif
CONFIG_GET_BOOL(video.hires_record, "video_hires_record");
CONFIG_GET_BOOL(video.post_filter_record, "video_post_filter_record");
#ifdef HAVE_DYLIB
CONFIG_GET_STRING(video.filter_path, "video_filter");
CONFIG_GET_STRING(video.external_driver, "video_external_driver");
+55 -7
View File
@@ -142,14 +142,16 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
if (g_extern.do_screenshot)
take_screenshot(data, width, height);
// Slightly messy code,
// but we really need to do processing before blocking on VSync for best possible scheduling.
#ifdef HAVE_FFMPEG
if (g_extern.recording)
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record))
{
struct ffemu_video_data ffemu_data = {
.data = data,
.pitch = height == 448 || height == 478 ? 1024 : 2048,
.width = width,
.height = height
.height = height,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
@@ -165,6 +167,20 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
g_extern.filter.psize(&owidth, &oheight);
g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer,
g_extern.filter.pitch, data, (height == 448 || height == 478) ? 1024 : 2048, width, height);
#ifdef HAVE_FFMPEG
if (g_extern.recording && g_settings.video.post_filter_record)
{
struct ffemu_video_data ffemu_data = {
.data = g_extern.filter.buffer,
.pitch = g_extern.filter.pitch,
.width = owidth,
.height = oheight,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
#endif
if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg))
g_extern.video_active = false;
}
@@ -340,7 +356,7 @@ static void fill_pathname_noext(char *out_path, const char *in_path, const char
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.6.1"
#define PACKAGE_VERSION "0.7"
#endif
#include "config.features.h"
@@ -383,6 +399,7 @@ static void print_help(void)
puts("Usage: ssnes [rom file] [options...]");
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-f/--fullscreen: Start SSNES in fullscreen regardless of config settings.");
puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed.");
#ifdef HAVE_CONFIGFILE
puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR);
@@ -441,6 +458,7 @@ static void parse_input(int argc, char *argv[])
struct option opts[] = {
{ "help", 0, NULL, 'h' },
{ "save", 1, NULL, 's' },
{ "fullscreen", 0, NULL, 'f' },
#ifdef HAVE_FFMPEG
{ "record", 1, NULL, 'r' },
#endif
@@ -485,7 +503,7 @@ static void parse_input(int argc, char *argv[])
#define CONFIG_FILE_ARG
#endif
char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
for(;;)
{
val = 0;
@@ -518,6 +536,10 @@ static void parse_input(int argc, char *argv[])
g_extern.has_set_save_path = true;
break;
case 'f':
g_extern.force_fullscreen = true;
break;
case 'g':
strlcpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path));
g_extern.game_type = SSNES_CART_SGB;
@@ -786,9 +808,11 @@ static inline void save_files(void)
#ifdef HAVE_FFMPEG
static void init_recording(void)
{
// Hardcode these options at the moment. Should be specificed in the config file later on.
if (g_extern.recording)
{
// Not perfectly SNES accurate, but this can be adjusted later during processing
// and playback losslessly, and we please the containers more by
// using "sane" values.
struct ffemu_rational ntsc_fps = {60000, 1000};
struct ffemu_rational pal_fps = {50000, 1000};
struct ffemu_params params = {
@@ -800,9 +824,29 @@ static void init_recording(void)
.samplerate = 32000,
.filename = g_extern.record_path,
.fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps,
.aspect_ratio = 4.0/3
.aspect_ratio = 4.0 / 3,
.rgb32 = false,
};
SSNES_LOG("Recording with FFmpeg to %s.\n", g_extern.record_path);
if (g_settings.video.hires_record)
{
params.out_width = 512;
params.out_height = 448;
}
if (g_settings.video.post_filter_record && g_extern.filter.active)
{
g_extern.filter.psize(&params.out_width, &params.out_height);
params.rgb32 = true;
unsigned max_width = 512;
unsigned max_height = 512;
g_extern.filter.psize(&max_width, &max_height);
params.fb_width = g_extern.filter.pitch >> 2;
params.fb_height = next_pow2(max_height);
}
SSNES_LOG("Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s)\n", g_extern.record_path, params.out_width, params.out_height, params.fb_width, params.fb_height, params.rgb32 ? "yes" : "no");
g_extern.rec = ffemu_new(&params);
if (!g_extern.rec)
{
@@ -1529,6 +1573,10 @@ int main(int argc, char *argv[])
// Main loop
for(;;)
{
// DSP plugin GUI events.
if (g_extern.audio_data.dsp_handle && g_extern.audio_data.dsp_plugin->events)
g_extern.audio_data.dsp_plugin->events(g_extern.audio_data.dsp_handle);
// Time to drop?
if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) ||
!driver.video->alive(driver.video_data) || g_extern.bsv_movie_end)
+9
View File
@@ -25,6 +25,9 @@
# Force 16-bit colors. Apparently some video cards in use today have troubles with 32-bit ...
# video_force_16bit = false
# Forcibly disable composition. Only works in Windows Vista/7 for now.
# video_disable_composition = false
# Video vsync.
# video_vsync = true
@@ -290,3 +293,9 @@
# Directory to dump screenshots to.
# screenshot_directory =
# Records video assuming video is hi-res.
# video_hires_record = false
# Records video after CPU video filter.
# video_post_filter_record = false