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 \
 	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 += $(LIBMAD_FILES)

+ 11 - 43
global.c

@@ -22,6 +22,7 @@
 //
 
 #include "main.h"
+#include "resampler.h"
 
 LPGLOBALVARS gpGlobals = NULL;
 extern BOOL g_fUseMidi;
@@ -66,7 +67,7 @@ PAL_InitGlobals(
 --*/
 {
    FILE     *fp;
-   CODEPAGE  iCodePage = CP_UNKNOWN;
+   CODEPAGE  iCodePage = CP_BIG5;		// Default for BIG5
    DWORD     dwWordLength = 10;			// Default for PAL DOS/WIN95
    DWORD     dwExtraMagicDescLines = 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
    INT       iSampleRate = 44100;		// Default for 44100 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 eCDType = PAL_HAS_SDLCD ? MUSIC_SDLCD : MUSIC_OGG;
    OPLTYPE   eOPLType = OPL_DOSBOX;
@@ -163,6 +165,10 @@ PAL_InitGlobals(
 				   {
 					   sscanf(ptr, "%d", &iOPLSampleRate);
 				   }
+				   else if (SDL_strcasecmp(p, "RESAMPLEQUALITY") == 0)
+				   {
+					   sscanf(ptr, "%d", &iResampleQuality);
+				   }
 				   else if (SDL_strcasecmp(p, "SURROUNDOPLOFFSET") == 0)
 				   {
 					   sscanf(ptr, "%f", &flSurroundOPLOffset);
@@ -207,56 +213,16 @@ PAL_InitGlobals(
 	   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
+   //
    gpGlobals->fIsWIN95 = dwIsDOS ? FALSE : TRUE;
    gpGlobals->fUseEmbeddedFonts = dwIsDOS && dwUseEmbeddedFonts ? TRUE : FALSE;
    gpGlobals->fUseSurroundOPL = dwUseStereo && dwUseSurroundOPL ? TRUE : FALSE;
    gpGlobals->iAudioChannels = dwUseStereo ? 2 : 1;
    gpGlobals->iSampleRate = iSampleRate;
    gpGlobals->iOPLSampleRate = iOPLSampleRate;
+   gpGlobals->iResampleQuality = iResampleQuality;
    gpGlobals->dSurroundOPLOffset = flSurroundOPLOffset;
    gpGlobals->eMusicType = eMusicType;
    gpGlobals->eCDType = eCDType;
@@ -266,7 +232,9 @@ PAL_InitGlobals(
    gpGlobals->dwExtraMagicDescLines = dwExtraMagicDescLines;
    gpGlobals->dwExtraItemDescLines = dwExtraItemDescLines;
 
+   //
    // Set decompress function
+   //
    Decompress = gpGlobals->fIsWIN95 ? YJ2_Decompress : YJ1_Decompress;
 
    //

+ 1 - 0
global.h

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

+ 67 - 3
libmad/music_mad.c

@@ -24,9 +24,10 @@
 #include <string.h>
 #include <stdlib.h>
 #include "music_mad.h"
+#include "../resampler.h" /* SDLPAL */
 
 mad_data *
-mad_openFile(const char *filename, SDL_AudioSpec *mixer) {
+mad_openFile(const char *filename, SDL_AudioSpec *mixer, int resampler_quality) {
   SDL_RWops *rw;
 
   rw = SDL_RWFromFile(filename, "rb");
@@ -34,11 +35,11 @@ mad_openFile(const char *filename, SDL_AudioSpec *mixer) {
 	return NULL;
   }
 
-  return mad_openFileRW(rw, mixer);
+  return mad_openFileRW(rw, mixer, resampler_quality);
 }
 
 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;
 
   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_end = 0;
   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;
 }
@@ -185,11 +189,38 @@ decode_frame(mad_data *mp3_mad) {
   out = mp3_mad->output_buffer + mp3_mad->output_end;
 
   if ((mp3_mad->status & MS_cvt_decoded) == 0) {
+    int hi, lo; /* SDLPAL */
 	mp3_mad->status |= MS_cvt_decoded;
 
 	/* The first frame determines some key properties of the stream.
 	   In particular, it tells us enough to set up the convert
 	   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);
   }
 
@@ -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);
 		/*assert(mp3_mad->output_end <= MAD_OUTPUT_BUFFER_SIZE);*/
 		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 {
   SDL_RWops *rw;
+  void *resampler[2];	/* SDLPAL: Avoid to use SDL's stupid resampler */
   struct mad_stream stream;
   struct mad_frame frame;
   struct mad_synth synth;
@@ -48,6 +49,8 @@ typedef struct {
   int volume;
   int status;
   int output_begin, output_end;
+  int upsample; /* SDLPAL: Is upsample or downsample */
+  int resampler_quality; /* SDLPAL:resampler quality */
   SDL_AudioSpec mixer;
   SDL_AudioCVT cvt;
 
@@ -55,8 +58,8 @@ typedef struct {
   unsigned char output_buffer[MAD_OUTPUT_BUFFER_SIZE];
 } 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_start(mad_data *mp3_mad);

+ 4 - 2
mp3player.c

@@ -18,11 +18,13 @@
 //
 
 #include "util.h"
+#include "global.h"
 
 #if PAL_HAS_MP3
 
 #include "sound.h"
 #include "players.h"
+#include "resampler.h"
 #include "libmad/music_mad.h"
 
 typedef struct tagMP3PLAYER
@@ -109,9 +111,9 @@ MP3_Play(
 
 	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)

+ 82 - 98
oggplay.c

@@ -27,11 +27,7 @@
 #if PAL_HAS_OGG
 #include <vorbis\vorbisfile.h>
 
-#define USE_RESAMPLER    1
-
-#if USE_RESAMPLER
 #include "resampler.h"
-#endif
 
 #define FLAG_OY 0x01
 #define FLAG_VI 0x02
@@ -60,14 +56,14 @@ typedef struct tagOGGPLAYER
 	vorbis_block     vb; /* local working space for packet->PCM decode */
 
 	FILE            *fp;
-#if USE_RESAMPLER
 	void            *resampler[2];
-#endif
 	INT              iFlags;
 	INT              iMusic;
 	INT              iStage;
+	INT              nChannels;
 	BOOL             fLoop;
 	BOOL             fReady;
+	BOOL             fUseResampler;
 } OGGPLAYER, *LPOGGPLAYER;
 
 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) {
 		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)
 {
-	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 {
-		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)
 {
-#if USE_RESAMPLER
 	int i;
 	for (i = 0; i < gpGlobals->iAudioChannels; i++) resampler_clear(player->resampler[0]);
-#endif
 	/* Do various cleanups */
 	if (player->iFlags & FLAG_VB) vorbis_block_clear(&player->vb);
 	if (player->iFlags & FLAG_VD) vorbis_dsp_clear(&player->vd);
@@ -239,13 +231,16 @@ static BOOL OGG_Rewind(LPOGGPLAYER player)
 																for vd here */
 		player->iStage = STAGE_PAGEOUT;
 		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);
 	}
 	else {
@@ -324,59 +319,52 @@ OGG_FillBuffer(
 				}
 			case STAGE_PCMOUT:
 				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 */
-							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));
 								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);
@@ -389,16 +377,14 @@ OGG_FillBuffer(
 				}
 				break;
 			case STAGE_REWIND:
-#if USE_RESAMPLER
 				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));
 						total_bytes += gpGlobals->iAudioChannels * sizeof(ogg_int16_t);
 					}
 					/* 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);
 				stage = player->iStage;
 				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
 OGG_Play(
 	VOID       *object,
@@ -490,6 +455,22 @@ OGG_Play(
 	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
 OGG_Init(
 	LPCSTR szFileName
@@ -498,9 +479,6 @@ OGG_Init(
 	LPOGGPLAYER player;
 	if (player = (LPOGGPLAYER)malloc(sizeof(OGGPLAYER)))
 	{
-#if USE_RESAMPLER
-		int i;
-#endif
 		memset(player, 0, sizeof(LPOGGPLAYER));
 
 		player->FillBuffer = OGG_FillBuffer;
@@ -513,13 +491,19 @@ OGG_Init(
 		player->iStage = 0;
 		player->fLoop = 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;
 	}
 	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);
 	resampler_remove_sample(_r);
-	if (sample >= 32767)
+	if (sample > 32767)
 		return 32767;
-	else if (sample <= -32767)
-		return -32767;
+	else if (sample < -32768)
+		return -32768;
 	else
 		return (short)sample;
 }

+ 2 - 2
rixplay.cpp

@@ -226,7 +226,7 @@ RIX_FillBuffer(
 		SHORT* ptr = (SHORT*)stream;
 		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);
 		}
 		stream = (LPBYTE)ptr;
@@ -417,7 +417,7 @@ RIX_Init(
 		for (int i = 0; i < gpGlobals->iAudioChannels; i++)
 		{
 			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);
 		}
 	}

+ 6 - 2
sdlpal.cfg.example

@@ -4,8 +4,8 @@
 # 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
 
 # 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
 #                    recommended value is 384.0, but other values may also be used.
 #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>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
-    <ClCompile Include="mp3player.c" />
+    <ClCompile Include="mp3play.c" />
     <ClCompile Include="oggplay.c" />
     <ClCompile Include="palcommon.c">
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

+ 1 - 1
sdlpal.vcxproj.filters

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

+ 380 - 123
sound.c

@@ -25,6 +25,7 @@
 #include "players.h"
 #include "util.h"
 #include "resampler.h"
+#include <math.h>
 
 #ifdef PAL_HAS_NATIVEMIDI
 #include "midi.h"
@@ -39,8 +40,6 @@ static BOOL  gSndOpened = FALSE;
 BOOL         g_fNoSound = FALSE;
 BOOL         g_fNoMusic = FALSE;
 
-static BOOL  g_fUseWav = FALSE;
-
 #ifdef __SYMBIAN32__
 INT          g_iVolume  = SDL_MIX_MAXVOLUME * 0.1;
 #endif
@@ -51,6 +50,8 @@ int          g_iCurrChannel = 0;
 
 #define PAL_CDTRACK_BASE    10000
 
+typedef LPCBYTE(*FNLoadSoundData)(LPCBYTE, DWORD, SDL_AudioSpec *);
+
 typedef struct tagSNDPLAYER
 {
    FILE                     *mkf;
@@ -58,126 +59,362 @@ typedef struct tagSNDPLAYER
    SDL_mutex                *mtx;
    LPBYTE                    buf[2], pos[2];
    INT                       audio_len[2];
+   void                     *resampler;
    MUSICPLAYER              *pMusPlayer;
    MUSICPLAYER              *pCDPlayer;
 #if PAL_HAS_SDLCD
    SDL_CD                   *pCD;
 #endif
+   FNLoadSoundData           LoadSoundData;
 } SNDPLAYER;
 
 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.
 
-    [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
@@ -295,7 +532,7 @@ SOUND_OpenAudio(
 {
    SDL_AudioSpec spec;
    char *mkfs[2];
-   BOOL use_wave[2];
+   FNLoadSoundData func[2];
    int i;
 
    if (gSndOpened)
@@ -313,13 +550,13 @@ SOUND_OpenAudio(
    //
    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
    {
-	   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++)
@@ -327,7 +564,7 @@ SOUND_OpenAudio(
 	   gSndPlayer.mkf = UTIL_OpenFile(mkfs[i]);
 	   if (gSndPlayer.mkf)
 	   {
-		   g_fUseWav = use_wave[i];
+		   gSndPlayer.LoadSoundData = func[i];
 		   break;
 	   }
    }
@@ -340,6 +577,7 @@ SOUND_OpenAudio(
    // Initialize the resampler
    //
    resampler_init();
+   gSndPlayer.resampler = resampler_create();
 
    //
    // Open the sound subsystem.
@@ -456,18 +694,6 @@ SOUND_OpenAudio(
    return 0;
 }
 
-#ifdef PSP
-void
-SOUND_ReloadVOC(
-	void
-)
-{
-   fclose(gSndPlayer.mkf);
-   gSndPlayer.mkf = UTIL_OpenFile("voc.mkf");
-   g_fUseWav = FALSE;
-}
-#endif
-
 VOID
 SOUND_CloseAudio(
    VOID
@@ -532,6 +758,12 @@ SOUND_CloseAudio(
    MIDI_Play(0, FALSE);
 #endif
 
+   if (gSndPlayer.resampler)
+   {
+      resampler_delete(gSndPlayer.resampler);
+	  gSndPlayer.resampler = NULL;
+   }
+
    SDL_DestroyMutex(gSndPlayer.mtx);
 }
 
@@ -615,7 +847,7 @@ SOUND_PlayChannel(
    SDL_AudioCVT    wavecvt;
    SDL_AudioSpec   wavespec;
    LPBYTE          buf, bufdec;
-   UINT            samplesize;
+   LPCBYTE         bufsrc;
    int             len;
 
    if (!gSndOpened || g_fNoSound)
@@ -629,9 +861,8 @@ SOUND_PlayChannel(
    SDL_mutexP(gSndPlayer.mtx);
    if (gSndPlayer.buf[iChannel] != NULL)
    {
-      LPBYTE p = gSndPlayer.buf[iChannel];
+      free(gSndPlayer.buf[iChannel]);
       gSndPlayer.buf[iChannel] = NULL;
-      free(p);
    }
    SDL_mutexV(gSndPlayer.mtx);
 
@@ -649,7 +880,7 @@ SOUND_PlayChannel(
       return;
    }
 
-   buf = (LPBYTE)calloc(len, 1);
+   buf = (LPBYTE)malloc(len);
    if (buf == NULL)
    {
       return;
@@ -660,8 +891,42 @@ SOUND_PlayChannel(
    //
    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
@@ -669,42 +934,34 @@ SOUND_PlayChannel(
    if (SDL_BuildAudioCVT(&wavecvt, wavespec.format, wavespec.channels, wavespec.freq,
       gSndPlayer.spec.format, gSndPlayer.spec.channels, gSndPlayer.spec.freq) < 0)
    {
-      free(bufdec);
+      free(buf);
       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);
    if (wavecvt.buf == NULL)
    {
-      free(bufdec);
+      free(buf);
       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
    //
    if (SDL_ConvertAudio(&wavecvt) < 0)
    {
+      free(wavecvt.buf);
       return;
    }
 
    SDL_mutexP(gSndPlayer.mtx);
    if (gSndPlayer.buf[iChannel] != NULL)
    {
-	   LPBYTE p = gSndPlayer.buf[iChannel];
+	   free(gSndPlayer.buf[iChannel]);
 	   gSndPlayer.buf[iChannel] = NULL;
-	   free(p);
    }
    gSndPlayer.buf[iChannel] = wavecvt.buf;
    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_fNoMusic;
 
+#define SOUND_IsIntegerConversion(a) ((((a) % gpGlobals->iSampleRate) | (gpGlobals->iSampleRate % (a))) == 0)
+
 #ifdef __cplusplus
 }
 #endif

+ 36 - 3
text.c

@@ -988,7 +988,8 @@ PAL_DialogIsPlayingRNG(
 }
 
 INT
-PAL_MultiByteToWideChar(
+PAL_MultiByteToWideCharCP(
+   CODEPAGE      cp,
    LPCSTR        mbs,
    int           mbslength,
    LPWSTR        wcs,
@@ -1001,6 +1002,7 @@ PAL_MultiByteToWideChar(
 
   Parameters:
 
+    [IN]  cp - Code page for conversion.
     [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.
@@ -1025,7 +1027,7 @@ PAL_MultiByteToWideChar(
 
 	if (!wcs)
 	{
-		switch (gpGlobals->iCodePage)
+		switch (cp)
 		{
 		case CP_SHIFTJIS:
 			for (i = 0; i < mbslength && mbs[i]; i++)
@@ -1069,7 +1071,7 @@ PAL_MultiByteToWideChar(
 	else
 	{
 		WCHAR invalid_char;
-		switch (gpGlobals->iCodePage)
+		switch (cp)
 		{
 		case CP_SHIFTJIS:
 			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
 PAL_GetInvalidChar(
    CODEPAGE      iCodePage

+ 9 - 0
text.h

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