diff --git a/MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace/xcshareddata/QuakeSpasm.xccheckout b/MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace/xcshareddata/QuakeSpasm.xccheckout
new file mode 100644
index 00000000..9cd610af
--- /dev/null
+++ b/MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace/xcshareddata/QuakeSpasm.xccheckout
@@ -0,0 +1,41 @@
+
+
+
+
+ IDESourceControlProjectFavoriteDictionaryKey
+
+ IDESourceControlProjectIdentifier
+ 9B8B3A85-F6C1-4D37-B6BC-7F34FBF23BF8
+ IDESourceControlProjectName
+ QuakeSpasm
+ IDESourceControlProjectOriginsDictionary
+
+ 2BD2BDEE-EFDD-4965-A751-0185457382EC
+ https://github.com/ericwa/Quakespasm
+
+ IDESourceControlProjectPath
+ MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace
+ IDESourceControlProjectRelativeInstallPathDictionary
+
+ 2BD2BDEE-EFDD-4965-A751-0185457382EC
+ ../../..
+
+ IDESourceControlProjectURL
+ https://github.com/ericwa/Quakespasm
+ IDESourceControlProjectVersion
+ 110
+ IDESourceControlProjectWCCIdentifier
+ 2BD2BDEE-EFDD-4965-A751-0185457382EC
+ IDESourceControlProjectWCConfigurations
+
+
+ IDESourceControlRepositoryExtensionIdentifierKey
+ public.vcs.git
+ IDESourceControlWCCIdentifierKey
+ 2BD2BDEE-EFDD-4965-A751-0185457382EC
+ IDESourceControlWCCName
+ quakespasm
+
+
+
+
diff --git a/MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace/xcuserdata/ericwa.xcuserdatad/UserInterfaceState.xcuserstate b/MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace/xcuserdata/ericwa.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 00000000..44cf0ab3
Binary files /dev/null and b/MacOSX/QuakeSpasm.xcodeproj/project.xcworkspace/xcuserdata/ericwa.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/MacOSX/QuakeSpasm.xcodeproj/xcuserdata/ericwa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/MacOSX/QuakeSpasm.xcodeproj/xcuserdata/ericwa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 00000000..fe2b4541
--- /dev/null
+++ b/MacOSX/QuakeSpasm.xcodeproj/xcuserdata/ericwa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,5 @@
+
+
+
diff --git a/MacOSX/QuakeSpasm.xcodeproj/xcuserdata/ericwa.xcuserdatad/xcschemes/xcschememanagement.plist b/MacOSX/QuakeSpasm.xcodeproj/xcuserdata/ericwa.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 00000000..36a83cff
--- /dev/null
+++ b/MacOSX/QuakeSpasm.xcodeproj/xcuserdata/ericwa.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ SuppressBuildableAutocreation
+
+ 8D1107260486CEB800E47090
+
+ primary
+
+
+
+
+
diff --git a/Quake/q_sound.h b/Quake/q_sound.h
index 7e560717..eaf838df 100644
--- a/Quake/q_sound.h
+++ b/Quake/q_sound.h
@@ -169,6 +169,7 @@ extern vec3_t listener_up;
extern cvar_t sndspeed;
extern cvar_t snd_mixspeed;
+extern cvar_t snd_filterquality;
extern cvar_t sfxvolume;
extern cvar_t loadas8bit;
diff --git a/Quake/snd_dma.c b/Quake/snd_dma.c
index 542a0b65..5e685c6d 100644
--- a/Quake/snd_dma.c
+++ b/Quake/snd_dma.c
@@ -77,6 +77,7 @@ cvar_t loadas8bit = {"loadas8bit", "0", CVAR_NONE};
cvar_t sndspeed = {"sndspeed", "11025", CVAR_NONE};
cvar_t snd_mixspeed = {"snd_mixspeed", "44100", CVAR_NONE};
+cvar_t snd_filterquality = {"snd_filterquality", "1", CVAR_NONE};
static cvar_t nosound = {"nosound", "0", CVAR_NONE};
static cvar_t ambient_level = {"ambient_level", "0.3", CVAR_NONE};
@@ -163,7 +164,8 @@ void S_Init (void)
Cvar_RegisterVariable(&_snd_mixahead);
Cvar_RegisterVariable(&sndspeed);
Cvar_RegisterVariable(&snd_mixspeed);
-
+ Cvar_RegisterVariable(&snd_filterquality);
+
if (safemode || COM_CheckParm("-nosound"))
return;
diff --git a/Quake/snd_mix.c b/Quake/snd_mix.c
index 6d555b8a..4bdb212c 100644
--- a/Quake/snd_mix.c
+++ b/Quake/snd_mix.c
@@ -156,6 +156,7 @@ S_MakeBlackmanWindowKernel
Based on equation 16-4 from
"The Scientist and Engineer's Guide to Digital Signal Processing"
+M must be even
kernel has room for M+1 floats,
f_c is the filter cutoff frequency, as a fraction of the samplerate
==============
@@ -192,95 +193,124 @@ static void S_MakeBlackmanWindowKernel(float *kernel, int M, float f_c)
}
}
-// must be divisible by 4
-#define FILTER_KERNEL_SIZE 384
+typedef struct {
+ float *memory; // kernelsize floats
+ float *kernel; // kernelsize floats
+ int kernelsize; // M+1, padded to be a multiple of 16
+ int M;
+ int parity; // 0-3
+ float f_c;
+} filter_t;
+
+static void S_UpdateFilter(filter_t *filter, int M, float f_c)
+{
+ if (filter->f_c != f_c || filter->M != M)
+ {
+ if (filter->memory != NULL) free(filter->memory);
+ if (filter->kernel != NULL) free(filter->kernel);
+
+ filter->M = M;
+ filter->f_c = f_c;
+
+ filter->parity = 0;
+ if ((M + 1) % 16 == 0)
+ filter->kernelsize = (M + 1);
+ else
+ filter->kernelsize = (M + 1) + 16 - ((M + 1) % 16);
+ filter->memory = calloc(filter->kernelsize, sizeof(float));
+ filter->kernel = calloc(filter->kernelsize, sizeof(float));
+
+ S_MakeBlackmanWindowKernel(filter->kernel, M, f_c);
+ }
+}
+
+static void S_ApplyFilter(filter_t *filter, int *data, int stride, int count)
+{
+ int i, j;
+ float *input;
+ const int kernelsize = filter->kernelsize;
+ const float *kernel = filter->kernel;
+ int parity = 0;
+
+ input = malloc(sizeof(float) * (filter->kernelsize + count));
+
+// set up the input buffer
+// memory holds the previous filter->kernelsize samples of input.
+
+ memcpy(input, filter->memory, filter->kernelsize * sizeof(float));
+
+ for (i=0; ikernelsize+i] = data[i * stride] / (32768.0 * 256.0);
+ }
+
+// copy out the last filter->kernelsize samples to 'memory' for next time
+
+ memcpy(filter->memory, input + count, filter->kernelsize * sizeof(float));
+
+// apply the filter
+
+ for (i=0; iparity = parity;
+
+ free(input);
+}
/*
==============
S_LowpassFilter
lowpass filters 24-bit integer samples in 'data' (stored in 32-bit ints).
-
-f_c is the filter cutoff frequency, as a fraction of the samplerate
memory must be an array of FILTER_KERNEL_SIZE floats
==============
*/
-static void S_LowpassFilter(float f_c, int *data, int stride, int count,
- float *memory)
+static void S_LowpassFilter(int *data, int stride, int count,
+ filter_t *memory)
{
- int i;
+ int M;
+ float bw;
-// M is the "kernel size" parameter for makekernel() - must be even.
-// FILTER_KERNEL_SIZE size is M+1, rounded up to be divisible by 4
- const int M = FILTER_KERNEL_SIZE - 2;
-
- float input[FILTER_KERNEL_SIZE + count];
-
- static float kernel[FILTER_KERNEL_SIZE];
- static float kernel_fc;
-
- if (f_c <= 0 || f_c > 0.5)
- return;
-
- if (count < FILTER_KERNEL_SIZE)
+ if (snd_filterquality.value == 0)
{
- Con_Warning("S_LowpassFilter: not enough samples");
- return;
+ M = 126;
+ bw = 0.90;
+ }
+ else
+ {
+ M = 222;
+ bw = 0.96;
}
-// prepare the kernel
+ float f_c = (bw * 11025 / 2.0) / 44100.0;
- if (kernel_fc != f_c)
- {
- S_MakeBlackmanWindowKernel(kernel, M, f_c);
- kernel_fc = f_c;
- }
+ S_UpdateFilter(memory, M, f_c);
-// set up the input buffer
-// memory holds the previous FILTER_KERNEL_SIZE samples of input.
-
- for (i=0; ispeed)
+ if (sndspeed.value == 11025 && shm->speed == 44100)
{
- static float memory_l[FILTER_KERNEL_SIZE];
- static float memory_r[FILTER_KERNEL_SIZE];
-
- const float cutoff_freq = (sndspeed.value * 0.5 * 0.96) / shm->speed;
-
- S_LowpassFilter(cutoff_freq, (int *)paintbuffer, 2, end - paintedtime, memory_l);
- S_LowpassFilter(cutoff_freq, ((int *)paintbuffer) + 1, 2, end - paintedtime, memory_r);
+ static filter_t memory_l, memory_r;
+ S_LowpassFilter((int *)paintbuffer, 2, end - paintedtime, &memory_l);
+ S_LowpassFilter(((int *)paintbuffer) + 1, 2, end - paintedtime, &memory_r);
}
// paint in the music