Browse Source

Full resampler support, minus bug fix. Remove the codepage autodetection which is not working for TC/SC.

louyihua 8 years ago
parent
commit
cb4b7bd42c
16 changed files with 612 additions and 285 deletions
  1. 2 2
      Makefile
  2. 11 43
      global.c
  3. 1 0
      global.h
  4. 67 3
      libmad/music_mad.c
  5. 5 2
      libmad/music_mad.h
  6. 4 2
      mp3player.c
  7. 82 98
      oggplay.c
  8. 3 3
      resampler.c
  9. 2 2
      rixplay.cpp
  10. 6 2
      sdlpal.cfg.example
  11. 1 1
      sdlpal.vcxproj
  12. 1 1
      sdlpal.vcxproj.filters
  13. 380 123
      sound.c
  14. 2 0
      sound.h
  15. 36 3
      text.c
  16. 9 0
      text.h

+ 2 - 2
Makefile

@@ -15,8 +15,8 @@ LIBMAD_FILES = libmad/bit.c libmad/decoder.c libmad/fixed.c libmad/frame.c \
 
 
 FILES = rixplay.cpp text.c font.c itemmenu.c scene.c palcommon.c script.c \
 FILES = rixplay.cpp text.c font.c itemmenu.c scene.c palcommon.c script.c \
 	util.c play.c getopt.c input.c uibattle.c game.c magicmenu.c map.c \
 	util.c play.c getopt.c input.c uibattle.c game.c magicmenu.c map.c \
-	ending.c uigame.c rngplay.c ui.c global.c main.c fight.c \
-	video.c palette.c sound.c res.c battle.c yj1.c
+	ending.c uigame.c rngplay.c ui.c global.c main.c fight.c mp3play.c \
+	video.c palette.c sound.c res.c battle.c yj1.c oggplay.c 
 
 
 FILES += $(ADPLUG_FILES)
 FILES += $(ADPLUG_FILES)
 FILES += $(LIBMAD_FILES)
 FILES += $(LIBMAD_FILES)

+ 11 - 43
global.c

@@ -22,6 +22,7 @@
 //
 //
 
 
 #include "main.h"
 #include "main.h"
+#include "resampler.h"
 
 
 LPGLOBALVARS gpGlobals = NULL;
 LPGLOBALVARS gpGlobals = NULL;
 extern BOOL g_fUseMidi;
 extern BOOL g_fUseMidi;
@@ -66,7 +67,7 @@ PAL_InitGlobals(
 --*/
 --*/
 {
 {
    FILE     *fp;
    FILE     *fp;
-   CODEPAGE  iCodePage = CP_UNKNOWN;
+   CODEPAGE  iCodePage = CP_BIG5;		// Default for BIG5
    DWORD     dwWordLength = 10;			// Default for PAL DOS/WIN95
    DWORD     dwWordLength = 10;			// Default for PAL DOS/WIN95
    DWORD     dwExtraMagicDescLines = 0;	// Default for PAL DOS/WIN95
    DWORD     dwExtraMagicDescLines = 0;	// Default for PAL DOS/WIN95
    DWORD     dwExtraItemDescLines = 0;	// Default for PAL DOS/WIN95
    DWORD     dwExtraItemDescLines = 0;	// Default for PAL DOS/WIN95
@@ -77,6 +78,7 @@ PAL_InitGlobals(
    float     flSurroundOPLOffset = 384.0f;// Default for 384.0
    float     flSurroundOPLOffset = 384.0f;// Default for 384.0
    INT       iSampleRate = 44100;		// Default for 44100 Hz
    INT       iSampleRate = 44100;		// Default for 44100 Hz
    INT       iOPLSampleRate = 49716;	// Default for 49716 Hz
    INT       iOPLSampleRate = 49716;	// Default for 49716 Hz
+   INT       iResampleQuality = RESAMPLER_QUALITY_MAX;	// Default to maximum quality
    MUSICTYPE eMusicType = g_fUseMidi ? MUSIC_MIDI : MUSIC_RIX;
    MUSICTYPE eMusicType = g_fUseMidi ? MUSIC_MIDI : MUSIC_RIX;
    MUSICTYPE eCDType = PAL_HAS_SDLCD ? MUSIC_SDLCD : MUSIC_OGG;
    MUSICTYPE eCDType = PAL_HAS_SDLCD ? MUSIC_SDLCD : MUSIC_OGG;
    OPLTYPE   eOPLType = OPL_DOSBOX;
    OPLTYPE   eOPLType = OPL_DOSBOX;
@@ -163,6 +165,10 @@ PAL_InitGlobals(
 				   {
 				   {
 					   sscanf(ptr, "%d", &iOPLSampleRate);
 					   sscanf(ptr, "%d", &iOPLSampleRate);
 				   }
 				   }
+				   else if (SDL_strcasecmp(p, "RESAMPLEQUALITY") == 0)
+				   {
+					   sscanf(ptr, "%d", &iResampleQuality);
+				   }
 				   else if (SDL_strcasecmp(p, "SURROUNDOPLOFFSET") == 0)
 				   else if (SDL_strcasecmp(p, "SURROUNDOPLOFFSET") == 0)
 				   {
 				   {
 					   sscanf(ptr, "%f", &flSurroundOPLOffset);
 					   sscanf(ptr, "%f", &flSurroundOPLOffset);
@@ -207,56 +213,16 @@ PAL_InitGlobals(
 	   UTIL_CloseFile(fp);
 	   UTIL_CloseFile(fp);
    }
    }
 
 
-   // Codepage auto detect 
-   if (CP_UNKNOWN == iCodePage)
-   {
-	   // Try to convert the content of word.dat with different codepages,
-	   // and use the codepage with minimal inconvertible characters
-	   // Works fine currently with SC/TC/JP.
-	   if (fp = UTIL_OpenFile("word.dat"))
-	   {
-		   char *buf;
-		   long len;
-		   int i, j, c, m = INT_MAX, m_i;
-		   fseek(fp, 0, SEEK_END);
-		   len = ftell(fp);
-		   buf = (char *)malloc(len);
-		   fseek(fp, 0, SEEK_SET);
-		   fread(buf, 1, len, fp);
-		   UTIL_CloseFile(fp);
-		   for (i = CP_MIN; i < CP_MAX; i++)
-		   {
-			   int wlen = PAL_MultiByteToWideChar(buf, len, NULL, 0);
-			   WCHAR *wbuf = (WCHAR *)malloc(wlen * sizeof(WCHAR));
-			   PAL_MultiByteToWideChar(buf, len, wbuf, wlen);
-			   for (j = c = 0; j < wlen; j++)
-			   {
-				   c += (wbuf[j] == PAL_GetInvalidChar(i)) ? 1 : 0;
-			   }
-			   if (c < m)
-			   {
-				   m = c;
-				   m_i = i;
-			   }
-			   free(wbuf);
-		   }
-		   free(buf);
-		   iCodePage = m_i;
-	   }
-	   else
-	   {
-		   iCodePage = CP_BIG5;
-	   }
-   }
-
    //
    //
    // Set configurable global options
    // Set configurable global options
+   //
    gpGlobals->fIsWIN95 = dwIsDOS ? FALSE : TRUE;
    gpGlobals->fIsWIN95 = dwIsDOS ? FALSE : TRUE;
    gpGlobals->fUseEmbeddedFonts = dwIsDOS && dwUseEmbeddedFonts ? TRUE : FALSE;
    gpGlobals->fUseEmbeddedFonts = dwIsDOS && dwUseEmbeddedFonts ? TRUE : FALSE;
    gpGlobals->fUseSurroundOPL = dwUseStereo && dwUseSurroundOPL ? TRUE : FALSE;
    gpGlobals->fUseSurroundOPL = dwUseStereo && dwUseSurroundOPL ? TRUE : FALSE;
    gpGlobals->iAudioChannels = dwUseStereo ? 2 : 1;
    gpGlobals->iAudioChannels = dwUseStereo ? 2 : 1;
    gpGlobals->iSampleRate = iSampleRate;
    gpGlobals->iSampleRate = iSampleRate;
    gpGlobals->iOPLSampleRate = iOPLSampleRate;
    gpGlobals->iOPLSampleRate = iOPLSampleRate;
+   gpGlobals->iResampleQuality = iResampleQuality;
    gpGlobals->dSurroundOPLOffset = flSurroundOPLOffset;
    gpGlobals->dSurroundOPLOffset = flSurroundOPLOffset;
    gpGlobals->eMusicType = eMusicType;
    gpGlobals->eMusicType = eMusicType;
    gpGlobals->eCDType = eCDType;
    gpGlobals->eCDType = eCDType;
@@ -266,7 +232,9 @@ PAL_InitGlobals(
    gpGlobals->dwExtraMagicDescLines = dwExtraMagicDescLines;
    gpGlobals->dwExtraMagicDescLines = dwExtraMagicDescLines;
    gpGlobals->dwExtraItemDescLines = dwExtraItemDescLines;
    gpGlobals->dwExtraItemDescLines = dwExtraItemDescLines;
 
 
+   //
    // Set decompress function
    // Set decompress function
+   //
    Decompress = gpGlobals->fIsWIN95 ? YJ2_Decompress : YJ1_Decompress;
    Decompress = gpGlobals->fIsWIN95 ? YJ2_Decompress : YJ1_Decompress;
 
 
    //
    //

+ 1 - 0
global.h

@@ -618,6 +618,7 @@ typedef struct tagGLOBALVARS
    INT              iAudioChannels;
    INT              iAudioChannels;
    INT              iSampleRate;
    INT              iSampleRate;
    INT              iOPLSampleRate;
    INT              iOPLSampleRate;
+   INT              iResampleQuality;
    MUSICTYPE        eMusicType;
    MUSICTYPE        eMusicType;
    MUSICTYPE        eCDType;
    MUSICTYPE        eCDType;
    OPLTYPE          eOPLType;
    OPLTYPE          eOPLType;

+ 67 - 3
libmad/music_mad.c

@@ -24,9 +24,10 @@
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include "music_mad.h"
 #include "music_mad.h"
+#include "../resampler.h" /* SDLPAL */
 
 
 mad_data *
 mad_data *
-mad_openFile(const char *filename, SDL_AudioSpec *mixer) {
+mad_openFile(const char *filename, SDL_AudioSpec *mixer, int resampler_quality) {
   SDL_RWops *rw;
   SDL_RWops *rw;
 
 
   rw = SDL_RWFromFile(filename, "rb");
   rw = SDL_RWFromFile(filename, "rb");
@@ -34,11 +35,11 @@ mad_openFile(const char *filename, SDL_AudioSpec *mixer) {
 	return NULL;
 	return NULL;
   }
   }
 
 
-  return mad_openFileRW(rw, mixer);
+  return mad_openFileRW(rw, mixer, resampler_quality);
 }
 }
 
 
 mad_data *
 mad_data *
-mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer) {
+mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer, int resampler_quality) {
   mad_data *mp3_mad;
   mad_data *mp3_mad;
 
 
   mp3_mad = (mad_data *)malloc(sizeof(mad_data));
   mp3_mad = (mad_data *)malloc(sizeof(mad_data));
@@ -53,6 +54,9 @@ mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer) {
   mp3_mad->output_begin = 0;
   mp3_mad->output_begin = 0;
   mp3_mad->output_end = 0;
   mp3_mad->output_end = 0;
   mp3_mad->mixer = *mixer;
   mp3_mad->mixer = *mixer;
+  mp3_mad->upsample = 0; /* SDLPAL */
+  mp3_mad->resampler_quality = resampler_quality; /* SDLPAL */
+  memset(mp3_mad->resampler, 0, sizeof(mp3_mad->resampler)); /* SDLPAL */
 
 
   return mp3_mad;
   return mp3_mad;
 }
 }
@@ -185,11 +189,38 @@ decode_frame(mad_data *mp3_mad) {
   out = mp3_mad->output_buffer + mp3_mad->output_end;
   out = mp3_mad->output_buffer + mp3_mad->output_end;
 
 
   if ((mp3_mad->status & MS_cvt_decoded) == 0) {
   if ((mp3_mad->status & MS_cvt_decoded) == 0) {
+    int hi, lo; /* SDLPAL */
 	mp3_mad->status |= MS_cvt_decoded;
 	mp3_mad->status |= MS_cvt_decoded;
 
 
 	/* The first frame determines some key properties of the stream.
 	/* The first frame determines some key properties of the stream.
 	   In particular, it tells us enough to set up the convert
 	   In particular, it tells us enough to set up the convert
 	   structure now. */
 	   structure now. */
+    /* ------------------------------- SDLPAL start ------------------------------- */
+    hi = (int)mp3_mad->frame.header.samplerate > mp3_mad->mixer.freq ? mp3_mad->frame.header.samplerate : mp3_mad->mixer.freq;
+    lo = (int)mp3_mad->frame.header.samplerate < mp3_mad->mixer.freq ? mp3_mad->frame.header.samplerate : mp3_mad->mixer.freq;
+	if (hi != lo) {
+      /* Need sample rate conversion, resampler should be used. Try to create resamplers. */
+      if (mp3_mad->resampler[0] = resampler_create()) {
+        if (mp3_mad->mixer.channels == 2) {
+          if ((mp3_mad->resampler[1] = resampler_create())) {
+            resampler_set_quality(mp3_mad->resampler[1], (hi % lo == 0) ? RESAMPLER_QUALITY_MIN : mp3_mad->resampler_quality);
+            resampler_set_rate(mp3_mad->resampler[1], (double)mp3_mad->frame.header.samplerate / (double)mp3_mad->mixer.freq);
+          }
+		  else {
+            resampler_delete(mp3_mad->resampler[0]);
+            mp3_mad->resampler[0] = NULL;
+          }
+        }
+      }
+      if (mp3_mad->resampler[0]) {
+        resampler_set_quality(mp3_mad->resampler[0], mp3_mad->resampler_quality);
+        resampler_set_rate(mp3_mad->resampler[0], (double)mp3_mad->frame.header.samplerate / (double)mp3_mad->mixer.freq);
+        /* Resampler successfully created, cheat SDL for not converting sample rate */
+        mp3_mad->upsample = mp3_mad->mixer.freq > (int)mp3_mad->frame.header.samplerate;
+        mp3_mad->mixer.freq = mp3_mad->frame.header.samplerate;
+      }
+    }
+    /* ------------------------------- SDLPAL end ------------------------------- */
 	SDL_BuildAudioCVT(&mp3_mad->cvt, AUDIO_S16, (Uint8)pcm->channels, mp3_mad->frame.header.samplerate, mp3_mad->mixer.format, mp3_mad->mixer.channels, mp3_mad->mixer.freq);
 	SDL_BuildAudioCVT(&mp3_mad->cvt, AUDIO_S16, (Uint8)pcm->channels, mp3_mad->frame.header.samplerate, mp3_mad->mixer.format, mp3_mad->mixer.channels, mp3_mad->mixer.freq);
   }
   }
 
 
@@ -258,6 +289,39 @@ mad_getSamples(mad_data *mp3_mad, Uint8 *stream, int len) {
 		mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio);
 		mp3_mad->output_end = (int)(mp3_mad->output_end * mp3_mad->cvt.len_ratio);
 		/*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
 		/*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
 		SDL_ConvertAudio(&mp3_mad->cvt);
 		SDL_ConvertAudio(&mp3_mad->cvt);
+
+        /* ------------------------------- SDLPAL start ------------------------------- */
+        if (mp3_mad->resampler[0]) {
+		  int dst_samples = 0, pos = 0, i;
+		  if (mp3_mad->upsample) {
+            /* Upsample, should move memory blocks to avoid overwrite. MP3's lowest samplerate
+               is 32KHz, while SDLPAL support up to 48KHz, so maximum upsample rate is 1.5.
+               As one frame has 1152 samples, the maximum bytes used will be 6912 bytes. So
+               it is safe by first moving memory blocks to the end of the buffer */
+            pos = sizeof(mp3_mad->output_buffer) - mp3_mad->output_end;
+            memmove(mp3_mad->output_buffer + pos, mp3_mad->output_buffer, mp3_mad->output_end);
+          }
+          
+          for (i = 0; i < mp3_mad->mixer.channels; i++) {
+            short *src = (short *)(mp3_mad->output_buffer + pos) + i;
+            short *dst = (short *)mp3_mad->output_buffer + i;
+            int src_samples = 1152; /* Defined by MP3 specification */
+            while(src_samples > 0) {
+              int to_write = resampler_get_free_count(mp3_mad->resampler[i]), j;
+              for (j = 0; j < to_write; j++) {
+                resampler_write_sample(mp3_mad->resampler[i], SDL_SwapLE16(*src));
+                src += mp3_mad->mixer.channels;
+              }
+			  src_samples -= to_write;
+              while (resampler_get_sample_count(mp3_mad->resampler[i]) > 0) {
+                *dst = SDL_SwapLE16(resampler_get_and_remove_sample(mp3_mad->resampler[i]));
+				dst += mp3_mad->mixer.channels; dst_samples++;
+              }
+            }
+          }
+          mp3_mad->output_end = dst_samples * (SDL_AUDIO_BITSIZE(mp3_mad->mixer.format) >> 3);
+        }
+        /* ------------------------------- SDLPAL end ------------------------------- */
 	  }
 	  }
 	}
 	}
 
 

+ 5 - 2
libmad/music_mad.h

@@ -40,6 +40,7 @@ enum {
 
 
 typedef struct {
 typedef struct {
   SDL_RWops *rw;
   SDL_RWops *rw;
+  void *resampler[2];	/* SDLPAL: Avoid to use SDL's stupid resampler */
   struct mad_stream stream;
   struct mad_stream stream;
   struct mad_frame frame;
   struct mad_frame frame;
   struct mad_synth synth;
   struct mad_synth synth;
@@ -48,6 +49,8 @@ typedef struct {
   int volume;
   int volume;
   int status;
   int status;
   int output_begin, output_end;
   int output_begin, output_end;
+  int upsample; /* SDLPAL: Is upsample or downsample */
+  int resampler_quality; /* SDLPAL:resampler quality */
   SDL_AudioSpec mixer;
   SDL_AudioSpec mixer;
   SDL_AudioCVT cvt;
   SDL_AudioCVT cvt;
 
 
@@ -55,8 +58,8 @@ typedef struct {
   unsigned char output_buffer[MAD_OUTPUT_BUFFER_SIZE];
   unsigned char output_buffer[MAD_OUTPUT_BUFFER_SIZE];
 } mad_data;
 } mad_data;
 
 
-mad_data *mad_openFile(const char *filename, SDL_AudioSpec *mixer);
-mad_data *mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer);
+mad_data *mad_openFile(const char *filename, SDL_AudioSpec *mixer, int resampler_quality);
+mad_data *mad_openFileRW(SDL_RWops *rw, SDL_AudioSpec *mixer, int resampler_quality);
 void mad_closeFile(mad_data *mp3_mad);
 void mad_closeFile(mad_data *mp3_mad);
 
 
 void mad_start(mad_data *mp3_mad);
 void mad_start(mad_data *mp3_mad);

+ 4 - 2
mp3player.c

@@ -18,11 +18,13 @@
 //
 //
 
 
 #include "util.h"
 #include "util.h"
+#include "global.h"
 
 
 #if PAL_HAS_MP3
 #if PAL_HAS_MP3
 
 
 #include "sound.h"
 #include "sound.h"
 #include "players.h"
 #include "players.h"
+#include "resampler.h"
 #include "libmad/music_mad.h"
 #include "libmad/music_mad.h"
 
 
 typedef struct tagMP3PLAYER
 typedef struct tagMP3PLAYER
@@ -109,9 +111,9 @@ MP3_Play(
 
 
 	if (iNum > 0)
 	if (iNum > 0)
 	{
 	{
-		if ((player->pMP3 = mad_openFile(va("%s/mp3/%.2d.mp3", PAL_PREFIX, iNum), SOUND_GetAudioSpec())) == NULL)
+		if ((player->pMP3 = mad_openFile(va("%s/mp3/%.2d.mp3", PAL_PREFIX, iNum), SOUND_GetAudioSpec(), gpGlobals->iResampleQuality)) == NULL)
 		{
 		{
-			player->pMP3 = mad_openFile(va("%s/MP3/%.2d.MP3", PAL_PREFIX, iNum), SOUND_GetAudioSpec());
+			player->pMP3 = mad_openFile(va("%s/MP3/%.2d.MP3", PAL_PREFIX, iNum), SOUND_GetAudioSpec(), gpGlobals->iResampleQuality);
 		}
 		}
 
 
 		if (player->pMP3)
 		if (player->pMP3)

+ 82 - 98
oggplay.c

@@ -27,11 +27,7 @@
 #if PAL_HAS_OGG
 #if PAL_HAS_OGG
 #include <vorbis\vorbisfile.h>
 #include <vorbis\vorbisfile.h>
 
 
-#define USE_RESAMPLER    1
-
-#if USE_RESAMPLER
 #include "resampler.h"
 #include "resampler.h"
-#endif
 
 
 #define FLAG_OY 0x01
 #define FLAG_OY 0x01
 #define FLAG_VI 0x02
 #define FLAG_VI 0x02
@@ -60,14 +56,14 @@ typedef struct tagOGGPLAYER
 	vorbis_block     vb; /* local working space for packet->PCM decode */
 	vorbis_block     vb; /* local working space for packet->PCM decode */
 
 
 	FILE            *fp;
 	FILE            *fp;
-#if USE_RESAMPLER
 	void            *resampler[2];
 	void            *resampler[2];
-#endif
 	INT              iFlags;
 	INT              iFlags;
 	INT              iMusic;
 	INT              iMusic;
 	INT              iStage;
 	INT              iStage;
+	INT              nChannels;
 	BOOL             fLoop;
 	BOOL             fLoop;
 	BOOL             fReady;
 	BOOL             fReady;
+	BOOL             fUseResampler;
 } OGGPLAYER, *LPOGGPLAYER;
 } OGGPLAYER, *LPOGGPLAYER;
 
 
 static SDL_FORCE_INLINE ogg_int16_t OGG_GetSample(float pcm, int volume)
 static SDL_FORCE_INLINE ogg_int16_t OGG_GetSample(float pcm, int volume)
@@ -80,33 +76,29 @@ static SDL_FORCE_INLINE ogg_int16_t OGG_GetSample(float pcm, int volume)
 	else if (val < -32768) {
 	else if (val < -32768) {
 		val = -32768;
 		val = -32768;
 	}
 	}
-	return SWAP16((ogg_int16_t)val);
+	return (ogg_int16_t)val;
 }
 }
 
 
-#if USE_RESAMPLER
 static SDL_FORCE_INLINE void OGG_FillResample(LPOGGPLAYER player, ogg_int16_t* stream)
 static SDL_FORCE_INLINE void OGG_FillResample(LPOGGPLAYER player, ogg_int16_t* stream)
 {
 {
-	int i;
-	if (player->vi.channels >= gpGlobals->iAudioChannels) {
-		for (i = 0; i < gpGlobals->iAudioChannels; i++) {
-			stream[i] = resampler_get_and_remove_sample(player->resampler[i]);
-		}
+	if (gpGlobals->iAudioChannels == 2) {
+		stream[0] = SDL_SwapLE16(resampler_get_and_remove_sample(player->resampler[0]));
+		stream[1] = (player->vi.channels > 1) ? SDL_SwapLE16(resampler_get_and_remove_sample(player->resampler[1])) : stream[0];
 	}
 	}
 	else {
 	else {
-		ogg_int16_t val = resampler_get_and_remove_sample(player->resampler[0]);
-		for (i = 0; i < gpGlobals->iAudioChannels; i++) {
-			stream[i] = val;
+		if (player->vi.channels > 1) {
+			*stream = SDL_SwapLE16((short)((int)(resampler_get_and_remove_sample(player->resampler[0]) + resampler_get_and_remove_sample(player->resampler[1])) >> 1));
+		}
+		else {
+			*stream = SDL_SwapLE16(resampler_get_and_remove_sample(player->resampler[0]));
 		}
 		}
 	}
 	}
 }
 }
-#endif
 
 
 static void OGG_Cleanup(LPOGGPLAYER player)
 static void OGG_Cleanup(LPOGGPLAYER player)
 {
 {
-#if USE_RESAMPLER
 	int i;
 	int i;
 	for (i = 0; i < gpGlobals->iAudioChannels; i++) resampler_clear(player->resampler[0]);
 	for (i = 0; i < gpGlobals->iAudioChannels; i++) resampler_clear(player->resampler[0]);
-#endif
 	/* Do various cleanups */
 	/* Do various cleanups */
 	if (player->iFlags & FLAG_VB) vorbis_block_clear(&player->vb);
 	if (player->iFlags & FLAG_VB) vorbis_block_clear(&player->vb);
 	if (player->iFlags & FLAG_VD) vorbis_dsp_clear(&player->vd);
 	if (player->iFlags & FLAG_VD) vorbis_dsp_clear(&player->vd);
@@ -239,13 +231,16 @@ static BOOL OGG_Rewind(LPOGGPLAYER player)
 																for vd here */
 																for vd here */
 		player->iStage = STAGE_PAGEOUT;
 		player->iStage = STAGE_PAGEOUT;
 		player->iFlags |= FLAG_VD | FLAG_VB;
 		player->iFlags |= FLAG_VD | FLAG_VB;
-#if USE_RESAMPLER
-		if (player->vi.rate != gpGlobals->iSampleRate) {
-			for (i = 0; i < gpGlobals->iAudioChannels; i++) {
-				resampler_set_rate(player->resampler[i], player->vi.rate / (double)gpGlobals->iSampleRate);
+
+		if (player->fUseResampler = player->vi.rate != gpGlobals->iSampleRate) {
+			double factor = (double)player->vi.rate / (double)gpGlobals->iSampleRate;
+			for (i = 0; i < min(player->vi.channels, 2); i++)
+			{
+				resampler_set_quality(player->resampler[i], SOUND_IsIntegerConversion(player->vi.rate) ? RESAMPLER_QUALITY_MIN : gpGlobals->iResampleQuality);
+				resampler_set_rate(player->resampler[i], factor);
+				resampler_clear(player->resampler[i]);
 			}
 			}
 		}
 		}
-#endif
 		return (player->fReady = TRUE);
 		return (player->fReady = TRUE);
 	}
 	}
 	else {
 	else {
@@ -324,59 +319,52 @@ OGG_FillBuffer(
 				}
 				}
 			case STAGE_PCMOUT:
 			case STAGE_PCMOUT:
 				if ((samples = vorbis_synthesis_pcmout(&player->vd, &pcm)) > 0) {
 				if ((samples = vorbis_synthesis_pcmout(&player->vd, &pcm)) > 0) {
-					int bout = (len - total_bytes) / gpGlobals->iAudioChannels / sizeof(ogg_int16_t);
-
-					if (bout > samples) bout = samples;
-#if USE_RESAMPLER
-					if (player->vi.rate != gpGlobals->iSampleRate) { /* Samplerate not same, use resampler */
-						int i, j, to_write;
-						
-						while (samples > 0) { /* Fill as many samples into resampler as possible */
-							to_write = resampler_get_free_count(player->resampler[0]);
-							if (to_write >= samples) to_write = samples;
-							samples -= to_write;
-
-							for (i = 0; i < min(player->vi.channels, gpGlobals->iAudioChannels); i++) {
-								float *mono = pcm[i];
-								for (j = 0; j < to_write; j++) {
-									resampler_write_sample(player->resampler[i], OGG_GetSample(mono[j], volume));
+					int bout;
+					if (player->fUseResampler) { /* Resampler is available and should be used */
+						bout = 0;
+						while (total_bytes < len && samples > 0) { /* Fill as many samples into resampler as possible */
+							int i, j, to_write = resampler_get_free_count(player->resampler[0]);
+
+							if (to_write > 0) {
+								if (to_write >= samples) to_write = samples;
+
+								for (i = 0; i < min(player->vi.channels, 2); i++) {
+									float *mono = pcm[i] + bout;
+									for (j = 0; j < to_write; j++) {
+										resampler_write_sample(player->resampler[i], OGG_GetSample(mono[j], volume));
+									}
 								}
 								}
 							}
 							}
 
 
 							/* Fetch resampled samples if available */
 							/* Fetch resampled samples if available */
-							while (total_bytes < len && resampler_get_sample_count(player->resampler[0])) {
+							j = resampler_get_sample_count(player->resampler[0]);
+							while (total_bytes < len && resampler_get_sample_count(player->resampler[0]) > 0) {
 								OGG_FillResample(player, (ogg_int16_t *)(stream + total_bytes));
 								OGG_FillResample(player, (ogg_int16_t *)(stream + total_bytes));
 								total_bytes += gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
 								total_bytes += gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
 							}
 							}
+
+							samples -= to_write; bout += to_write;
 						}
 						}
 					}
 					}
-					else
-#endif
-					{
-						int i, j;
-
-						if (player->vi.channels >= gpGlobals->iAudioChannels)
-						{
-							/* convert floats to 16 bit signed ints (host order) and interleave */
-							for (i = 0; i < gpGlobals->iAudioChannels; i++) {
-								ogg_int16_t *ptr = (ogg_int16_t *)(stream + total_bytes) + i;
-								float *mono = pcm[i];
-								for (j = 0; j < bout; j++) {
-									*ptr = OGG_GetSample(mono[j], volume);
-									ptr += gpGlobals->iAudioChannels;
-								}
+					else {
+						int i;
+						ogg_int16_t *ptr = (ogg_int16_t *)(stream + total_bytes);
+						bout = (len - total_bytes) / gpGlobals->iAudioChannels / sizeof(ogg_int16_t);
+						if (bout > samples) bout = samples;
+						for (i = 0; i < bout; i++) {
+							if (gpGlobals->iAudioChannels == 2) {
+								ptr[0] = SDL_SwapLE16(OGG_GetSample(pcm[0][i], volume));
+								ptr[1] = (player->vi.channels > 1) ? SDL_SwapLE16(OGG_GetSample(pcm[1][i], volume)) : ptr[0];
 							}
 							}
-						}
-						else
-						{
-							ogg_int16_t *ptr = (ogg_int16_t *)(stream + total_bytes);
-							float *mono = pcm[0];
-							for (j = 0; j < bout; j++) {
-								ogg_int16_t val = OGG_GetSample(mono[j], volume);
-								for (i = 0; i < gpGlobals->iAudioChannels; i++) {
-									*ptr++ = val;
+							else {
+								if (player->vi.channels > 1) {
+									ptr[0] = SDL_SwapLE16((short)((int)(OGG_GetSample(pcm[0][i], volume) + OGG_GetSample(pcm[1][i], volume)) >> 1));
+								}
+								else {
+									ptr[0] = SDL_SwapLE16(OGG_GetSample(pcm[0][i], volume));
 								}
 								}
 							}
 							}
+							ptr += gpGlobals->iAudioChannels;
 						}
 						}
 
 
 						total_bytes += bout * gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
 						total_bytes += bout * gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
@@ -389,16 +377,14 @@ OGG_FillBuffer(
 				}
 				}
 				break;
 				break;
 			case STAGE_REWIND:
 			case STAGE_REWIND:
-#if USE_RESAMPLER
 				if (player->vi.rate != gpGlobals->iSampleRate) { /* If there are samples in the resampler, fetch them first */
 				if (player->vi.rate != gpGlobals->iSampleRate) { /* If there are samples in the resampler, fetch them first */
-					while (total_bytes < len && resampler_get_sample_count(player->resampler[0])) {
+					while (total_bytes < len && resampler_get_sample_count(player->resampler[0]) > 0) {
 						OGG_FillResample(player, (ogg_int16_t *)(stream + total_bytes));
 						OGG_FillResample(player, (ogg_int16_t *)(stream + total_bytes));
 						total_bytes += gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
 						total_bytes += gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
 					}
 					}
 					/* Break out if there are still samples in the resampler */
 					/* Break out if there are still samples in the resampler */
-					if (resampler_get_sample_count(player->resampler[0])) break;
+					if (resampler_get_sample_count(player->resampler[0]) > 0) break;
 				}
 				}
-#endif
 				OGG_Rewind(player);
 				OGG_Rewind(player);
 				stage = player->iStage;
 				stage = player->iStage;
 				break;
 				break;
@@ -410,27 +396,6 @@ OGG_FillBuffer(
 	}
 	}
 }
 }
 
 
-static VOID
-OGG_Shutdown(
-	VOID       *object
-	)
-{
-	if (object)
-	{
-		LPOGGPLAYER player = (LPOGGPLAYER)object;
-#if USE_RESAMPLER
-		int i;
-#endif
-		OGG_Cleanup(player);
-#if USE_RESAMPLER
-		for (i = 0; i < gpGlobals->iAudioChannels; i++)
-			resampler_delete(player->resampler[i]);
-#endif
-		if (player->fp) UTIL_CloseFile(player->fp);
-		free(player);
-	}
-}
-
 static BOOL
 static BOOL
 OGG_Play(
 OGG_Play(
 	VOID       *object,
 	VOID       *object,
@@ -490,6 +455,22 @@ OGG_Play(
 	return TRUE;
 	return TRUE;
 }
 }
 
 
+static VOID
+OGG_Shutdown(
+	VOID       *object
+	)
+{
+	if (object)
+	{
+		LPOGGPLAYER player = (LPOGGPLAYER)object;
+		OGG_Cleanup(player);
+		resampler_delete(player->resampler[0]);
+		resampler_delete(player->resampler[1]);
+		UTIL_CloseFile(player->fp);
+		free(player);
+	}
+}
+
 LPMUSICPLAYER
 LPMUSICPLAYER
 OGG_Init(
 OGG_Init(
 	LPCSTR szFileName
 	LPCSTR szFileName
@@ -498,9 +479,6 @@ OGG_Init(
 	LPOGGPLAYER player;
 	LPOGGPLAYER player;
 	if (player = (LPOGGPLAYER)malloc(sizeof(OGGPLAYER)))
 	if (player = (LPOGGPLAYER)malloc(sizeof(OGGPLAYER)))
 	{
 	{
-#if USE_RESAMPLER
-		int i;
-#endif
 		memset(player, 0, sizeof(LPOGGPLAYER));
 		memset(player, 0, sizeof(LPOGGPLAYER));
 
 
 		player->FillBuffer = OGG_FillBuffer;
 		player->FillBuffer = OGG_FillBuffer;
@@ -513,13 +491,19 @@ OGG_Init(
 		player->iStage = 0;
 		player->iStage = 0;
 		player->fLoop = FALSE;
 		player->fLoop = FALSE;
 		player->fReady = FALSE;
 		player->fReady = FALSE;
-#if USE_RESAMPLER
-		for (i = 0; i < gpGlobals->iAudioChannels; i++)
+		player->fUseResampler = FALSE;
+
+		player->resampler[0] = resampler_create();
+		if (player->resampler[0])
 		{
 		{
-			player->resampler[i] = resampler_create();
-			resampler_set_quality(player->resampler[i], RESAMPLER_QUALITY_MAX);
+			player->resampler[1] = resampler_create();
+			if (player->resampler[1] == NULL)
+			{
+				resampler_delete(player->resampler[0]);
+				player->resampler[0] = NULL;
+			}
 		}
 		}
-#endif
+
 		return (LPMUSICPLAYER)player;
 		return (LPMUSICPLAYER)player;
 	}
 	}
 	else
 	else

+ 3 - 3
resampler.c

@@ -949,10 +949,10 @@ short resampler_get_and_remove_sample(void *_r)
 {
 {
 	int sample = (int)round(resampler_get_sample(_r) / 256.0);
 	int sample = (int)round(resampler_get_sample(_r) / 256.0);
 	resampler_remove_sample(_r);
 	resampler_remove_sample(_r);
-	if (sample >= 32767)
+	if (sample > 32767)
 		return 32767;
 		return 32767;
-	else if (sample <= -32767)
-		return -32767;
+	else if (sample < -32768)
+		return -32768;
 	else
 	else
 		return (short)sample;
 		return (short)sample;
 }
 }

+ 2 - 2
rixplay.cpp

@@ -226,7 +226,7 @@ RIX_FillBuffer(
 		SHORT* ptr = (SHORT*)stream;
 		SHORT* ptr = (SHORT*)stream;
 		for (i = 0; i < (int)(l / sizeof(SHORT)); i++)
 		for (i = 0; i < (int)(l / sizeof(SHORT)); i++)
 		{
 		{
-			*ptr++ = SWAP16((int)(*(SHORT *)(pRixPlayer->pos)) * volume / SDL_MIX_MAXVOLUME);
+			*ptr++ = SDL_SwapLE16((int)(*(SHORT *)(pRixPlayer->pos)) * volume / SDL_MIX_MAXVOLUME);
 			pRixPlayer->pos += sizeof(SHORT);
 			pRixPlayer->pos += sizeof(SHORT);
 		}
 		}
 		stream = (LPBYTE)ptr;
 		stream = (LPBYTE)ptr;
@@ -417,7 +417,7 @@ RIX_Init(
 		for (int i = 0; i < gpGlobals->iAudioChannels; i++)
 		for (int i = 0; i < gpGlobals->iAudioChannels; i++)
 		{
 		{
 			pRixPlayer->resampler[i] = resampler_create();
 			pRixPlayer->resampler[i] = resampler_create();
-			resampler_set_quality(pRixPlayer->resampler[i], RESAMPLER_QUALITY_MAX);
+			resampler_set_quality(pRixPlayer->resampler[i], SOUND_IsIntegerConversion(gpGlobals->iOPLSampleRate) ? RESAMPLER_QUALITY_MIN : gpGlobals->iResampleQuality);
 			resampler_set_rate(pRixPlayer->resampler[i], (double)gpGlobals->iOPLSampleRate / (double)gpGlobals->iSampleRate);
 			resampler_set_rate(pRixPlayer->resampler[i], (double)gpGlobals->iOPLSampleRate / (double)gpGlobals->iSampleRate);
 		}
 		}
 	}
 	}

+ 6 - 2
sdlpal.cfg.example

@@ -4,8 +4,8 @@
 # Lines started with '#' is treated as comments.    #
 # Lines started with '#' is treated as comments.    #
 #####################################################
 #####################################################
 
 
-# CodePage: Indicates the codepage to use, 0 for big5, 1 for gbk, 2 for shift-jis.
-#           If not specified, SDLPAL will try to detect automatically (default).
+# CodePage: Indicates the codepage to use, 0 for big5 (default), 1 for gbk,
+#           2 for shift-jis.
 #CodePage=0
 #CodePage=0
 
 
 # WordLength: Idicates the length of each word, 10 for chinese version (default),
 # WordLength: Idicates the length of each word, 10 for chinese version (default),
@@ -71,3 +71,7 @@
 # SurroundOPLOffset: Indicates the frequency offset of surround opl. The default and
 # SurroundOPLOffset: Indicates the frequency offset of surround opl. The default and
 #                    recommended value is 384.0, but other values may also be used.
 #                    recommended value is 384.0, but other values may also be used.
 #SurroundOPLOffset=384.0
 #SurroundOPLOffset=384.0
+
+# ResampleQuality: Indicates the resample quality, valid values from 0 to 4 (default).
+#                  Larger value indicates higher quality and higher hardware requirement.
+#ResampleQuality=4

+ 1 - 1
sdlpal.vcxproj

@@ -222,7 +222,7 @@
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     </ClCompile>
-    <ClCompile Include="mp3player.c" />
+    <ClCompile Include="mp3play.c" />
     <ClCompile Include="oggplay.c" />
     <ClCompile Include="oggplay.c" />
     <ClCompile Include="palcommon.c">
     <ClCompile Include="palcommon.c">
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

+ 1 - 1
sdlpal.vcxproj.filters

@@ -290,7 +290,7 @@
     <ClCompile Include="oggplay.c">
     <ClCompile Include="oggplay.c">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
-    <ClCompile Include="mp3player.c">
+    <ClCompile Include="mp3play.c">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
   </ItemGroup>
   </ItemGroup>

+ 380 - 123
sound.c

@@ -25,6 +25,7 @@
 #include "players.h"
 #include "players.h"
 #include "util.h"
 #include "util.h"
 #include "resampler.h"
 #include "resampler.h"
+#include <math.h>
 
 
 #ifdef PAL_HAS_NATIVEMIDI
 #ifdef PAL_HAS_NATIVEMIDI
 #include "midi.h"
 #include "midi.h"
@@ -39,8 +40,6 @@ static BOOL  gSndOpened = FALSE;
 BOOL         g_fNoSound = FALSE;
 BOOL         g_fNoSound = FALSE;
 BOOL         g_fNoMusic = FALSE;
 BOOL         g_fNoMusic = FALSE;
 
 
-static BOOL  g_fUseWav = FALSE;
-
 #ifdef __SYMBIAN32__
 #ifdef __SYMBIAN32__
 INT          g_iVolume  = SDL_MIX_MAXVOLUME * 0.1;
 INT          g_iVolume  = SDL_MIX_MAXVOLUME * 0.1;
 #endif
 #endif
@@ -51,6 +50,8 @@ int          g_iCurrChannel = 0;
 
 
 #define PAL_CDTRACK_BASE    10000
 #define PAL_CDTRACK_BASE    10000
 
 
+typedef LPCBYTE(*FNLoadSoundData)(LPCBYTE, DWORD, SDL_AudioSpec *);
+
 typedef struct tagSNDPLAYER
 typedef struct tagSNDPLAYER
 {
 {
    FILE                     *mkf;
    FILE                     *mkf;
@@ -58,126 +59,362 @@ typedef struct tagSNDPLAYER
    SDL_mutex                *mtx;
    SDL_mutex                *mtx;
    LPBYTE                    buf[2], pos[2];
    LPBYTE                    buf[2], pos[2];
    INT                       audio_len[2];
    INT                       audio_len[2];
+   void                     *resampler;
    MUSICPLAYER              *pMusPlayer;
    MUSICPLAYER              *pMusPlayer;
    MUSICPLAYER              *pCDPlayer;
    MUSICPLAYER              *pCDPlayer;
 #if PAL_HAS_SDLCD
 #if PAL_HAS_SDLCD
    SDL_CD                   *pCD;
    SDL_CD                   *pCD;
 #endif
 #endif
+   FNLoadSoundData           LoadSoundData;
 } SNDPLAYER;
 } SNDPLAYER;
 
 
 static SNDPLAYER gSndPlayer;
 static SNDPLAYER gSndPlayer;
 
 
-static SDL_AudioSpec *
-SOUND_LoadVOCFromBuffer(
-   LPCBYTE                lpVOC,
-   DWORD                  dwLen,
-   SDL_AudioSpec         *lpSpec,
-   LPBYTE                *lppBuffer
-)
+typedef struct tagRIFFHEADER
+{
+	DWORD   riff_sig;	/* 'RIFF' */
+	DWORD   data_length;	/* Total length minus eight, little-endian */
+	DWORD   riff_type;	/* 'WAVE' */
+} RIFFHEADER, *LPRIFFHEADER;
+typedef const RIFFHEADER *LPCRIFFHEADER;
+
+typedef struct tagRIFFCHUNK
+{
+	DWORD   chunk_type;	/* 'fmt ' and so on */
+	DWORD   chunk_length;	/* Total chunk length minus eight, little-endian */
+} RIFFCHUNK, *LPRIFFCHUNK;
+typedef const RIFFCHUNK *LPCRIFFCHUNK;
+
+typedef struct tagWAVEFORMATPCM
+{
+	WORD    wFormatTag;        /* format type */
+	WORD    nChannels;         /* number of channels (i.e. mono, stereo, etc.) */
+	DWORD   nSamplesPerSec;    /* sample rate */
+	DWORD   nAvgBytesPerSec;   /* for buffer estimation */
+	WORD    nBlockAlign;       /* block size of data */
+	WORD    wBitsPerSample;
+} WAVEFORMATPCM, *LPWAVEFORMATPCM;
+typedef const WAVEFORMATPCM *LPCWAVEFORMATPCM;
+
+static LPCBYTE
+SOUND_LoadWAVEData(
+	LPCBYTE                lpData,
+	DWORD                  dwLen,
+	SDL_AudioSpec         *lpSpec
+	)
+	/*++
+		Purpose:
+
+		Return the WAVE data pointer inside the input buffer.
+
+		Parameters:
+
+		[IN]  lpData - pointer to the buffer of the WAVE file.
+
+		[IN]  dwLen - length of the buffer of the WAVE file.
+
+		[OUT] lpSpec - pointer to the SDL_AudioSpec structure, which contains
+                       some basic information about the WAVE file.
+
+		Return value:
+
+		Pointer to the WAVE data inside the input buffer, NULL if failed.
+
+	--*/
+{
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#	define RIFF		'RIFF'
+#	define WAVE		'WAVE'
+#	define FMT		'fmt '
+#	define DATA		'data'
+#	define PCM     0x0100
+#else
+#	define RIFF		'FFIR'
+#	define WAVE		'EVAW'
+#	define FMT		' tmf'
+#	define DATA		'atad'
+#	define PCM     0x0001
+#endif
+	LPCRIFFHEADER lpRiff = (LPCRIFFHEADER)lpData;
+	LPCRIFFCHUNK lpChunk;
+	LPCWAVEFORMATPCM lpFormat = NULL;
+	LPCBYTE lpWaveData = NULL;
+	DWORD len;
+
+	if (dwLen < sizeof(RIFFHEADER) || lpRiff->riff_sig != RIFF || lpRiff->riff_type != WAVE || dwLen < SDL_SwapLE32(lpRiff->data_length) + 8)
+	{
+		return NULL;
+	}
+
+	lpChunk = (LPCRIFFCHUNK)(lpRiff + 1); dwLen -= sizeof(RIFFHEADER);
+	while (dwLen >= sizeof(RIFFCHUNK))
+	{
+		len = SDL_SwapLE32(lpChunk->chunk_length);
+		if (dwLen >= sizeof(RIFFCHUNK) + len)
+			dwLen -= sizeof(RIFFCHUNK) + len;
+		else
+			return NULL;
+
+		switch (lpChunk->chunk_type)
+		{
+		case FMT:
+			lpFormat = (LPCWAVEFORMATPCM)(lpChunk + 1);
+			if (len != sizeof(WAVEFORMATPCM) || lpFormat->wFormatTag != PCM)
+			{
+				return NULL;
+			}
+			break;
+		case DATA:
+			lpWaveData = (LPCBYTE)(lpChunk + 1);
+			dwLen = 0;
+			break;
+		}
+		lpChunk = (LPCRIFFCHUNK)((LPCBYTE)(lpChunk + 1) + len);
+	}
+
+	if (lpFormat == NULL || lpWaveData == NULL)
+	{
+		return NULL;
+	}
+
+	lpSpec->channels = lpFormat->nChannels;
+	lpSpec->format = (lpFormat->wBitsPerSample == 16) ? AUDIO_S16 : AUDIO_U8;
+	lpSpec->freq = lpFormat->nSamplesPerSec;
+	lpSpec->size = len;
+
+	return lpWaveData;
+
+#undef RIFF
+#undef WAVE
+#undef FMT
+}
+
+typedef struct tagVOCHEADER
+{
+	char    signature[0x14];	/* "Creative Voice File\x1A" */
+	WORD    data_offset;		/* little endian */
+	WORD	version;
+	WORD	version_checksum;
+} VOCHEADER, *LPVOCHEADER;
+typedef const VOCHEADER *LPCVOCHEADER;
+
+static LPCBYTE
+SOUND_LoadVOCData(
+	LPCBYTE                lpData,
+	DWORD                  dwLen,
+	SDL_AudioSpec         *lpSpec
+	)
 /*++
 /*++
-  Purpose:
+	Purpose:
 
 
-    Load a VOC file in a buffer. Currently supports type 01 block only.
+	Return the VOC data pointer inside the input buffer. Currently supports type 01 block only.
 
 
-  Parameters:
+	Parameters:
 
 
-    [IN]  lpVOC - pointer to the buffer of the VOC file.
+	[IN]  lpData - pointer to the buffer of the VOC file.
 
 
-    [IN]  dwLen - length of the buffer of the VOC file.
+	[IN]  dwLen - length of the buffer of the VOC file.
 
 
-    [OUT] lpSpec - pointer to the SDL_AudioSpec structure, which contains
+	[OUT] lpSpec - pointer to the SDL_AudioSpec structure, which contains
                    some basic information about the VOC file.
                    some basic information about the VOC file.
 
 
-    [OUT] lppBuffer - the output buffer.
-
-  Return value:
+	Return value:
 
 
-    Pointer to the SDL_AudioSpec structure, NULL if failed.
+	Pointer to the WAVE data inside the input buffer, NULL if failed.
 
 
+	Reference: http://sox.sourceforge.net/AudioFormats-11.html
 --*/
 --*/
 {
 {
-   INT freq, len, x, i, l;
-   SDL_RWops *rw;
+	LPCVOCHEADER lpVOC = (LPCVOCHEADER)lpData;
+
+	if (dwLen < sizeof(VOCHEADER) || memcmp(lpVOC->signature, "Creative Voice File\x1A", 0x14) || SDL_SwapLE16(lpVOC->data_offset) >= dwLen)
+	{
+		return NULL;
+	}
+
+	lpData += SDL_SwapLE16(lpVOC->data_offset);
+	dwLen -= SDL_SwapLE16(lpVOC->data_offset);
+
+	while (dwLen && *lpData)
+	{
+		DWORD len;
+		if (dwLen >= 4)
+		{
+			len = lpData[1] | (lpData[2] << 8) | (lpData[3] << 16);
+			if (dwLen >= len + 4)
+				dwLen -= len + 4;
+			else
+				return NULL;
+		}
+		else
+		{
+			return NULL;
+		}
+		if (*lpData == 0x01)
+		{
+			if (lpData[5] != 0) return NULL;	/* Only 8-bit is supported */
+
+			lpSpec->format = AUDIO_U8;
+			lpSpec->channels = 1;
+			lpSpec->freq = ((1000000 / (256 - lpData[4]) + 99) / 100) * 100; /* Round to next 100Hz */
+			lpSpec->size = len - 2;
+
+			return lpData + 6;
+		}
+		else
+		{
+			lpData += len + 4;
+		}
+	}
+
+	return NULL;
+}
 
 
-   if (g_fUseWav)
-   {
-      rw = SDL_RWFromConstMem(lpVOC, dwLen);
-      if (rw == NULL) return NULL;
+static void
+SOUND_ResampleU8(
+	LPCBYTE                lpData,
+	const SDL_AudioSpec   *lpSpec,
+	LPBYTE                 lpBuffer,
+	DWORD                  dwLen,
+	void                  *resampler
+	)
+/*++
+	Purpose:
 
 
-      len = dwLen;
+	Resample 8-bit unsigned PCM data into 16-bit signed (little-endian) PCM data.
 
 
-      SDL_LoadWAV_RW(rw, 1, lpSpec, lppBuffer, (Uint32 *)&len);
-      lpSpec->size = len;
+	Parameters:
 
 
-      return lpSpec;
-   }
-   else
-   {
-      //
-      // Skip header
-      //
-      lpVOC += 0x1B;
+	[IN]  lpData - pointer to the buffer of the input PCM data.
 
 
-      //
-      // Length is 3 bytes long
-      //
-      len = (lpVOC[0] | (lpVOC[1] << 8) | (lpVOC[2] << 16)) - 2;
-      lpVOC += 3;
+	[IN]  lpSpec - pointer to the SDL_AudioSpec structure, which contains
+                   some basic information about the input PCM data.
 
 
-      //
-      // One byte for frequency
-      //
-      freq = 1000000 / (256 - *lpVOC);
+	[IN]  lpBuffer - pointer of the buffer of the output PCM data.
 
 
-#if 1
+	[IN]  dwLen - length of the buffer of the output PCM data, should be exactly
+                  the number of bytes needed of the resampled data.
 
 
-      lpVOC += 2;
+	[IN]  resampler - pointer of the resampler instance.
 
 
-      //
-      // Convert the sample manually, as SDL doesn't like "strange" sample rates.
-      //
-      x = (INT)(len * ((FLOAT)gpGlobals->iSampleRate / freq));
+	Return value:
 
 
-      *lppBuffer = (LPBYTE)calloc(1, x);
-      if (*lppBuffer == NULL)
-      {
-         return NULL;
-      }
-      for (i = 0; i < x; i++)
-      {
-         l = (INT)(i * (freq / (FLOAT)gpGlobals->iSampleRate));
-         if (l >= len)
-         {
-            l = len - 1;
-         }
-         (*lppBuffer)[i] = lpVOC[l];
-      }
+	None.
+--*/
+{
+	int src_samples = lpSpec->size / lpSpec->channels, i;
+
+	for (i = 0; i < lpSpec->channels; i++)
+	{
+		LPCBYTE src = lpData + i;
+		short *dst = (short *)lpBuffer + i;
+		int channel_len = dwLen / lpSpec->channels, total_bytes = 0;
+
+		resampler_clear(resampler);
+		while (total_bytes < channel_len && src_samples > 0)
+		{
+			int to_write, j;
+			to_write = resampler_get_free_count(resampler);
+			if (to_write > src_samples) to_write = src_samples;
+			for (j = 0; j < to_write; j++)
+			{
+				resampler_write_sample(resampler, (*src ^ 0x80) << 8);
+				src += lpSpec->channels;
+			}
+			src_samples -= to_write;
+			while (total_bytes < channel_len && resampler_get_sample_count(resampler) > 0)
+			{
+				*dst = SDL_SwapLE16(resampler_get_and_remove_sample(resampler));
+				dst += lpSpec->channels; total_bytes += (SDL_AUDIO_BITSIZE(AUDIO_S16) >> 3);
+			}
+		}
+		/* Flush resampler's output buffer */
+		while (total_bytes < channel_len)
+		{
+			int j, to_write = resampler_get_free_count(resampler);
+			for (j = 0; j < to_write; j++)
+				resampler_write_sample(resampler, (src[-lpSpec->channels] ^ 0x80) << 8);
+			while (total_bytes < channel_len && resampler_get_sample_count(resampler) > 0)
+			{
+				*dst = SDL_SwapLE16(resampler_get_and_remove_sample(resampler));
+				dst += lpSpec->channels; total_bytes += (SDL_AUDIO_BITSIZE(AUDIO_S16) >> 3);
+			}
+		}
+	}
+}
 
 
-      lpSpec->channels = 1;
-      lpSpec->format = AUDIO_U8;
-      lpSpec->freq = gpGlobals->iSampleRate;
-      lpSpec->size = x;
+static void
+SOUND_ResampleS16(
+	LPCBYTE                lpData,
+	const SDL_AudioSpec   *lpSpec,
+	LPBYTE                 lpBuffer,
+	DWORD                  dwLen,
+	void                  *resampler
+	)
+/*++
+	Purpose:
 
 
-#else
+	Resample 16-bit signed (little-endian) PCM data into 16-bit signed (little-endian) PCM data.
 
 
-      *lppBuffer = (unsigned char *)malloc(len);
-      if (*lppBuffer == NULL)
-      {
-         return NULL;
-      }
+	Parameters:
 
 
-      lpSpec->channels = 1;
-      lpSpec->format = AUDIO_U8;
-      lpSpec->freq = freq;
-      lpSpec->size = len;
+	[IN]  lpData - pointer to the buffer of the input PCM data.
 
 
-      lpVOC += 2;
-      memcpy(*lppBuffer, lpVOC, len);
+	[IN]  lpSpec - pointer to the SDL_AudioSpec structure, which contains
+                   some basic information about the input PCM data.
 
 
-#endif
+	[IN]  lpBuffer - pointer of the buffer of the output PCM data.
 
 
-      return lpSpec;
-   }
+	[IN]  dwLen - length of the buffer of the output PCM data, should be exactly
+                  the number of bytes needed of the resampled data.
+
+	[IN]  resampler - pointer of the resampler instance.
+
+	Return value:
+
+	None.
+--*/
+{
+	int src_samples = lpSpec->size / lpSpec->channels / 2, i;
+
+	for (i = 0; i < lpSpec->channels; i++)
+	{
+		const short *src = (short *)lpData + i;
+		short *dst = (short *)lpBuffer + i;
+		int channel_len = dwLen / lpSpec->channels, total_bytes = 0;
+
+		resampler_clear(resampler);
+		while (total_bytes < channel_len && src_samples > 0)
+		{
+			int to_write, j;
+			to_write = resampler_get_free_count(resampler);
+			if (to_write > src_samples) to_write = src_samples;
+			for (j = 0; j < to_write; j++)
+			{
+				resampler_write_sample(resampler, SDL_SwapLE16(*src));
+				src += lpSpec->channels;
+			}
+			src_samples -= to_write;
+			while (total_bytes < channel_len && resampler_get_sample_count(resampler) > 0)
+			{
+				*dst = SDL_SwapLE16(resampler_get_and_remove_sample(resampler));
+				dst += lpSpec->channels; total_bytes += (SDL_AUDIO_BITSIZE(AUDIO_S16) >> 3);
+			}
+		}
+		/* Flush resampler's output buffer */
+		while (total_bytes < channel_len)
+		{
+			int j, to_write = resampler_get_free_count(resampler);
+			short val = SDL_SwapLE16(src[-lpSpec->channels]);
+			for (j = 0; j < to_write; j++)
+				resampler_write_sample(resampler, val);
+			while (total_bytes < channel_len && resampler_get_sample_count(resampler) > 0)
+			{
+				*dst = SDL_SwapLE16(resampler_get_and_remove_sample(resampler));
+				dst += lpSpec->channels; total_bytes += (SDL_AUDIO_BITSIZE(AUDIO_S16) >> 3);
+			}
+		}
+	}
 }
 }
 
 
 static VOID SDLCALL
 static VOID SDLCALL
@@ -295,7 +532,7 @@ SOUND_OpenAudio(
 {
 {
    SDL_AudioSpec spec;
    SDL_AudioSpec spec;
    char *mkfs[2];
    char *mkfs[2];
-   BOOL use_wave[2];
+   FNLoadSoundData func[2];
    int i;
    int i;
 
 
    if (gSndOpened)
    if (gSndOpened)
@@ -313,13 +550,13 @@ SOUND_OpenAudio(
    //
    //
    if (gpGlobals->fIsWIN95)
    if (gpGlobals->fIsWIN95)
    {
    {
-	   mkfs[0] = "sounds.mkf"; use_wave[0] = TRUE;
-	   mkfs[1] = "voc.mkf"; use_wave[1] = FALSE;
+	   mkfs[0] = "sounds.mkf"; func[0] = SOUND_LoadWAVEData;
+	   mkfs[1] = "voc.mkf"; func[1] = SOUND_LoadVOCData;
    }
    }
    else
    else
    {
    {
-	   mkfs[0] = "voc.mkf"; use_wave[0] = FALSE;
-	   mkfs[1] = "sounds.mkf"; use_wave[1] = TRUE;
+	   mkfs[0] = "voc.mkf"; func[0] = SOUND_LoadVOCData;
+	   mkfs[1] = "sounds.mkf"; func[1] = SOUND_LoadWAVEData;
    }
    }
 
 
    for (i = 0; i < 2; i++)
    for (i = 0; i < 2; i++)
@@ -327,7 +564,7 @@ SOUND_OpenAudio(
 	   gSndPlayer.mkf = UTIL_OpenFile(mkfs[i]);
 	   gSndPlayer.mkf = UTIL_OpenFile(mkfs[i]);
 	   if (gSndPlayer.mkf)
 	   if (gSndPlayer.mkf)
 	   {
 	   {
-		   g_fUseWav = use_wave[i];
+		   gSndPlayer.LoadSoundData = func[i];
 		   break;
 		   break;
 	   }
 	   }
    }
    }
@@ -340,6 +577,7 @@ SOUND_OpenAudio(
    // Initialize the resampler
    // Initialize the resampler
    //
    //
    resampler_init();
    resampler_init();
+   gSndPlayer.resampler = resampler_create();
 
 
    //
    //
    // Open the sound subsystem.
    // Open the sound subsystem.
@@ -456,18 +694,6 @@ SOUND_OpenAudio(
    return 0;
    return 0;
 }
 }
 
 
-#ifdef PSP
-void
-SOUND_ReloadVOC(
-	void
-)
-{
-   fclose(gSndPlayer.mkf);
-   gSndPlayer.mkf = UTIL_OpenFile("voc.mkf");
-   g_fUseWav = FALSE;
-}
-#endif
-
 VOID
 VOID
 SOUND_CloseAudio(
 SOUND_CloseAudio(
    VOID
    VOID
@@ -532,6 +758,12 @@ SOUND_CloseAudio(
    MIDI_Play(0, FALSE);
    MIDI_Play(0, FALSE);
 #endif
 #endif
 
 
+   if (gSndPlayer.resampler)
+   {
+      resampler_delete(gSndPlayer.resampler);
+	  gSndPlayer.resampler = NULL;
+   }
+
    SDL_DestroyMutex(gSndPlayer.mtx);
    SDL_DestroyMutex(gSndPlayer.mtx);
 }
 }
 
 
@@ -615,7 +847,7 @@ SOUND_PlayChannel(
    SDL_AudioCVT    wavecvt;
    SDL_AudioCVT    wavecvt;
    SDL_AudioSpec   wavespec;
    SDL_AudioSpec   wavespec;
    LPBYTE          buf, bufdec;
    LPBYTE          buf, bufdec;
-   UINT            samplesize;
+   LPCBYTE         bufsrc;
    int             len;
    int             len;
 
 
    if (!gSndOpened || g_fNoSound)
    if (!gSndOpened || g_fNoSound)
@@ -629,9 +861,8 @@ SOUND_PlayChannel(
    SDL_mutexP(gSndPlayer.mtx);
    SDL_mutexP(gSndPlayer.mtx);
    if (gSndPlayer.buf[iChannel] != NULL)
    if (gSndPlayer.buf[iChannel] != NULL)
    {
    {
-      LPBYTE p = gSndPlayer.buf[iChannel];
+      free(gSndPlayer.buf[iChannel]);
       gSndPlayer.buf[iChannel] = NULL;
       gSndPlayer.buf[iChannel] = NULL;
-      free(p);
    }
    }
    SDL_mutexV(gSndPlayer.mtx);
    SDL_mutexV(gSndPlayer.mtx);
 
 
@@ -649,7 +880,7 @@ SOUND_PlayChannel(
       return;
       return;
    }
    }
 
 
-   buf = (LPBYTE)calloc(len, 1);
+   buf = (LPBYTE)malloc(len);
    if (buf == NULL)
    if (buf == NULL)
    {
    {
       return;
       return;
@@ -660,8 +891,42 @@ SOUND_PlayChannel(
    //
    //
    PAL_MKFReadChunk(buf, len, iSoundNum, gSndPlayer.mkf);
    PAL_MKFReadChunk(buf, len, iSoundNum, gSndPlayer.mkf);
 
 
-   SOUND_LoadVOCFromBuffer(buf, len, &wavespec, &bufdec);
-   free(buf);
+   bufsrc = gSndPlayer.LoadSoundData(buf, len, &wavespec);
+   if (bufsrc == NULL)
+   {
+	   free(buf);
+	   return;
+   }
+
+   if (wavespec.freq != gSndPlayer.spec.freq)
+   {
+	   /* Resampler is needed */
+	   resampler_set_quality(gSndPlayer.resampler, SOUND_IsIntegerConversion(wavespec.freq) ? RESAMPLER_QUALITY_MIN : gpGlobals->iResampleQuality);
+	   resampler_set_rate(gSndPlayer.resampler, (double)wavespec.freq / (double)gSndPlayer.spec.freq);
+	   len = (int)ceil(wavespec.size * (double)gSndPlayer.spec.freq / (double)wavespec.freq) * (SDL_AUDIO_BITSIZE(AUDIO_S16) / SDL_AUDIO_BITSIZE(wavespec.format));
+	   if (len >= wavespec.channels * 2)
+	   {
+		   bufdec = malloc(len);
+		   if (wavespec.format == AUDIO_S16)
+			   SOUND_ResampleS16(bufsrc, &wavespec, bufdec, len, gSndPlayer.resampler);
+		   else
+			   SOUND_ResampleU8(bufsrc, &wavespec, bufdec, len, gSndPlayer.resampler);
+		   /* Free the original buffer and reset the pointer for simpler later operations */
+		   free(buf); buf = bufdec;
+		   wavespec.format = AUDIO_S16;
+		   wavespec.freq = gSndPlayer.spec.freq;
+	   }
+	   else
+	   {
+		   free(buf);
+		   return;
+	   }
+   }
+   else
+   {
+	   bufdec = (LPBYTE)bufsrc;
+	   len = wavespec.size;
+   }
 
 
    //
    //
    // Build the audio converter and create conversion buffers
    // Build the audio converter and create conversion buffers
@@ -669,42 +934,34 @@ SOUND_PlayChannel(
    if (SDL_BuildAudioCVT(&wavecvt, wavespec.format, wavespec.channels, wavespec.freq,
    if (SDL_BuildAudioCVT(&wavecvt, wavespec.format, wavespec.channels, wavespec.freq,
       gSndPlayer.spec.format, gSndPlayer.spec.channels, gSndPlayer.spec.freq) < 0)
       gSndPlayer.spec.format, gSndPlayer.spec.channels, gSndPlayer.spec.freq) < 0)
    {
    {
-      free(bufdec);
+      free(buf);
       return;
       return;
    }
    }
 
 
-   samplesize = ((wavespec.format & 0xFF) / 8) * wavespec.channels;
-   wavecvt.len = wavespec.size & ~(samplesize - 1);
+   wavecvt.len = len & ~((SDL_AUDIO_BITSIZE(wavespec.format) >> 3) * wavespec.channels - 1);
    wavecvt.buf = (LPBYTE)malloc(wavecvt.len * wavecvt.len_mult);
    wavecvt.buf = (LPBYTE)malloc(wavecvt.len * wavecvt.len_mult);
    if (wavecvt.buf == NULL)
    if (wavecvt.buf == NULL)
    {
    {
-      free(bufdec);
+      free(buf);
       return;
       return;
    }
    }
-   memcpy(wavecvt.buf, bufdec, wavespec.size);
-   if (g_fUseWav)
-   {
-      SDL_FreeWAV(bufdec);
-   }
-   else
-   {
-      free(bufdec);
-   }
+   memcpy(wavecvt.buf, bufdec, len);
+   free(buf);
 
 
    //
    //
    // Run the audio converter
    // Run the audio converter
    //
    //
    if (SDL_ConvertAudio(&wavecvt) < 0)
    if (SDL_ConvertAudio(&wavecvt) < 0)
    {
    {
+      free(wavecvt.buf);
       return;
       return;
    }
    }
 
 
    SDL_mutexP(gSndPlayer.mtx);
    SDL_mutexP(gSndPlayer.mtx);
    if (gSndPlayer.buf[iChannel] != NULL)
    if (gSndPlayer.buf[iChannel] != NULL)
    {
    {
-	   LPBYTE p = gSndPlayer.buf[iChannel];
+	   free(gSndPlayer.buf[iChannel]);
 	   gSndPlayer.buf[iChannel] = NULL;
 	   gSndPlayer.buf[iChannel] = NULL;
-	   free(p);
    }
    }
    gSndPlayer.buf[iChannel] = wavecvt.buf;
    gSndPlayer.buf[iChannel] = wavecvt.buf;
    gSndPlayer.audio_len[iChannel] = wavecvt.len * wavecvt.len_mult;
    gSndPlayer.audio_len[iChannel] = wavecvt.len * wavecvt.len_mult;

+ 2 - 0
sound.h

@@ -83,6 +83,8 @@ extern int g_iCurrChannel;
 extern BOOL       g_fNoSound;
 extern BOOL       g_fNoSound;
 extern BOOL       g_fNoMusic;
 extern BOOL       g_fNoMusic;
 
 
+#define SOUND_IsIntegerConversion(a) ((((a) % gpGlobals->iSampleRate) | (gpGlobals->iSampleRate % (a))) == 0)
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 36 - 3
text.c

@@ -988,7 +988,8 @@ PAL_DialogIsPlayingRNG(
 }
 }
 
 
 INT
 INT
-PAL_MultiByteToWideChar(
+PAL_MultiByteToWideCharCP(
+   CODEPAGE      cp,
    LPCSTR        mbs,
    LPCSTR        mbs,
    int           mbslength,
    int           mbslength,
    LPWSTR        wcs,
    LPWSTR        wcs,
@@ -1001,6 +1002,7 @@ PAL_MultiByteToWideChar(
 
 
   Parameters:
   Parameters:
 
 
+    [IN]  cp - Code page for conversion.
     [IN]  mbs - Pointer to the multi-byte string.
     [IN]  mbs - Pointer to the multi-byte string.
 	[IN]  mbslength - Length of the multi-byte string, or -1 for auto-detect.
 	[IN]  mbslength - Length of the multi-byte string, or -1 for auto-detect.
 	[IN]  wcs - Pointer to the wide string buffer.
 	[IN]  wcs - Pointer to the wide string buffer.
@@ -1025,7 +1027,7 @@ PAL_MultiByteToWideChar(
 
 
 	if (!wcs)
 	if (!wcs)
 	{
 	{
-		switch (gpGlobals->iCodePage)
+		switch (cp)
 		{
 		{
 		case CP_SHIFTJIS:
 		case CP_SHIFTJIS:
 			for (i = 0; i < mbslength && mbs[i]; i++)
 			for (i = 0; i < mbslength && mbs[i]; i++)
@@ -1069,7 +1071,7 @@ PAL_MultiByteToWideChar(
 	else
 	else
 	{
 	{
 		WCHAR invalid_char;
 		WCHAR invalid_char;
-		switch (gpGlobals->iCodePage)
+		switch (cp)
 		{
 		{
 		case CP_SHIFTJIS:
 		case CP_SHIFTJIS:
 			invalid_char = 0x30fb;
 			invalid_char = 0x30fb;
@@ -1171,6 +1173,37 @@ PAL_MultiByteToWideChar(
 	}
 	}
 }
 }
 
 
+INT
+PAL_MultiByteToWideChar(
+   LPCSTR        mbs,
+   int           mbslength,
+   LPWSTR        wcs,
+   int           wcslength
+)
+/*++
+  Purpose:
+
+    Convert multi-byte string into the corresponding unicode string.
+
+  Parameters:
+
+    [IN]  mbs - Pointer to the multi-byte string.
+	[IN]  mbslength - Length of the multi-byte string, or -1 for auto-detect.
+	[IN]  wcs - Pointer to the wide string buffer.
+	[IN]  wcslength - Length of the wide string buffer.
+
+  Return value:
+
+    The length of converted wide string. If mbslength is set to -1, the returned
+	value includes the terminal null-char; otherwise, the null-char is not included.
+	If wcslength is set to 0, wcs can be set to NULL and the return value is the
+	required length of the wide string buffer.
+
+--*/
+{
+	return PAL_MultiByteToWideCharCP(gpGlobals->iCodePage, mbs, mbslength, wcs, wcslength);
+}
+
 WCHAR
 WCHAR
 PAL_GetInvalidChar(
 PAL_GetInvalidChar(
    CODEPAGE      iCodePage
    CODEPAGE      iCodePage

+ 9 - 0
text.h

@@ -111,6 +111,15 @@ PAL_MultiByteToWideChar(
    int           wcslength
    int           wcslength
 );
 );
 
 
+INT
+PAL_MultiByteToWideCharCP(
+	CODEPAGE      cp,
+	LPCSTR        mbs,
+	int           mbslength,
+	LPWSTR        wcs,
+	int           wcslength
+	);
+
 WCHAR
 WCHAR
 PAL_GetInvalidChar(
 PAL_GetInvalidChar(
    CODEPAGE      iCodePage
    CODEPAGE      iCodePage