Browse Source

SDL1: Audio and video support

1. Implements audio support for SDL1
2. Implements blit scaling and correct video rendering for SDL1
pull/274/head
Gleb Mazovetskiy 7 years ago committed by Anders Jenbo
parent
commit
5359149a38
  1. 354
      SourceS/sdl2_to_1_2_backports.h
  2. 163
      SourceX/storm/storm.cpp

354
SourceS/sdl2_to_1_2_backports.h

@ -16,6 +16,7 @@
#define SDL_zero(x) SDL_memset(&(x), 0, sizeof((x)))
#define SDL_InvalidParamError(param) SDL_SetError("Parameter '%s' is invalid", (param))
#define SDL_Log puts
#define SDL_floor floor
//== Events handling
@ -100,7 +101,8 @@ inline void SDL_GetWindowPosition(SDL_Window *window, int *x, int *y)
printf("SDL_GetWindowPosition %d %d", *x, *y);
}
inline void SDL_SetWindowPosition(SDL_Window *window, int x, int y) {
inline void SDL_SetWindowPosition(SDL_Window *window, int x, int y)
{
DUMMY();
}
@ -132,12 +134,11 @@ inline void SDL_DestroyWindow(SDL_Window *window)
}
inline void
SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
SDL_WarpMouseInWindow(SDL_Window *window, int x, int y)
{
SDL_WarpMouse(x, y);
}
//= Renderer stubs
#define SDL_Renderer void
@ -244,6 +245,22 @@ inline void SDLBackport_PixelformatToMask(int pixelformat, Uint32 *flags, Uint32
}
}
/**
* A limited implementation of `a.format` == `b.format` from SDL2.
*/
inline bool SDLBackport_PixelFormatFormatEq(const SDL_PixelFormat *a, const SDL_PixelFormat *b)
{
return a->BitsPerPixel == b->BitsPerPixel && (a->palette != nullptr) == (b->palette != nullptr);
}
/**
* Similar to `SDL_ISPIXELFORMAT_INDEXED` from SDL2.
*/
inline bool SDLBackport_IsPixelFormatIndexed(const SDL_PixelFormat *pf)
{
return pf->BitsPerPixel == 8 && pf->palette != nullptr;
}
//= Surface creation
inline SDL_Surface *
@ -264,6 +281,334 @@ SDL_CreateRGBSurfaceWithFormatFrom(void *pixels, Uint32 flags, int width, int he
return SDL_CreateRGBSurfaceFrom(pixels, flags, width, height, depth, rmask, gmask, bmask, amask);
}
//= BlitScaled backport from SDL 2.0.9.
#define SDL_BlitScaled SDL_UpperBlitScaled
#define DEFINE_COPY_ROW(name, type) \
static void name(type *src, int src_w, type *dst, int dst_w) \
{ \
int i; \
int pos, inc; \
type pixel = 0; \
\
pos = 0x10000; \
inc = (src_w << 16) / dst_w; \
for (i = dst_w; i > 0; --i) { \
while (pos >= 0x10000L) { \
pixel = *src++; \
pos -= 0x10000L; \
} \
*dst++ = pixel; \
pos += inc; \
} \
}
DEFINE_COPY_ROW(copy_row1, Uint8)
DEFINE_COPY_ROW(copy_row2, Uint16)
DEFINE_COPY_ROW(copy_row4, Uint32)
static void
copy_row3(Uint8 *src, int src_w, Uint8 *dst, int dst_w)
{
int i;
int pos, inc;
Uint8 pixel[3] = { 0, 0, 0 };
pos = 0x10000;
inc = (src_w << 16) / dst_w;
for (i = dst_w; i > 0; --i) {
while (pos >= 0x10000L) {
pixel[0] = *src++;
pixel[1] = *src++;
pixel[2] = *src++;
pos -= 0x10000L;
}
*dst++ = pixel[0];
*dst++ = pixel[1];
*dst++ = pixel[2];
pos += inc;
}
}
// NOTE: Not thread-safe
inline int
SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect,
SDL_Surface *dst, const SDL_Rect *dstrect)
{
// All the ASM support has been removed, as the platforms that the ASM
// implementation exists for support SDL2 anyway.
int src_locked;
int dst_locked;
int pos, inc;
int dst_maxrow;
int src_row, dst_row;
Uint8 *srcp = NULL;
Uint8 *dstp;
SDL_Rect full_src;
SDL_Rect full_dst;
const int bpp = dst->format->BytesPerPixel;
if (!SDLBackport_PixelFormatFormatEq(src->format, dst->format)) {
SDL_SetError("Only works with same format surfaces");
return -1;
}
/* Verify the blit rectangles */
if (srcrect) {
if ((srcrect->x < 0) || (srcrect->y < 0) || ((srcrect->x + srcrect->w) > src->w) || ((srcrect->y + srcrect->h) > src->h)) {
SDL_SetError("Invalid source blit rectangle");
return -1;
}
} else {
full_src.x = 0;
full_src.y = 0;
full_src.w = src->w;
full_src.h = src->h;
srcrect = &full_src;
}
if (dstrect) {
if ((dstrect->x < 0) || (dstrect->y < 0) || ((dstrect->x + dstrect->w) > dst->w) || ((dstrect->y + dstrect->h) > dst->h)) {
SDL_SetError("Invalid destination blit rectangle");
return -1;
}
} else {
full_dst.x = 0;
full_dst.y = 0;
full_dst.w = dst->w;
full_dst.h = dst->h;
dstrect = &full_dst;
}
/* Lock the destination if it's in hardware */
dst_locked = 0;
if (SDL_MUSTLOCK(dst)) {
if (SDL_LockSurface(dst) < 0) {
SDL_SetError("Unable to lock destination surface");
return -1;
}
dst_locked = 1;
}
/* Lock the source if it's in hardware */
src_locked = 0;
if (SDL_MUSTLOCK(src)) {
if (SDL_LockSurface(src) < 0) {
if (dst_locked) {
SDL_UnlockSurface(dst);
}
SDL_SetError("Unable to lock source surface");
return -1;
}
src_locked = 1;
}
/* Set up the data... */
pos = 0x10000;
inc = (srcrect->h << 16) / dstrect->h;
src_row = srcrect->y;
dst_row = dstrect->y;
/* Perform the stretch blit */
for (dst_maxrow = dst_row + dstrect->h; dst_row < dst_maxrow; ++dst_row) {
dstp = (Uint8 *)dst->pixels + (dst_row * dst->pitch)
+ (dstrect->x * bpp);
while (pos >= 0x10000L) {
srcp = (Uint8 *)src->pixels + (src_row * src->pitch)
+ (srcrect->x * bpp);
++src_row;
pos -= 0x10000L;
}
switch (bpp) {
case 1:
copy_row1(srcp, srcrect->w, dstp, dstrect->w);
break;
case 2:
copy_row2((Uint16 *)srcp, srcrect->w,
(Uint16 *)dstp, dstrect->w);
break;
case 3:
copy_row3(srcp, srcrect->w, dstp, dstrect->w);
break;
case 4:
copy_row4((Uint32 *)srcp, srcrect->w,
(Uint32 *)dstp, dstrect->w);
break;
}
pos += inc;
}
/* We need to unlock the surfaces if they're locked */
if (dst_locked) {
SDL_UnlockSurface(dst);
}
if (src_locked) {
SDL_UnlockSurface(src);
}
return (0);
}
inline int
SDL_LowerBlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
if (SDLBackport_PixelFormatFormatEq(src->format, dst->format) && !SDLBackport_IsPixelFormatIndexed(src->format)) {
return SDL_SoftStretch(src, srcrect, dst, dstrect);
} else {
return SDL_LowerBlit(src, srcrect, dst, dstrect);
}
}
// NOTE: The second argument is const in SDL2 but not here.
inline int
SDL_UpperBlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
double src_x0, src_y0, src_x1, src_y1;
double dst_x0, dst_y0, dst_x1, dst_y1;
SDL_Rect final_src, final_dst;
double scaling_w, scaling_h;
int src_w, src_h;
int dst_w, dst_h;
/* Make sure the surfaces aren't locked */
if (!src || !dst) {
SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface");
return -1;
}
if (src->locked || dst->locked) {
SDL_SetError("Surfaces must not be locked during blit");
return -1;
}
if (NULL == srcrect) {
src_w = src->w;
src_h = src->h;
} else {
src_w = srcrect->w;
src_h = srcrect->h;
}
if (NULL == dstrect) {
dst_w = dst->w;
dst_h = dst->h;
} else {
dst_w = dstrect->w;
dst_h = dstrect->h;
}
if (dst_w == src_w && dst_h == src_h) {
/* No scaling, defer to regular blit */
return SDL_BlitSurface(src, srcrect, dst, dstrect);
}
scaling_w = (double)dst_w / src_w;
scaling_h = (double)dst_h / src_h;
if (NULL == dstrect) {
dst_x0 = 0;
dst_y0 = 0;
dst_x1 = dst_w - 1;
dst_y1 = dst_h - 1;
} else {
dst_x0 = dstrect->x;
dst_y0 = dstrect->y;
dst_x1 = dst_x0 + dst_w - 1;
dst_y1 = dst_y0 + dst_h - 1;
}
if (NULL == srcrect) {
src_x0 = 0;
src_y0 = 0;
src_x1 = src_w - 1;
src_y1 = src_h - 1;
} else {
src_x0 = srcrect->x;
src_y0 = srcrect->y;
src_x1 = src_x0 + src_w - 1;
src_y1 = src_y0 + src_h - 1;
/* Clip source rectangle to the source surface */
if (src_x0 < 0) {
dst_x0 -= src_x0 * scaling_w;
src_x0 = 0;
}
if (src_x1 >= src->w) {
dst_x1 -= (src_x1 - src->w + 1) * scaling_w;
src_x1 = src->w - 1;
}
if (src_y0 < 0) {
dst_y0 -= src_y0 * scaling_h;
src_y0 = 0;
}
if (src_y1 >= src->h) {
dst_y1 -= (src_y1 - src->h + 1) * scaling_h;
src_y1 = src->h - 1;
}
}
/* Clip destination rectangle to the clip rectangle */
/* Translate to clip space for easier calculations */
dst_x0 -= dst->clip_rect.x;
dst_x1 -= dst->clip_rect.x;
dst_y0 -= dst->clip_rect.y;
dst_y1 -= dst->clip_rect.y;
if (dst_x0 < 0) {
src_x0 -= dst_x0 / scaling_w;
dst_x0 = 0;
}
if (dst_x1 >= dst->clip_rect.w) {
src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w;
dst_x1 = dst->clip_rect.w - 1;
}
if (dst_y0 < 0) {
src_y0 -= dst_y0 / scaling_h;
dst_y0 = 0;
}
if (dst_y1 >= dst->clip_rect.h) {
src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h;
dst_y1 = dst->clip_rect.h - 1;
}
/* Translate back to surface coordinates */
dst_x0 += dst->clip_rect.x;
dst_x1 += dst->clip_rect.x;
dst_y0 += dst->clip_rect.y;
dst_y1 += dst->clip_rect.y;
final_src.x = (int)SDL_floor(src_x0 + 0.5);
final_src.y = (int)SDL_floor(src_y0 + 0.5);
final_src.w = (int)SDL_floor(src_x1 + 1 + 0.5) - (int)SDL_floor(src_x0 + 0.5);
final_src.h = (int)SDL_floor(src_y1 + 1 + 0.5) - (int)SDL_floor(src_y0 + 0.5);
final_dst.x = (int)SDL_floor(dst_x0 + 0.5);
final_dst.y = (int)SDL_floor(dst_y0 + 0.5);
final_dst.w = (int)SDL_floor(dst_x1 - dst_x0 + 1.5);
final_dst.h = (int)SDL_floor(dst_y1 - dst_y0 + 1.5);
if (final_dst.w < 0)
final_dst.w = 0;
if (final_dst.h < 0)
final_dst.h = 0;
if (dstrect)
*dstrect = final_dst;
if (final_dst.w == 0 || final_dst.h == 0 || final_src.w <= 0 || final_src.h <= 0) {
/* No-op. */
return 0;
}
return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst);
}
//= Display handling
typedef struct
@ -287,10 +632,13 @@ inline int SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode *mode)
switch (info->vfmt->BitsPerPixel) {
case 8:
mode->format = SDL_PIXELFORMAT_INDEX8;
break;
case 32:
mode->format = SDL_PIXELFORMAT_RGBA8888;
break;
default:
mode->format = 0;
break;
}
mode->w = info->current_w;

163
SourceX/storm/storm.cpp

@ -1,4 +1,9 @@
#include "devilution.h"
#ifdef USE_SDL1
#include <queue>
#endif
#include "miniwin/ddraw.h"
#include "stubs.h"
#include <Radon.hpp>
@ -426,8 +431,18 @@ SDL_Surface *SVidSurface;
BYTE *SVidBuffer;
unsigned long SVidWidth, SVidHeight;
#ifndef USE_SDL1
#ifdef USE_SDL1
static bool HaveAudio()
{
return SDL_GetAudioStatus() != SDL_AUDIO_STOPPED;
}
#else
SDL_AudioDeviceID deviceId;
static bool HaveAudio()
{
return deviceId != 0;
}
#endif
void SVidRestartMixer()
@ -439,6 +454,90 @@ void SVidRestartMixer()
Mix_ReserveChannels(1);
}
#ifdef USE_SDL1
struct AudioQueueItem {
unsigned char *data;
unsigned long len;
const unsigned char *pos;
};
class AudioQueue {
public:
static void Callback(void *userdata, Uint8 *out, int out_len)
{
static_cast<AudioQueue *>(userdata)->Dequeue(out, out_len);
}
void Subscribe(SDL_AudioSpec *spec)
{
spec->userdata = this;
spec->callback = AudioQueue::Callback;
}
void Enqueue(const unsigned char *data, unsigned long len)
{
SDL_LockAudio();
EnqueueUnsafe(data, len);
SDL_UnlockAudio();
}
void Clear()
{
while (!queue_.empty())
Pop();
}
private:
void EnqueueUnsafe(const unsigned char *data, unsigned long len)
{
AudioQueueItem item;
item.data = new unsigned char[len];
memcpy(item.data, data, len * sizeof(item.data[0]));
item.len = len;
item.pos = item.data;
queue_.push(item);
}
void Dequeue(Uint8 *out, int out_len)
{
AudioQueueItem *item;
while ((item = Next()) != NULL) {
if (out_len <= item->len) {
SDL_MixAudio(out, item->pos, out_len, SDL_MIX_MAXVOLUME);
item->pos += out_len;
item->len -= out_len;
return;
}
SDL_MixAudio(out, item->pos, item->len, SDL_MIX_MAXVOLUME);
out += item->len;
out_len -= item->len;
Pop();
}
memset(out, 0, sizeof(out[0]) * out_len);
}
AudioQueueItem *Next()
{
while (!queue_.empty() && queue_.front().len == 0)
Pop();
if (queue_.empty())
return NULL;
return &queue_.front();
}
void Pop()
{
delete[] queue_.front().data;
queue_.pop();
}
std::queue<AudioQueueItem> queue_;
};
static AudioQueue *sVidAudioQueue = new AudioQueue();
#endif
BOOL SVidPlayBegin(char *filename, int a2, int a3, int a4, int a5, int flags, HANDLE *video)
{
if (flags & 0x10000 || flags & 0x20000000) {
@ -479,12 +578,13 @@ BOOL SVidPlayBegin(char *filename, int a2, int a3, int a4, int a5, int flags, HA
Mix_CloseAudio();
#ifdef USE_SDL1
if (SDL_OpenAudio(&audioFormat, NULL) != 0) {
SDL_Log(SDL_GetError());
SVidRestartMixer();
return false;
}
SDL_PauseAudio(0);
sVidAudioQueue->Subscribe(&audioFormat);
if (SDL_OpenAudio(&audioFormat, NULL) != 0) {
SDL_Log(SDL_GetError());
SVidRestartMixer();
return false;
}
SDL_PauseAudio(0);
#else
deviceId = SDL_OpenAudioDevice(NULL, 0, &audioFormat, NULL, 0);
if (deviceId == 0) {
@ -537,19 +637,14 @@ BOOL SVidPlayBegin(char *filename, int a2, int a3, int a4, int a5, int flags, HA
}
#ifdef USE_SDL1
if (SDL_SetPalette(SVidSurface, SDL_LOGPAL, SVidPalette->colors, 0, SVidPalette->ncolors) != 1) {
SDL_Log(SDL_GetError());
if (SDL_GetAudioStatus() != SDL_AUDIO_STOPPED)
SVidRestartMixer();
return false;
}
#else
if (SDL_SetSurfacePalette(SVidSurface, SVidPalette) <= -1) {
#endif
SDL_Log(SDL_GetError());
if (deviceId > 0)
if (HaveAudio())
SVidRestartMixer();
return false;
}
#endif
SVidFrameEnd = SDL_GetTicks() * 1000 + SVidFrameLength;
@ -596,20 +691,29 @@ BOOL SVidPlayContinue(void)
SDL_Log(SDL_GetError());
return false;
}
#ifdef USE_SDL1
if (SDL_SetPalette(SVidSurface, SDL_LOGPAL, SVidPalette->colors, 0, SVidPalette->ncolors) != 1) {
SDL_Log(SDL_GetError());
return false;
}
#endif
}
if (SDL_GetTicks() * 1000 >= SVidFrameEnd) {
return SVidLoadNextFrame(); // Skip video and audio if the system is to slow
}
if (HaveAudio()) {
#ifdef USE_SDL1
// TODO: Support audio.
sVidAudioQueue->Enqueue(smk_get_audio(SVidSMK, 0), smk_get_audio_size(SVidSMK, 0));
#else
if (deviceId && SDL_QueueAudio(deviceId, smk_get_audio(SVidSMK, 0), smk_get_audio_size(SVidSMK, 0)) <= -1) {
SDL_Log(SDL_GetError());
return false;
}
if (SDL_QueueAudio(deviceId, smk_get_audio(SVidSMK, 0), smk_get_audio_size(SVidSMK, 0)) <= -1) {
SDL_Log(SDL_GetError());
return false;
}
#endif
}
if (SDL_GetTicks() * 1000 >= SVidFrameEnd) {
return SVidLoadNextFrame(); // Skip video if the system is to slow
@ -637,20 +741,17 @@ BOOL SVidPlayContinue(void)
SDL_Rect pal_surface_offset = { (SCREEN_WIDTH - scaledW) / 2, (SCREEN_HEIGHT - scaledH) / 2, scaledW, scaledH };
#ifdef USE_SDL1
// TODO: Scale before blitting.
SDL_Surface *tmp = SDL_ConvertSurface(SVidSurface, window->format, 0);
if (SDL_BlitSurface(tmp, NULL, surface, &pal_surface_offset) <= -1) {
SDL_Log(SDL_GetError());
return false;
}
// NOTE: Consider resolution switching instead if video doesn't play
// fast enough.
#else
Uint32 format = SDL_GetWindowPixelFormat(window);
SDL_Surface *tmp = SDL_ConvertSurfaceFormat(SVidSurface, format, 0);
#endif
if (SDL_BlitScaled(tmp, NULL, surface, &pal_surface_offset) <= -1) {
SDL_Log(SDL_GetError());
return false;
}
#endif
SDL_FreeSurface(tmp);
}
@ -667,19 +768,17 @@ BOOL SVidPlayContinue(void)
BOOL SVidPlayEnd(HANDLE video)
{
if (HaveAudio()) {
#ifdef USE_SDL1
if (SDL_GetAudioStatus() != SDL_AUDIO_STOPPED) {
SDL_CloseAudio();
SVidRestartMixer();
}
sVidAudioQueue->Clear();
#else
if (deviceId) {
SDL_ClearQueuedAudio(deviceId);
SDL_CloseAudioDevice(deviceId);
deviceId = 0;
#endif
SVidRestartMixer();
}
#endif
if (SVidSMK)
smk_close(SVidSMK);
@ -690,7 +789,10 @@ BOOL SVidPlayEnd(HANDLE video)
}
SDL_FreePalette(SVidPalette);
SVidPalette = NULL;
SDL_FreeSurface(SVidSurface);
SVidSurface = NULL;
SFileCloseFile(video);
video = NULL;
@ -786,5 +888,4 @@ BOOL SFileEnableDirectAccess(BOOL enable)
directFileAccess = enable;
return true;
}
}

Loading…
Cancel
Save