diff --git a/src/d_main.c b/src/d_main.c
index 60a1579c3b499acce2d952781f5fd3a1f97d5339..fc082b85c67fae210c6f57b8ce397ab114a120f4 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -1270,6 +1270,9 @@ void D_SRB2Main(void)
 	// initialise locale code
 	M_StartupLocale();
 
+	CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
+	Z_Init();
+
 	// get parameters from a response file (eg: srb2 @parms.txt)
 	M_FindResponseFile();
 
@@ -1374,9 +1377,6 @@ void D_SRB2Main(void)
 	// any wad file is added, as they may contain colors themselves
 	M_InitPlayerSetupColors();
 
-	CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
-	Z_Init();
-
 	clientGamedata = M_NewGameDataStruct();
 	serverGamedata = M_NewGameDataStruct();
 
diff --git a/src/deh_lua.c b/src/deh_lua.c
index ee02746c20968ed154f74d01b6937853a78913bc..9dc1f304f75eb8e1adb4b4cc3fe5f568b17c4210 100644
--- a/src/deh_lua.c
+++ b/src/deh_lua.c
@@ -50,7 +50,7 @@ static inline int lib_freeslot(lua_State *L)
 			sfxenum_t sfx;
 			strlwr(word);
 			CONS_Printf("Sound sfx_%s allocated.\n",word);
-			sfx = S_AddSoundFx(word, false, 0, false);
+			sfx = S_AddSoundFx(word, false, 0);
 			if (sfx != sfx_None) {
 				lua_pushinteger(L, sfx);
 				r++;
@@ -458,8 +458,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	}
 	else if (!mathlib && fastncmp("sfx_",word,4)) {
 		p = word+4;
-		for (i = 0; i < NUMSFX; i++)
-			if (S_sfx[i].name && fastcmp(p, S_sfx[i].name)) {
+		for (i = 0; (UINT32)i < S_numsfx; i++)
+			if (S_sfx[i]->name && fastcmp(p, S_sfx[i]->name)) {
 				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
@@ -467,8 +467,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	}
 	else if (mathlib && fastncmp("SFX_",word,4)) { // SOCs are ALL CAPS!
 		p = word+4;
-		for (i = 0; i < NUMSFX; i++)
-			if (S_sfx[i].name && fasticmp(p, S_sfx[i].name)) {
+		for (i = 0; (UINT32)i < S_numsfx; i++)
+			if (S_sfx[i]->name && fasticmp(p, S_sfx[i]->name)) {
 				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
@@ -476,8 +476,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
 	}
 	else if (mathlib && fastncmp("DS",word,2)) {
 		p = word+2;
-		for (i = 0; i < NUMSFX; i++)
-			if (S_sfx[i].name && fasticmp(p, S_sfx[i].name)) {
+		for (i = 0; (UINT32)i < S_numsfx; i++)
+			if (S_sfx[i]->name && fasticmp(p, S_sfx[i]->name)) {
 				CacheAndPushConstant(L, word, i);
 				return 1;
 			}
diff --git a/src/deh_soc.c b/src/deh_soc.c
index c62082f29fe0cfcef103269fad5f2127dd2c6a86..3b248d0831fee63562de5fab89e076a7525de4d4 100644
--- a/src/deh_soc.c
+++ b/src/deh_soc.c
@@ -439,7 +439,7 @@ void readfreeslots(MYFILE *f)
 			// TODO: Out-of-slots warnings/errors.
 			// TODO: Name too long (truncated) warnings.
 			if (fastcmp(type, "SFX"))
-				S_AddSoundFx(word, false, 0, false);
+				S_AddSoundFx(word, false, 0);
 			else if (fastcmp(type, "SPR"))
 			{
 				if (strlen(word) > MAXSPRITENAME)
@@ -2875,20 +2875,20 @@ void readsound(MYFILE *f, INT32 num)
 
 			if (fastcmp(word, "SINGULAR"))
 			{
-				S_sfx[num].singularity = value;
+				S_sfx[num]->singularity = value;
 			}
 			else if (fastcmp(word, "PRIORITY"))
 			{
-				S_sfx[num].priority = value;
+				S_sfx[num]->priority = value;
 			}
 			else if (fastcmp(word, "FLAGS"))
 			{
-				S_sfx[num].pitch = value;
+				S_sfx[num]->pitch = value;
 			}
 			else if (fastcmp(word, "CAPTION") || fastcmp(word, "DESCRIPTION"))
 			{
-				deh_strlcpy(S_sfx[num].caption, word2,
-					sizeof(S_sfx[num].caption), va("Sound effect %d: caption", num));
+				deh_strlcpy(S_sfx[num]->caption, word2,
+					sizeof(S_sfx[num]->caption), va("Sound effect %d: caption", num));
 			}
 			else
 				deh_warning("Sound %d : unknown word '%s'",num,word);
@@ -4193,8 +4193,8 @@ sfxenum_t get_sfx(const char *word)
 		word += 4; // take off the SFX_
 	else if (fastncmp("DS",word,2))
 		word += 2; // take off the DS
-	for (i = 0; i < NUMSFX; i++)
-		if (S_sfx[i].name && fasticmp(word, S_sfx[i].name))
+	for (i = 0; i < S_numsfx; i++)
+		if (S_sfx[i]->name && fasticmp(word, S_sfx[i]->name))
 			return i;
 	deh_warning("Couldn't find sfx named 'SFX_%s'",word);
 	return sfx_None;
diff --git a/src/dehacked.c b/src/dehacked.c
index 881bcee439cdaa2914a8c79671b37f34f86051f1..fd76503618355847d246f8304a46d91f5b1564d7 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -453,11 +453,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 				{
 					if (i == 0 && word2[0] != '0') // If word2 isn't a number
 						i = get_sfx(word2); // find a sound by name
-					if (i < NUMSFX && i > 0)
+					if ((UINT32)i < S_numsfx && i > 0)
 						readsound(f, i);
 					else
 					{
-						deh_warning("Sound %d out of range (1 - %d)", i, NUMSFX-1);
+						deh_warning("Sound %d out of range (1 - %d)", i, S_numsfx-1);
 						ignorelines(f);
 					}
 				}
diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c
index 81f1d8e68fadf94e69c97e02daa65e3ecccf36ab..fc73efde86ea877d32cda1b3fded10d386ccafa0 100644
--- a/src/hardware/hw3sound.c
+++ b/src/hardware/hw3sound.c
@@ -168,7 +168,7 @@ void HW3S_StopSoundByID(void *origin, sfxenum_t sfx_id)
 
 	for (snum = 0; snum < num_sources; snum++)
 	{
-		if (sources[snum].sfxinfo == &S_sfx[sfx_id] && sources[snum].origin == origin)
+		if (sources[snum].sfxinfo == S_sfx[sfx_id] && sources[snum].origin == origin)
 		{
 			HW3S_KillSource(snum);
 			break;
@@ -182,7 +182,7 @@ void HW3S_StopSoundByNum(sfxenum_t sfxnum)
 
 	for (snum = 0; snum < num_sources; snum++)
 	{
-		if (sources[snum].sfxinfo == &S_sfx[sfxnum])
+		if (sources[snum].sfxinfo == S_sfx[sfxnum])
 		{
 			HW3S_KillSource(snum);
 			break;
@@ -357,13 +357,13 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan
 	if (sound_disabled)
 		return -1;
 
-	sfx = &S_sfx[sfx_id];
+	sfx = S_sfx[sfx_id];
 
 	if (sfx->skinsound!=-1 && origin && origin->skin)
 	{
 		// it redirect player sound to the sound in the skin table
 		sfx_id = ((skin_t *)origin->skin)->soundsid[sfx->skinsound];
-		sfx    = &S_sfx[sfx_id];
+		sfx    = S_sfx[sfx_id];
 	}
 
 	if (!sfx->data)
@@ -754,7 +754,7 @@ INT32 HW3S_IdPlaying(sfxenum_t id)
 {
 	INT32         snum;
 	for (snum = 0; snum < num_sources; snum++)
-		if ((size_t)(sources[snum].sfxinfo - S_sfx) == (size_t)id)
+		if (sources[snum].sfxinfo == S_sfx[id])
 			return 1;
 	return 0;
 }
@@ -769,7 +769,7 @@ INT32 HW3S_SoundPlaying(void *origin, sfxenum_t id)
 	for (snum = 0; snum < num_sources; snum++)
 	{
 		if (sources[snum].origin == origin
-		 && (size_t)(sources[snum].sfxinfo - S_sfx) == (size_t)id)
+		 && sources[snum].sfxinfo == S_sfx[id])
 			return 1;
 	}
 	return 0;
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 5fefddb3e35d26109fa829f2475ad8d1d978864a..6575a5f43b4c2f6efa591eaa7a9be7c4163d1f01 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3246,8 +3246,8 @@ static int lib_sStartSound(lua_State *L)
 	player_t *player = NULL;
 	//NOHUD
 
-	if (sound_id >= NUMSFX)
-		return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1);
+	if (sound_id >= S_numsfx)
+		return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, S_numsfx-1);
 
 	if (!lua_isnone(L, 3) && lua_isuserdata(L, 3))
 	{
@@ -3276,8 +3276,8 @@ static int lib_sStartSoundAtVolume(lua_State *L)
 	player_t *player = NULL;
 	//NOHUD
 
-	if (sound_id >= NUMSFX)
-		return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1);
+	if (sound_id >= S_numsfx)
+		return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, S_numsfx-1);
 	if (!lua_isnone(L, 4) && lua_isuserdata(L, 4))
 	{
 		player = *((player_t **)luaL_checkudata(L, 4, META_PLAYER));
@@ -3310,8 +3310,8 @@ static int lib_sStopSoundByID(lua_State *L)
 	sfxenum_t sound_id = luaL_checkinteger(L, 2);
 	//NOHUD
 
-	if (sound_id >= NUMSFX)
-		return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1);
+	if (sound_id >= S_numsfx)
+		return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, S_numsfx-1);
 	if (!lua_isnil(L, 1))
 		if (!GetValidSoundOrigin(L, &origin))
 			return LUA_ErrInvalid(L, "mobj_t/sector_t");
@@ -3515,8 +3515,8 @@ static int lib_sIdPlaying(lua_State *L)
 {
 	sfxenum_t id = luaL_checkinteger(L, 1);
 	//NOHUD
-	if (id >= NUMSFX)
-		return luaL_error(L, "sfx %d out of range (0 - %d)", id, NUMSFX-1);
+	if (id >= S_numsfx)
+		return luaL_error(L, "sfx %d out of range (0 - %d)", id, S_numsfx-1);
 	lua_pushboolean(L, S_IdPlaying(id));
 	return 1;
 }
@@ -3527,8 +3527,8 @@ static int lib_sSoundPlaying(lua_State *L)
 	sfxenum_t id = luaL_checkinteger(L, 2);
 	//NOHUD
 	INLEVEL
-	if (id >= NUMSFX)
-		return luaL_error(L, "sfx %d out of range (0 - %d)", id, NUMSFX-1);
+	if (id >= S_numsfx)
+		return luaL_error(L, "sfx %d out of range (0 - %d)", id, S_numsfx-1);
 	if (!GetValidSoundOrigin(L, &origin))
 		return LUA_ErrInvalid(L, "mobj_t/sector_t");
 
@@ -3555,7 +3555,7 @@ static int lib_sStartMusicCaption(lua_State *L)
 
 	if (lifespan && (!player || P_IsLocalPlayer(player)))
 	{
-		strlcpy(S_sfx[sfx_None].caption, caption, sizeof(S_sfx[sfx_None].caption));
+		strlcpy(S_sfx[sfx_None]->caption, caption, sizeof(S_sfx[sfx_None]->caption));
 		S_StartCaption(sfx_None, -1, lifespan);
 	}
 	return 0;
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 335e11be0ee29ebcb95094aaeb2474a374a5385e..16712deffe88d11ed4f3aa2f92e15b2c750b54b4 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1092,7 +1092,7 @@ static int lib_setMobjInfo(lua_State *L)
 			info->seestate = (statenum_t)value;
 		} else if (i == 5 || (str && fastcmp(str,"seesound"))) {
 			value = luaL_checkinteger(L, 3);
-			if (value < sfx_None || value >= NUMSFX)
+			if (value < sfx_None || (UINT32)value >= S_numsfx)
 				return luaL_error(L, "seesound number %d is invalid.", value);
 			info->seesound = (sfxenum_t)value;
 		} else if (i == 6 || (str && fastcmp(str,"reactiontime")))
@@ -1435,9 +1435,9 @@ static int lib_getSfxInfo(lua_State *L)
 	lua_remove(L, 1);
 
 	i = luaL_checkinteger(L, 1);
-	if (i == 0 || i >= NUMSFX)
-		return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
-	LUA_PushUserdata(L, &S_sfx[i], META_SFXINFO);
+	if (i == 0 || i >= S_numsfx)
+		return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, S_numsfx-1);
+	LUA_PushUserdata(L, S_sfx[i], META_SFXINFO);
 	return 1;
 }
 
@@ -1449,9 +1449,9 @@ static int lib_setSfxInfo(lua_State *L)
 	lua_remove(L, 1);
 	{
 		UINT32 i = luaL_checkinteger(L, 1);
-		if (i == 0 || i >= NUMSFX)
-			return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, NUMSFX-1);
-		info = &S_sfx[i]; // get the sfxinfo to assign to.
+		if (i == 0 || i >= S_numsfx)
+			return luaL_error(L, "sfxinfo[] index %d out of range (1 - %d)", i, S_numsfx-1);
+		info = S_sfx[i]; // get the sfxinfo to assign to.
 	}
 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
 	lua_remove(L, 1); // pop sfx num, don't need it any more.
@@ -1496,7 +1496,7 @@ static int lib_setSfxInfo(lua_State *L)
 
 static int lib_sfxlen(lua_State *L)
 {
-	lua_pushinteger(L, NUMSFX);
+	lua_pushinteger(L, S_numsfx);
 	return 1;
 }
 
@@ -1574,11 +1574,9 @@ static int sfxinfo_set(lua_State *L)
 static int sfxinfo_num(lua_State *L)
 {
 	sfxinfo_t *sfx = *((sfxinfo_t **)luaL_checkudata(L, 1, META_SFXINFO));
-
 	I_Assert(sfx != NULL);
-	I_Assert(sfx >= S_sfx);
 
-	lua_pushinteger(L, (UINT32)(sfx-S_sfx));
+	lua_pushinteger(L, S_GetSoundIndex(sfx));
 	return 1;
 }
 
diff --git a/src/m_menu.c b/src/m_menu.c
index 5928787e38c9818725963b9089e7d47ce7a065c7..250ba9de6606cd200c37e969149eeb088d32d3ad 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -7563,7 +7563,7 @@ static void M_PauseLevelSelect(INT32 choice)
 			'\x1D' | V_YELLOWMAP, false);
 	}
 	if (cv_soundtest.value)
-		V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + y + 8, V_YELLOWMAP, S_sfx[cv_soundtest.value].name);
+		V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + y + 8, V_YELLOWMAP, S_sfx[cv_soundtest.value]->name);
 }*/
 
 static musicdef_t *curplaying = NULL;
@@ -7813,7 +7813,7 @@ static void M_DrawSoundTest(void)
 
 				if (curplaying == soundtestdefs[t])
 				{
-					sfxstr = (cv_soundtest.value) ? S_sfx[cv_soundtest.value].name : "N/A";
+					sfxstr = (cv_soundtest.value) ? S_sfx[cv_soundtest.value]->name : "N/A";
 					i = V_StringWidth(sfxstr, 0);
 					V_DrawFill(165+140-9-i, y-4, i+8, 16, 150);
 					V_DrawRightAlignedString(165+140-5, y, V_YELLOWMAP, sfxstr);
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index e073a863c90bbe88025fd6f7ed9f4d0decac23d0..bbe893526a51a914782ef6a6384e1d93b27ebffc 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -4379,14 +4379,14 @@ static void Gravity_OnChange(void)
 
 static void SoundTest_OnChange(void)
 {
-	INT32 sfxfreeint = (INT32)sfxfree;
+	INT32 numsfxint = (INT32)S_numsfx;
 	if (cv_soundtest.value < 0)
 	{
-		CV_SetValue(&cv_soundtest, sfxfreeint-1);
+		CV_SetValue(&cv_soundtest, numsfxint-1);
 		return;
 	}
 
-	if (cv_soundtest.value >= sfxfreeint)
+	if (cv_soundtest.value >= numsfxint)
 	{
 		CV_SetValue(&cv_soundtest, 0);
 		return;
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 29348a0f4c9c3d2597a6d671669ab054254343d6..6c4b8ceaa11faa551f89003ff87a90cba296567c 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -4496,7 +4496,7 @@ void A_Invincibility(mobj_t *actor)
 		if (mariomode)
 			G_GhostAddColor(GHC_INVINCIBLE);
 		P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV);
-		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+		strlcpy(S_sfx[sfx_None]->caption, "Invincibility", 14);
 		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
 	}
 }
@@ -4531,7 +4531,7 @@ void A_SuperSneakers(mobj_t *actor)
 			S_SpeedMusic(1.4f);
 		else
 			P_PlayJingle(player, JT_SHOES);
-		strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
+		strlcpy(S_sfx[sfx_None]->caption, "Speed shoes", 12);
 		S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
 	}
 }
@@ -11486,7 +11486,7 @@ void A_VileAttack(mobj_t *actor)
 	if (P_MobjWasRemoved(actor))
 		return;
 
-	if (locvar1 <= 0 || locvar1 >= NUMSFX)
+	if (locvar1 <= 0 || (UINT32)locvar1 >= S_numsfx)
 		soundtoplay = sfx_brakrx;
 	else
 		soundtoplay = (sfxenum_t)locvar1;
@@ -11622,7 +11622,7 @@ void A_VileFire(mobj_t *actor)
 	P_SetThingPosition(actor);
 
 	// Play sound, if one's specified
-	if (locvar1 > 0 && locvar1 < NUMSFX)
+	if (locvar1 > 0 && (UINT32)locvar1 < S_numsfx)
 		S_StartSound(actor, (sfxenum_t)locvar1);
 
 	// Now draw the line to the actor's target
@@ -11779,7 +11779,7 @@ void A_BrakChase(mobj_t *actor)
 	}
 
 	// Optionally play a sound effect
-	if (locvar2 > 0 && locvar2 < NUMSFX)
+	if (locvar2 > 0 && (UINT32)locvar2 < S_numsfx)
 		S_StartSound(actor, (sfxenum_t)locvar2);
 
 	// make active sound
diff --git a/src/p_inter.c b/src/p_inter.c
index 30a02120539672740164c63939d829d501903dfe..79e04b32084ae31069e66481c2f9834edfc2b516 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -307,7 +307,7 @@ void P_DoMatchSuper(player_t *player)
 		S_StopMusic();
 		if (mariomode)
 			G_GhostAddColor(GHC_INVINCIBLE);
-		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+		strlcpy(S_sfx[sfx_None]->caption, "Invincibility", 14);
 		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
 		S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
 	}
@@ -330,7 +330,7 @@ void P_DoMatchSuper(player_t *player)
 					S_StopMusic();
 					if (mariomode)
 						G_GhostAddColor(GHC_INVINCIBLE);
-					strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+					strlcpy(S_sfx[sfx_None]->caption, "Invincibility", 14);
 					S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
 					S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
 				}
diff --git a/src/p_setup.c b/src/p_setup.c
index 2c9ca50b65cbbd4454495eefd11c84f5cebd3567..e9966201ec600d3c6fbd189ccb64de9cd34c5a8b 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1299,11 +1299,11 @@ static void P_WriteSkincolor(INT32 constant, char **target)
 static void P_WriteSfx(INT32 constant, char **target)
 {
 	if (constant <= sfx_None
-	|| constant >= (INT32)sfxfree)
+	|| constant >= (INT32)S_numsfx)
 		return;
 
 	P_WriteDuplicateText(
-		va("SFX_%s", S_sfx[constant].name),
+		va("SFX_%s", S_sfx[constant]->name),
 		target
 	);
 }
@@ -8107,15 +8107,15 @@ void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num)
 	for (; num > 0; num--, lumpinfo++)
 	{
 		// Let's check whether it's replacing an existing sound or it's a brand new one.
-		for (j = 1; j < NUMSFX; j++)
+		for (j = 1; j < S_numsfx; j++)
 		{
-			if (S_sfx[j].name && !strnicmp(S_sfx[j].name, lumpinfo->name + 2, 6))
+			if (S_sfx[j]->name && !strnicmp(S_sfx[j]->name, lumpinfo->name + 2, 6))
 			{
 				// the sound will be reloaded when needed,
 				// since sfx->data will be NULL
 				CONS_Debug(DBG_SETUP, "Sound %.8s replaced\n", lumpinfo->name);
 
-				I_FreeSfx(&S_sfx[j]);
+				I_FreeSfx(S_sfx[j]);
 				break; // there shouldn't be two sounds with the same name, so stop looking
 			}
 		}
@@ -8244,15 +8244,15 @@ static boolean P_LoadAddon(UINT16 numlumps)
 			{
 				if (name[1] == 'S')
 				{
-					for (j = 1; j < NUMSFX; j++)
+					for (j = 1; j < S_numsfx; j++)
 					{
-						if (S_sfx[j].name && !strnicmp(S_sfx[j].name, name + 2, 6))
+						if (S_sfx[j]->name && !strnicmp(S_sfx[j]->name, name + 2, 6))
 						{
 							// the sound will be reloaded when needed,
 							// since sfx->data will be NULL
 							CONS_Debug(DBG_SETUP, "Sound %.8s replaced\n", name);
 
-							I_FreeSfx(&S_sfx[j]);
+							I_FreeSfx(S_sfx[j]);
 
 							sreplaces++;
 							break; // there shouldn't be two sounds with the same name, so stop looking
diff --git a/src/p_spec.c b/src/p_spec.c
index 28f9859ab1c44d77400fda0ee64f59f85f8d19c6..32f2de6f34dd4558fee600b5397047e1bc5355f1 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1900,7 +1900,7 @@ static void P_PlaySFX(INT32 sfxnum, mobj_t *mo, sector_t *callsec, INT16 tag, te
 	if (sfxnum == sfx_None)
 		return; // Do nothing!
 
-	if (sfxnum < sfx_None || sfxnum >= NUMSFX)
+	if (sfxnum < sfx_None || (UINT32)sfxnum >= S_numsfx)
 	{
 		CONS_Debug(DBG_GAMELOGIC, "Line type 414 Executor: sfx number %d is invalid!\n", sfxnum);
 		return;
diff --git a/src/p_user.c b/src/p_user.c
index 503453b6227b8f21564d303c014e62204efee51c..2545166bd8af731f9d4aaa41c95b356302ac1b10 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -1573,7 +1573,7 @@ void P_PlayLivesJingle(player_t *player)
 		P_PlayJingle(player, JT_1UP);
 		if (player)
 			player->powers[pw_extralife] = extralifetics + 1;
-		strlcpy(S_sfx[sfx_None].caption, "One-up", 7);
+		strlcpy(S_sfx[sfx_None]->caption, "One-up", 7);
 		S_StartCaption(sfx_None, -1, extralifetics+1);
 	}
 }
@@ -1629,7 +1629,7 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname)
 			case JT_SHOES:  // Speed shoes
 				if (players[i].powers[pw_sneakers] > 1 && !players[i].powers[pw_super])
 				{
-					//strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
+					//strlcpy(S_sfx[sfx_None]->caption, "Speed shoes", 12);
 					//S_StartCaption(sfx_None, -1, players[i].powers[pw_sneakers]);
 					result = true;
 				}
@@ -1641,7 +1641,7 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname)
 			case JT_MINV: // Mario Invincibility
 				if (players[i].powers[pw_invulnerability] > 1)
 				{
-					//strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+					//strlcpy(S_sfx[sfx_None]->caption, "Invincibility", 14);
 					//S_StartCaption(sfx_None, -1, players[i].powers[pw_invulnerability]);
 					result = true;
 				}
@@ -1710,7 +1710,7 @@ void P_RestoreMusic(player_t *player)
 	// Invulnerability
 	else if (player->powers[pw_invulnerability] > 1 && !player->powers[pw_super])
 	{
-		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+		strlcpy(S_sfx[sfx_None]->caption, "Invincibility", 14);
 		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
 		if (!S_RecallMusic(JT_INV, false) && !S_RecallMusic(JT_MINV, false))
 			P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV);
@@ -1719,7 +1719,7 @@ void P_RestoreMusic(player_t *player)
 	// Shoes
 	else if (player->powers[pw_sneakers] > 1 && !player->powers[pw_super])
 	{
-		strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
+		strlcpy(S_sfx[sfx_None]->caption, "Speed shoes", 12);
 		S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
 		if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)
 		{
diff --git a/src/r_skins.c b/src/r_skins.c
index c09ff3417f9061f7ed96b70b6083d7c47bb3f219..f378f30d379739ade7073fe6ec0484f48a049369 100644
--- a/src/r_skins.c
+++ b/src/r_skins.c
@@ -196,7 +196,7 @@ boolean P_IsValidSprite2(skin_t *skin, UINT16 spr2)
 
 static void Sk_SetDefaultValue(skin_t *skin)
 {
-	INT32 i;
+	UINT32 i;
 	//
 	// set default skin values
 	//
@@ -248,9 +248,9 @@ static void Sk_SetDefaultValue(skin_t *skin)
 
 	skin->natkcolor = SKINCOLOR_NONE;
 
-	for (i = 0; i < sfx_skinsoundslot0; i++)
-		if (S_sfx[i].skinsound != -1)
-			skin->soundsid[S_sfx[i].skinsound] = i;
+	for (i = 0; i < S_numsfx; i++)
+		if (S_sfx[i]->skinsound != -1)
+			skin->soundsid[S_sfx[i]->skinsound] = i;
 }
 
 //
@@ -755,16 +755,16 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value)
 
 		// copy name of sounds that are remapped
 		// for this skin
-		for (i = 0; i < sfx_skinsoundslot0; i++)
+		for (i = 0; i < S_numsfx; i++)
 		{
-			if (!S_sfx[i].name)
+			if (!S_sfx[i]->name)
 				continue;
-			if (S_sfx[i].skinsound != -1
-				&& !stricmp(S_sfx[i].name,
+			if (S_sfx[i]->skinsound != -1
+				&& !stricmp(S_sfx[i]->name,
 					stoken + stokenadjust))
 			{
-				skin->soundsid[S_sfx[i].skinsound] =
-					S_AddSoundFx(value, S_sfx[i].singularity, S_sfx[i].pitch, true);
+				skin->soundsid[S_sfx[i]->skinsound] =
+					S_AddSoundFx(value, S_sfx[i]->singularity, S_sfx[i]->pitch);
 				found = true;
 			}
 		}
diff --git a/src/s_sound.c b/src/s_sound.c
index 32353a59c2d590dd8ea1f74b47399de0886684d0..608d3aae064598fc42d339388391f008b3a9ffdd 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -401,7 +401,7 @@ void S_StopSoundByID(void *origin, sfxenum_t sfx_id)
 #endif
 	for (cnum = 0; cnum < numofchannels; cnum++)
 	{
-		if (channels[cnum].sfxinfo == &S_sfx[sfx_id] && channels[cnum].origin == origin)
+		if (channels[cnum].sfxinfo == S_sfx[sfx_id] && channels[cnum].origin == origin)
 		{
 			S_StopChannel(cnum);
 			break;
@@ -422,7 +422,7 @@ void S_StopSoundByNum(sfxenum_t sfxnum)
 #endif
 	for (cnum = 0; cnum < numofchannels; cnum++)
 	{
-		if (channels[cnum].sfxinfo == &S_sfx[sfxnum])
+		if (channels[cnum].sfxinfo == S_sfx[sfxnum])
 		{
 			S_StopChannel(cnum);
 			break;
@@ -441,14 +441,14 @@ void S_StartCaption(sfxenum_t sfx_id, INT32 cnum, UINT16 lifespan)
 
 	// check for bogus sound #
 	// I_Assert(sfx_id >= 0); -- allowing sfx_None; this shouldn't be allowed directly if S_StartCaption is ever exposed to Lua by itself
-	I_Assert(sfx_id < NUMSFX);
+	I_Assert(sfx_id < S_numsfx);
 
-	sfx = &S_sfx[sfx_id];
+	sfx = S_sfx[sfx_id];
 
 	if (sfx->caption[0] == '/') // no caption for this one
 		return;
 
-	start = ((closedcaptions[0].s && (closedcaptions[0].s-S_sfx == sfx_None)) ? 1 : 0);
+	start = ((closedcaptions[0].s && (closedcaptions[0].s == S_sfx[sfx_None])) ? 1 : 0);
 
 	if (sfx_id)
 	{
@@ -593,15 +593,15 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
 
 	// check for bogus sound #
 	I_Assert(sfx_id >= 1);
-	I_Assert(sfx_id < NUMSFX);
+	I_Assert(sfx_id < S_numsfx);
 
-	sfx = &S_sfx[sfx_id];
+	sfx = S_sfx[sfx_id];
 
 	if (sfx->skinsound != -1 && origin && origin->skin)
 	{
 		// redirect player sound to the sound in the skin table
 		sfx_id = ((skin_t *)origin->skin)->soundsid[sfx->skinsound];
-		sfx = &S_sfx[sfx_id];
+		sfx = S_sfx[sfx_id];
 	}
 
 	// Initialize sound parameters
@@ -998,7 +998,7 @@ void S_UpdateClosedCaptions(void)
 		if (!closedcaptions[i].s)
 			continue;
 
-		if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped)
+		if (i == 0 && (closedcaptions[0].s == S_sfx[sfx_None]) && gamestopped)
 			continue;
 
 		if (!(--closedcaptions[i].t))
@@ -1034,8 +1034,8 @@ void S_SetSfxVolume(INT32 volume)
 void S_ClearSfx(void)
 {
 	size_t i;
-	for (i = 1; i < NUMSFX; i++)
-		I_FreeSfx(S_sfx + i);
+	for (i = 1; i < S_numsfx; i++)
+		I_FreeSfx(S_sfx[i]);
 }
 
 static void S_StopChannel(INT32 cnum)
@@ -1237,7 +1237,7 @@ INT32 S_IdPlaying(sfxenum_t id)
 #endif
 
 	for (cnum = 0; cnum < numofchannels; cnum++)
-		if ((size_t)(channels[cnum].sfxinfo - S_sfx) == (size_t)id)
+		if (channels[cnum].sfxinfo == S_sfx[id])
 			return 1;
 	return 0;
 }
@@ -1258,7 +1258,7 @@ INT32 S_SoundPlaying(void *origin, sfxenum_t id)
 	for (cnum = 0; cnum < numofchannels; cnum++)
 	{
 		if (channels[cnum].origin == origin
-		 && (size_t)(channels[cnum].sfxinfo - S_sfx) == (size_t)id)
+		 && channels[cnum].sfxinfo == S_sfx[id])
 			return 1;
 	}
 	return 0;
@@ -1267,18 +1267,17 @@ INT32 S_SoundPlaying(void *origin, sfxenum_t id)
 //
 // S_StartSoundName
 // Starts a sound using the given name.
-#define MAXNEWSOUNDS 10
-static sfxenum_t newsounds[MAXNEWSOUNDS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 void S_StartSoundName(void *mo, const char *soundname)
 {
-	INT32 i, soundnum = 0;
+	UINT32 i;
+	INT32 soundnum = 0;
 	// Search existing sounds...
-	for (i = sfx_None + 1; i < NUMSFX; i++)
+	for (i = sfx_None + 1; i < S_numsfx; i++)
 	{
-		if (!S_sfx[i].name)
+		if (!S_sfx[i]->name)
 			continue;
-		if (!stricmp(S_sfx[i].name, soundname))
+		if (!stricmp(S_sfx[i]->name, soundname))
 		{
 			soundnum = i;
 			break;
@@ -1287,25 +1286,7 @@ void S_StartSoundName(void *mo, const char *soundname)
 
 	if (!soundnum)
 	{
-		for (i = 0; i < MAXNEWSOUNDS; i++)
-		{
-			if (newsounds[i] == 0)
-				break;
-			if (!S_IdPlaying(newsounds[i]))
-			{
-				S_RemoveSoundFx(newsounds[i]);
-				break;
-			}
-		}
-
-		if (i == MAXNEWSOUNDS)
-		{
-			CONS_Debug(DBG_GAMELOGIC, "Cannot load another extra sound!\n");
-			return;
-		}
-
-		soundnum = S_AddSoundFx(soundname, false, 0, false);
-		newsounds[i] = soundnum;
+		soundnum = S_AddSoundFx(soundname, false, 0);
 	}
 
 	S_StartSound(mo, soundnum);
@@ -1318,7 +1299,7 @@ void S_StartSoundName(void *mo, const char *soundname)
 //
 void S_InitSfxChannels(INT32 sfxVolume)
 {
-	INT32 i;
+	UINT32 i;
 
 	if (dedicated)
 		return;
@@ -1328,10 +1309,10 @@ void S_InitSfxChannels(INT32 sfxVolume)
 	SetChannelsNum();
 
 	// Note that sounds have not been cached (yet).
-	for (i = 1; i < NUMSFX; i++)
+	for (i = 1; i < S_numsfx; i++)
 	{
-		S_sfx[i].usefulness = -1; // for I_GetSfx()
-		S_sfx[i].lumpnum = LUMPERROR;
+		S_sfx[i]->usefulness = -1; // for I_GetSfx()
+		S_sfx[i]->lumpnum = LUMPERROR;
 	}
 
 	// precache sounds if requested by cmdline, or precachesound var true
@@ -1340,9 +1321,9 @@ void S_InitSfxChannels(INT32 sfxVolume)
 		// Initialize external data (all sounds) at start, keep static.
 		CONS_Printf(M_GetText("Loading sounds... "));
 
-		for (i = 1; i < NUMSFX; i++)
-			if (S_sfx[i].name)
-				S_sfx[i].data = I_GetSfx(&S_sfx[i]);
+		for (i = 1; i < S_numsfx; i++)
+			if (S_sfx[i]->name)
+				S_sfx[i]->data = I_GetSfx(S_sfx[i]);
 
 		CONS_Printf(M_GetText(" pre-cached all sound data\n"));
 	}
@@ -2325,7 +2306,7 @@ void S_StopMusic(void)
 
 	if (cv_closedcaptioning.value)
 	{
-		if (closedcaptions[0].s-S_sfx == sfx_None)
+		if (closedcaptions[0].s == S_sfx[sfx_None])
 		{
 			if (gamestate != wipegamestate)
 			{
diff --git a/src/screen.c b/src/screen.c
index 9a82a15618a4045d651d10aae38a8e963b6711a5..aee4ee1dab6f5013c5218fbb233d462f2dce2ab0 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -507,7 +507,7 @@ void SCR_ClosedCaptions(void)
 		if (!closedcaptions[i].s)
 			continue;
 
-		music = (closedcaptions[i].s-S_sfx == sfx_None);
+		music = (closedcaptions[i].s == S_sfx[sfx_None]);
 
 		if (music && !gamestopped && (closedcaptions[i].t < flashingtics) && (closedcaptions[i].t & 1))
 			continue;
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 8f2ca340879d5236822c534b2f9da57774a5356e..d20b036e9ad94b13fe709b485408729abf878c67 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -610,7 +610,7 @@ void I_FreeSfx(sfxinfo_t *sfx)
 INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel)
 {
 	UINT8 volume = (((UINT16)vol + 1) * (UINT16)sfx_volume) / 62; // (256 * 31) / 62 == 127
-	INT32 handle = Mix_PlayChannel(channel, S_sfx[id].data, 0);
+	INT32 handle = Mix_PlayChannel(channel, S_sfx[id]->data, 0);
 	Mix_Volume(handle, volume);
 	Mix_SetPanning(handle, min((UINT16)(0xff-sep)<<1, 0xff), min((UINT16)(sep)<<1, 0xff));
 	(void)pitch; // Mixer can't handle pitch
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index 2705261d623e4d1109ecdbdd2aa7d6fb5072579b..875ed8ca6194963798b5e86ab80993d4db9a52ab 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -88,8 +88,6 @@
 // Needed for calling the actual sound output.
 #define NUM_CHANNELS            MIX_CHANNELS*4
 
-#define INDEXOFSFX(x) ((sfxinfo_t *)x - S_sfx)
-
 static Uint16 samplecount = 1024; //Alam: 1KB samplecount at 22050hz is 46.439909297052154195011337868481ms of buffer
 
 typedef struct chan_struct
@@ -435,7 +433,7 @@ static INT32 addsfx(sfxenum_t sfxid, INT32 volume, INT32 step, INT32 seperation)
 	// Okay, in the less recent channel,
 	//  we will handle the new SFX.
 	// Set pointer to raw data.
-	channels[slot].data = (Uint8 *)S_sfx[sfxid].data;
+	channels[slot].data = (Uint8 *)S_sfx[sfxid]->data;
 	channels[slot].samplerate = (channels[slot].data[3]<<8)+channels[slot].data[2];
 	channels[slot].data += 8; //Alam: offset of the sound header
 
@@ -460,12 +458,12 @@ static INT32 addsfx(sfxenum_t sfxid, INT32 volume, INT32 step, INT32 seperation)
 
 	// Preserve sound SFX id,
 	//  e.g. for avoiding duplicates of chainsaw.
-	channels[slot].id = S_sfx[sfxid].data;
+	channels[slot].id = S_sfx[sfxid]->data;
 
 	channels[slot].sfxid = sfxid;
 
 	// Set pointer to end of raw data.
-	channels[slot].end = channels[slot].data + S_sfx[sfxid].length;
+	channels[slot].end = channels[slot].data + S_sfx[sfxid]->length;
 
 
 	// You tell me.
@@ -566,14 +564,14 @@ void I_FreeSfx(sfxinfo_t * sfx)
 	{
 		size_t i;
 
-		for (i = 1; i < NUMSFX; i++)
+		for (i = 1; i < S_numsfx; i++)
 		{
 			// Alias? Example is the chaingun sound linked to pistol.
-			if (S_sfx[i].data == sfx->data)
+			if (S_sfx[i]->data == sfx->data)
 			{
-				if (S_sfx+i != sfx) S_sfx[i].data = NULL;
-				S_sfx[i].lumpnum = LUMPERROR;
-				S_sfx[i].length = 0;
+				if (S_sfx[i] != sfx) S_sfx[i]->data = NULL;
+				S_sfx[i]->lumpnum = LUMPERROR;
+				S_sfx[i]->length = 0;
 			}
 		}
 		//Snd_LockAudio(); //Alam: too much?
@@ -614,7 +612,7 @@ INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priori
 	if (sound_disabled)
 		return 0;
 
-	if (S_sfx[id].data == NULL) return -1;
+	if (S_sfx[id]->data == NULL) return -1;
 
 	Snd_LockAudio();
 	id = addsfx(id, vol, steptable[pitch], sep);
@@ -1275,7 +1273,7 @@ void I_StartupSound(void)
 			//I_AddExitFunc(I_ShutdownSound);
 			snddev.bps = 16;
 			snddev.sample_rate = audio.freq;
-			snddev.numsfxs = NUMSFX;
+			snddev.numsfxs = S_numsfx;
 #if defined (_WIN32)
 			snddev.cooplevel = 0x00000002;
 			snddev.hWnd = vid.WndParent;
diff --git a/src/sounds.c b/src/sounds.c
index 19b69dd2825748ae97493172a2571d970a1b4dec..6b33ade2478d53b871938430523f3fb60a0f76a9 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -25,7 +25,7 @@
 // Information about all the sfx
 //
 
-sfxinfo_t S_sfx[NUMSFX] =
+static const sfxinfo_t S_startsfx[] =
 {
 
 /*****
@@ -827,95 +827,52 @@ sfxinfo_t S_sfx[NUMSFX] =
   // initialized to NULL
 };
 
-char freeslotnames[sfx_freeslot0 + NUMSFXFREESLOTS + NUMSKINSFXSLOTS][7];
+sfxinfo_t **S_sfx;
+UINT32 S_numsfx;
 
 // Prepare free sfx slots to add sfx at run time
 void S_InitRuntimeSounds (void)
 {
-	sfxenum_t i;
-	INT32 value;
-	char soundname[10];
+	UINT32 i;
 
-	for (i = sfx_freeslot0; i <= sfx_lastskinsoundslot; i++)
+	S_numsfx = sizeof(S_startsfx) / sizeof(S_startsfx[0]);
+	S_sfx = Z_Malloc(S_numsfx * sizeof(*S_sfx), PU_STATIC, NULL);
+	for (i = 0; i < S_numsfx; i++)
 	{
-		value = (i+1) - sfx_freeslot0;
-
-		if (value < 10)
-			sprintf(soundname, "fre00%d", value);
-		else if (value < 100)
-			sprintf(soundname, "fre0%d", value);
-		else if (value < 1000)
-			sprintf(soundname, "fre%d", value);
-		else
-			sprintf(soundname, "fr%d", value);
-
-		strcpy(freeslotnames[value-1], soundname);
-
-		S_sfx[i].name = freeslotnames[value-1];
-		S_sfx[i].singularity = false;
-		S_sfx[i].priority = 0;
-		S_sfx[i].pitch = 0;
-		S_sfx[i].volume = -1;
-		S_sfx[i].data = NULL;
-		S_sfx[i].length = 0;
-		S_sfx[i].skinsound = -1;
-		S_sfx[i].usefulness = -1;
-		S_sfx[i].lumpnum = LUMPERROR;
-		//strlcpy(S_sfx[i].caption, "", 1);
-		S_sfx[i].caption[0] = '\0';
+		S_sfx[i] = Z_Malloc(sizeof(sfxinfo_t), PU_STATIC, NULL);
+		memcpy(S_sfx[i], &S_startsfx[i], sizeof(sfxinfo_t));
 	}
 }
 
-sfxenum_t sfxfree = sfx_freeslot0;
-
 // Add a new sound fx into a free sfx slot.
 //
-sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound)
+sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags)
 {
-	sfxenum_t i;
-
-	if (skinsound)
-	{
-		for (i = sfx_skinsoundslot0; i < NUMSFX; i++)
-		{
-			if (S_sfx[i].priority)
-				continue;
-			break;
-		}
-	}
-	else
-		i = sfxfree;
-
-	if (i < NUMSFX)
-	{
-		strncpy(freeslotnames[i-sfx_freeslot0], name, 6);
-		S_sfx[i].singularity = singular;
-		S_sfx[i].priority = 60;
-		S_sfx[i].pitch = flags;
-		S_sfx[i].volume = -1;
-		S_sfx[i].lumpnum = LUMPERROR;
-		S_sfx[i].skinsound = -1;
-		S_sfx[i].usefulness = -1;
-
-		/// \todo if precached load it here
-		S_sfx[i].data = NULL;
-
-		if (!skinsound)
-			sfxfree++;
-
-		return i;
-	}
-	CONS_Alert(CONS_WARNING, M_GetText("No more free sound slots\n"));
-	return 0;
+	UINT32 i = S_numsfx;
+	S_sfx = Z_Realloc(S_sfx, ++S_numsfx * sizeof(*S_sfx), PU_STATIC, NULL);
+	S_sfx[i] = Z_Malloc(sizeof(sfxinfo_t), PU_STATIC, NULL);
+	S_sfx[i]->name = Z_StrDup(name);
+	S_sfx[i]->singularity = singular;
+	S_sfx[i]->priority = 60;
+	S_sfx[i]->pitch = flags;
+	S_sfx[i]->volume = -1;
+	/// \todo if precached load it here
+	S_sfx[i]->data = NULL;
+	S_sfx[i]->length = 0;
+	S_sfx[i]->skinsound = -1;
+	S_sfx[i]->usefulness = -1;
+	S_sfx[i]->lumpnum = LUMPERROR;
+	S_sfx[i]->caption[0] = '\0';
+	return i;
 }
 
-void S_RemoveSoundFx(sfxenum_t id)
+UINT32 S_GetSoundIndex(sfxinfo_t *sfx)
 {
-	if (id >= sfx_freeslot0 && id <= sfx_lastskinsoundslot
-		&& S_sfx[id].priority != 0)
+	UINT32 i;
+	for (i = 0; i < S_numsfx; i++)
 	{
-		S_sfx[id].lumpnum = LUMPERROR;
-		I_FreeSfx(&S_sfx[id]);
-		S_sfx[id].priority = 0;
+		if (S_sfx[i] == sfx)
+			return i;
 	}
+	I_Error("Tried to get index of an invalid sfxinfo_t!");
 }
diff --git a/src/sounds.h b/src/sounds.h
index bf934276858a301a542492abd560f730a4f1077d..c8d2fd3e8781512d36b4dd236831654600165594 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -41,10 +41,6 @@ typedef enum
 	NUMSKINSOUNDS
 } skinsound_t;
 
-// free sfx for S_AddSoundFx()
-#define NUMSFXFREESLOTS 1600 // Matches SOC Editor.
-#define NUMSKINSFXSLOTS (128*NUMSKINSOUNDS)
-
 //
 // SoundFX struct.
 //
@@ -90,7 +86,8 @@ struct sfxinfo_struct
 };
 
 // the complete set of sound effects
-extern sfxinfo_t S_sfx[];
+extern sfxinfo_t **S_sfx;
+extern UINT32 S_numsfx;
 
 //
 // Identifiers for all sfx in game.
@@ -870,24 +867,10 @@ typedef enum
 	sfx_kc6c,
 	sfx_kc6d,
 	sfx_kc6e,
-
-	// free slots for S_AddSoundFx() at run-time --------------------
-	sfx_freeslot0,
-	//
-	// ... 1600 free sounds here ...
-	//
-	sfx_lastfreeslot = sfx_freeslot0 + NUMSFXFREESLOTS-1,
-	// end of freeslots ---------------------------------------------
-
-	sfx_skinsoundslot0,
-	sfx_lastskinsoundslot = sfx_skinsoundslot0 + NUMSKINSFXSLOTS-1,
-	NUMSFX
 } sfxenum_t;
 
-
 void S_InitRuntimeSounds(void);
-sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags, boolean skinsound);
-extern sfxenum_t sfxfree; // sound test and slotting
-void S_RemoveSoundFx(sfxenum_t id);
+sfxenum_t S_AddSoundFx(const char *name, boolean singular, INT32 flags);
+UINT32 S_GetSoundIndex(sfxinfo_t *sfx);
 
 #endif