(svn r6532) - Feature: Add support for NewGRF sound effects. Currently sound priority isn't supported.
authorpeter1138
Wed, 27 Sep 2006 18:17:01 +0000
changeset 4656 9c1d8c4d3e60
parent 4655 2af9a0c4cec2
child 4657 dfbc1d1e6fd0
(svn r6532) - Feature: Add support for NewGRF sound effects. Currently sound priority isn't supported.
Makefile
aircraft_cmd.c
newgrf.c
newgrf.h
newgrf_callbacks.h
newgrf_engine.c
newgrf_engine.h
newgrf_sound.c
newgrf_sound.h
openttd.vcproj
openttd_vs80.vcproj
roadveh_cmd.c
ship_cmd.c
sound.c
sound.h
train_cmd.c
tunnelbridge_cmd.c
vehicle.c
vehicle.h
--- a/Makefile	Wed Sep 27 16:07:07 2006 +0000
+++ b/Makefile	Wed Sep 27 18:17:01 2006 +0000
@@ -697,6 +697,7 @@
 SRCS += newgrf.c
 SRCS += newgrf_cargo.c
 SRCS += newgrf_engine.c
+SRCS += newgrf_sound.c
 SRCS += newgrf_spritegroup.c
 SRCS += newgrf_station.c
 SRCS += newgrf_text.c
--- a/aircraft_cmd.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/aircraft_cmd.c	Wed Sep 27 18:17:01 2006 +0000
@@ -23,6 +23,7 @@
 #include "newgrf_engine.h"
 #include "newgrf_callbacks.h"
 #include "newgrf_text.h"
+#include "newgrf_sound.h"
 #include "date.h"
 
 static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport);
@@ -858,7 +859,9 @@
 
 static void PlayAircraftSound(const Vehicle* v)
 {
-	SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
+	if (!PlayVehicleSound(v, VSE_START)) {
+		SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
+	}
 }
 
 static bool UpdateAircraftSpeed(Vehicle *v)
@@ -1426,7 +1429,9 @@
 static void AircraftLandAirplane(Vehicle *v)
 {
 	AircraftLand(v);
-	SndPlayVehicleFx(SND_17_SKID_PLANE, v);
+	if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
+		SndPlayVehicleFx(SND_17_SKID_PLANE, v);
+	}
 	MaybeCrashAirplane(v);
 }
 
--- a/newgrf.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/newgrf.c	Wed Sep 27 18:17:01 2006 +0000
@@ -25,6 +25,8 @@
 #include "table/sprites.h"
 #include "date.h"
 #include "currency.h"
+#include "sound.h"
+#include "newgrf_sound.h"
 #include "newgrf_spritegroup.h"
 
 /* TTDPatch extended GRF format codec
@@ -58,6 +60,14 @@
 static byte *_preload_sprite = NULL;
 
 
+typedef enum GrfDataType {
+	GDT_SOUND,
+} GrfDataType;
+
+static byte _grf_data_blocks;
+static GrfDataType _grf_data_type;
+
+
 typedef enum grfspec_feature {
 	GSF_TRAIN,
 	GSF_ROAD,
@@ -1182,6 +1192,67 @@
 	return ret;
 }
 
+static bool SoundEffectChangeInfo(uint sid, int numinfo, int prop, byte **bufp, int len)
+{
+	byte *buf = *bufp;
+	int i;
+	bool ret = false;
+
+	if (_cur_grffile->sound_offset == 0) {
+		grfmsg(GMS_WARN, "SoundEffectChangeInfo: No effects defined, skipping.");
+		return false;
+	}
+
+	switch (prop) {
+		case 0x08: /* Relative volume */
+			FOR_EACH_OBJECT {
+				uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
+
+				if (sound >= GetNumSounds()) {
+					grfmsg(GMS_WARN, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds());
+				} else {
+					GetSound(sound)->volume = grf_load_byte(&buf);
+				}
+			}
+			break;
+
+		case 0x09: /* Priority */
+			FOR_EACH_OBJECT {
+				uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
+
+				if (sound >= GetNumSounds()) {
+					grfmsg(GMS_WARN, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds());
+				} else {
+					GetSound(sound)->priority = grf_load_byte(&buf);
+				}
+			}
+			break;
+
+		case 0x0A: /* Override old sound */
+			FOR_EACH_OBJECT {
+				uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
+				uint orig_sound = grf_load_byte(&buf);
+
+				if (sound >= GetNumSounds() || orig_sound >= GetNumSounds()) {
+					grfmsg(GMS_WARN, "SoundEffectChangeInfo: Sound %d or %d not defined (max %d)", sound, orig_sound, GetNumSounds());
+				} else {
+					FileEntry *newfe = GetSound(sound);
+					FileEntry *oldfe = GetSound(orig_sound);
+
+					/* Literally copy the data of the new sound over the original */
+					memcpy(oldfe, newfe, sizeof(*oldfe));
+				}
+			}
+			break;
+
+		default:
+			ret = true;
+	}
+
+	*bufp = buf;
+	return ret;
+}
+
 /* Action 0x00 */
 static void FeatureChangeInfo(byte *buf, int len)
 {
@@ -1214,7 +1285,7 @@
 		/* GSF_INDUSTRYTILES */NULL,
 		/* GSF_INDUSTRIES */   NULL,
 		/* GSF_CARGOS */       NULL,
-		/* GSF_SOUNDFX */      NULL,
+		/* GSF_SOUNDFX */      SoundEffectChangeInfo,
 	};
 
 	uint8 feature;
@@ -2708,6 +2779,113 @@
 	grfmsg(GMS_NOTICE, "DefineGotoLabel: GOTO target with label 0x%02X", label->label);
 }
 
+/* Action 0x11 */
+static void GRFSound(byte *buf, int len)
+{
+	/* <11> <num>
+	 *
+	 * W num      Number of sound files that follow */
+
+	uint16 num;
+
+	check_length(len, 1, "GRFSound");
+	buf++;
+	num = grf_load_word(&buf);
+
+	_grf_data_blocks = num;
+	_grf_data_type   = GDT_SOUND;
+
+	if (_cur_grffile->sound_offset == 0) _cur_grffile->sound_offset = GetNumSounds();
+}
+
+static void LoadGRFSound(byte *buf, int len)
+{
+	byte *buf_start = buf;
+	FileEntry *se;
+
+	/* Allocate a sound entry. This is done even if the data is not loaded
+	 * so that the indices used elsewhere are still correct. */
+	se = AllocateFileEntry();
+
+	if (grf_load_dword(&buf) != 'FFIR') {
+		grfmsg(GMS_WARN, "LoadGRFSound: Missing RIFF header");
+		return;
+	}
+
+	/* Size of file -- we ignore this */
+	grf_load_dword(&buf);
+
+	if (grf_load_dword(&buf) != 'EVAW') {
+		grfmsg(GMS_WARN, "LoadGRFSound: Invalid RIFF type");
+		return;
+	}
+
+	for (;;) {
+		uint32 tag  = grf_load_dword(&buf);
+		uint32 size = grf_load_dword(&buf);
+
+		switch (tag) {
+			case ' tmf': /* 'fmt ' */
+				/* Audio format, must be 1 (PCM) */
+				if (grf_load_word(&buf) != 1) {
+					grfmsg(GMS_WARN, "LoadGRFSound: Invalid audio format");
+					return;
+				}
+				se->channels = grf_load_word(&buf);
+				se->rate = grf_load_dword(&buf);
+				grf_load_dword(&buf);
+				grf_load_word(&buf);
+				se->bits_per_sample = grf_load_word(&buf);
+
+				/* Consume any extra bytes */
+				for (; size > 16; size--) grf_load_byte(&buf);
+				break;
+
+			case 'atad': /* 'data' */
+				se->file_size    = size;
+				se->file_offset  = FioGetPos() - (len - (buf - buf_start)) + 1;
+				se->file_offset |= _file_index << 24;
+
+				/* Set default volume and priority */
+				se->volume = 0x80;
+				se->priority = 0;
+
+				grfmsg(GMS_NOTICE, "LoadGRFSound: channels %u, sample rate %u, bits per sample %u, length %u", se->channels, se->rate, se->bits_per_sample, size);
+				return;
+
+			default:
+				se->file_size = 0;
+				return;
+		}
+	}
+}
+
+/* 'Action 0xFF' */
+static void GRFDataBlock(byte *buf, int len)
+{
+	byte name_len;
+	const char *name;
+
+	if (_grf_data_blocks == 0) {
+		grfmsg(GMS_WARN, "GRFDataBlock: unexpected data block, skipping.");
+		return;
+	}
+
+	buf++;
+	name_len = grf_load_byte(&buf);
+	name = (const char *)buf;
+	buf += name_len + 1;
+
+	grfmsg(GMS_NOTICE, "GRFDataBlock: block name '%s'...", name);
+
+	_grf_data_blocks--;
+
+	switch (_grf_data_type) {
+		case GDT_SOUND: LoadGRFSound(buf, len - name_len - 2); break;
+		default: NOT_REACHED(); break;
+	}
+}
+
 static void InitializeGRFSpecial(void)
 {
 	_ttdpatch_flags[0] =  ((_patches.always_small_airport ? 1 : 0) << 0x0C)  // keepsmallairport
@@ -2878,6 +3056,7 @@
 	_traininfo_vehicle_pitch = 0;
 	_traininfo_vehicle_width = 29;
 
+	InitializeSoundPool();
 	InitializeSpriteGroupPool();
 }
 
@@ -2986,7 +3165,7 @@
 	/* We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
 	 * is not in memory and scanning the file every time would be too expensive.
 	 * In other stages we skip action 0x10 since it's already dealt with. */
-	static const uint32 action_mask[] = {0x10000, 0x0000FB40, 0x0000FFFF};
+	static const uint32 action_mask[] = {0x10000, 0x0002FB40, 0x0000FFFF};
 
 	static const SpecialSpriteHandler handlers[] = {
 		/* 0x00 */ FeatureChangeInfo,
@@ -3006,6 +3185,7 @@
 		/* 0x0E */ GRFInhibit,
 		/* 0x0F */ NULL, // TODO implement
 		/* 0x10 */ DefineGotoLabel,
+		/* 0x11 */ GRFSound,
 	};
 
 	byte* buf;
@@ -3029,7 +3209,10 @@
 
 	action = buf[0];
 
-	if (action >= lengthof(handlers)) {
+	if (action == 0xFF) {
+		DEBUG(grf, 7) ("Handling data block in stage %d", stage);
+		GRFDataBlock(buf, num);
+	} else if (action >= lengthof(handlers)) {
 		DEBUG(grf, 7) ("Skipping unknown action 0x%02X", action);
 	} else if (!HASBIT(action_mask[stage], action)) {
 		DEBUG(grf, 7) ("Skipping action 0x%02X in stage %d", action, stage);
--- a/newgrf.h	Wed Sep 27 16:07:07 2006 +0000
+++ b/newgrf.h	Wed Sep 27 18:17:01 2006 +0000
@@ -39,6 +39,8 @@
 	int spritegroups_count;
 	struct SpriteGroup **spritegroups;
 
+	uint sound_offset;
+
 	StationSpec **stations;
 
 	uint32 param[0x80];
--- a/newgrf_callbacks.h	Wed Sep 27 16:07:07 2006 +0000
+++ b/newgrf_callbacks.h	Wed Sep 27 18:17:01 2006 +0000
@@ -45,6 +45,9 @@
 	/* Called when the player (or AI) tries to start or stop a vehicle. Mainly
 	 * used for preventing a vehicle from leaving the depot. */
 	CBID_VEHICLE_START_STOP_CHECK   = 0x31,
+
+	/* Called to play a special sound effect */
+	CBID_VEHICLE_SOUND_EFFECT       = 0x33,
 };
 
 /**
--- a/newgrf_engine.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/newgrf_engine.c	Wed Sep 27 18:17:01 2006 +0000
@@ -651,7 +651,7 @@
 			}
 
 		case 0x46: /* Motion counter */
-			return 0;
+			return v->motion_counter;
 
 		case 0x47: { /* Vehicle cargo info */
 			/* Format: ccccwwtt
--- a/newgrf_engine.h	Wed Sep 27 16:07:07 2006 +0000
+++ b/newgrf_engine.h	Wed Sep 27 18:17:01 2006 +0000
@@ -3,6 +3,7 @@
 #ifndef NEWGRF_ENGINE_H
 #define NEWGRF_ENGINE_H
 
+#include "newgrf.h"
 #include "direction.h"
 #include "newgrf_cargo.h"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/newgrf_sound.c	Wed Sep 27 18:17:01 2006 +0000
@@ -0,0 +1,73 @@
+/* $Id$ */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "pool.h"
+#include "sound.h"
+#include "engine.h"
+#include "vehicle.h"
+#include "newgrf_callbacks.h"
+#include "newgrf_engine.h"
+#include "newgrf_sound.h"
+
+enum {
+	SOUND_POOL_BLOCK_SIZE_BITS = 3, /* (1 << 3) == 8 items */
+	SOUND_POOL_MAX_BLOCKS      = 1000,
+};
+
+static uint _sound_count = 0;
+static MemoryPool _sound_pool = { "Sound", SOUND_POOL_MAX_BLOCKS, SOUND_POOL_BLOCK_SIZE_BITS, sizeof(FileEntry), NULL, NULL, 0, 0, NULL };
+
+
+/* Allocate a new FileEntry */
+FileEntry *AllocateFileEntry(void)
+{
+	if (_sound_count == _sound_pool.total_items) {
+		if (!AddBlockToPool(&_sound_pool)) return NULL;
+	}
+
+	return (FileEntry*)GetItemFromPool(&_sound_pool, _sound_count++);
+}
+
+
+void InitializeSoundPool(void)
+{
+	CleanPool(&_sound_pool);
+	_sound_count = 0;
+
+	/* Copy original sound data to the pool */
+	SndCopyToPool();
+}
+
+
+FileEntry *GetSound(uint index)
+{
+	if (index >= _sound_count) return NULL;
+	return (FileEntry*)GetItemFromPool(&_sound_pool, index);
+}
+
+
+uint GetNumSounds(void)
+{
+	return _sound_count;
+}
+
+
+bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event)
+{
+	const GRFFile *file = GetEngineGRF(v->engine_type);
+	uint16 callback;
+
+	/* If the engine has no GRF ID associated it can't ever play any new sounds */
+	if (file == NULL) return false;
+
+	/* Check that the vehicle type uses the sound effect callback */
+	if (!HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_SOUND_EFFECT)) return false;
+
+	callback = GetVehicleCallback(CBID_VEHICLE_SOUND_EFFECT, event, 0, v->engine_type, v);
+	if (callback == CALLBACK_FAILED) return false;
+	if (callback >= GetNumOriginalSounds()) callback += file->sound_offset - GetNumOriginalSounds();
+
+	SndPlayVehicleFx(callback, v);
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/newgrf_sound.h	Wed Sep 27 18:17:01 2006 +0000
@@ -0,0 +1,25 @@
+/* $Id$ */
+
+#ifndef NEWGRF_SOUND_H
+#define NEWGRF_SOUND_H
+
+typedef enum VehicleSoundEvents {
+	VSE_START        = 1,
+	VSE_TUNNEL       = 2,
+	VSE_BREAKDOWN    = 3,
+	VSE_RUNNING      = 4,
+	VSE_TOUCHDOWN    = 5,
+	VSE_TRAIN_EFFECT = 6,
+	VSE_RUNNING_16   = 7,
+	VSE_STOPPED_16   = 8,
+	VSE_LOAD_UNLOAD  = 9,
+} VehicleSoundEvent;
+
+
+FileEntry *AllocateFileEntry(void);
+void InitializeSoundPool(void);
+FileEntry *GetSound(uint index);
+uint GetNumSounds(void);
+bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event);
+
+#endif /* NEWGRF_SOUND_H */
--- a/openttd.vcproj	Wed Sep 27 16:07:07 2006 +0000
+++ b/openttd.vcproj	Wed Sep 27 18:17:01 2006 +0000
@@ -304,6 +304,9 @@
 				RelativePath=".\newgrf_engine.c">
 			</File>
 			<File
+				RelativePath=".\newgrf_sound.c">
+			</File>
+			<File
 				RelativePath=".\newgrf_spritegroup.c">
 			</File>
 			<File
@@ -551,6 +554,9 @@
 				RelativePath=".\newgrf_engine.h">
 			</File>
 			<File
+				RelativePath=".\newgrf_sound.h">
+			</File>
+			<File
 				RelativePath=".\newgrf_spritegroup.h">
 			</File>
 			<File
--- a/openttd_vs80.vcproj	Wed Sep 27 16:07:07 2006 +0000
+++ b/openttd_vs80.vcproj	Wed Sep 27 18:17:01 2006 +0000
@@ -653,6 +653,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\newgrf_sound.c"
+				>
+			</File>
+			<File
 				RelativePath=".\newgrf_spritegroup.c"
 				>
 			</File>
@@ -1028,6 +1032,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\newgrf_sound.h"
+				>
+			</File>
+			<File
 				RelativePath=".\newgrf_spritegroup.h"
 				>
 			</File>
--- a/roadveh_cmd.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/roadveh_cmd.c	Wed Sep 27 18:17:01 2006 +0000
@@ -26,6 +26,7 @@
 #include "newgrf_callbacks.h"
 #include "newgrf_engine.h"
 #include "newgrf_text.h"
+#include "newgrf_sound.h"
 #include "yapf/yapf.h"
 #include "date.h"
 
@@ -621,8 +622,10 @@
 		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
-		SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
-			SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
+		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
+			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
+				SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
+		}
 
 		if (!(v->vehstatus & VS_HIDDEN)) {
 			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
@@ -760,10 +763,12 @@
 
 static void StartRoadVehSound(const Vehicle* v)
 {
-	SoundFx s = RoadVehInfo(v->engine_type)->sfx;
-	if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
-		s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
-	SndPlayVehicleFx(s, v);
+	if (!PlayVehicleSound(v, VSE_START)) {
+		SoundFx s = RoadVehInfo(v->engine_type)->sfx;
+		if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
+			s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
+		SndPlayVehicleFx(s, v);
+	}
 }
 
 typedef struct RoadVehFindData {
--- a/ship_cmd.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/ship_cmd.c	Wed Sep 27 18:17:01 2006 +0000
@@ -25,6 +25,7 @@
 #include "debug.h"
 #include "newgrf_callbacks.h"
 #include "newgrf_text.h"
+#include "newgrf_sound.h"
 #include "date.h"
 
 static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
@@ -173,8 +174,10 @@
 		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
-		SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
-			SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
+		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
+			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
+				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
+		}
 
 		if (!(v->vehstatus & VS_HIDDEN)) {
 			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
@@ -198,7 +201,9 @@
 
 static void PlayShipSound(Vehicle *v)
 {
-	SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
+	if (!PlayVehicleSound(v, VSE_START)) {
+		SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
+	}
 }
 
 static void ProcessShipOrder(Vehicle *v)
--- a/sound.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/sound.c	Wed Sep 27 18:17:01 2006 +0000
@@ -10,14 +10,7 @@
 #include "window.h"
 #include "viewport.h"
 #include "fileio.h"
-
-typedef struct FileEntry {
-	uint32 file_offset;
-	uint32 file_size;
-	uint16 rate;
-	uint8 bits_per_sample;
-	uint8 channels;
-} FileEntry;
+#include "newgrf_sound.h"
 
 static uint _file_count;
 static FileEntry *_files;
@@ -100,14 +93,19 @@
 	}
 }
 
+uint GetNumOriginalSounds(void)
+{
+	return _file_count;
+}
+
 static bool SetBankSource(MixerChannel *mc, uint bank)
 {
 	const FileEntry *fe;
 	int8 *mem;
 	uint i;
 
-	if (bank >= _file_count) return false;
-	fe = &_files[bank];
+	if (bank >= GetNumSounds()) return false;
+	fe = GetSound(bank);
 
 	if (fe->file_size == 0) return false;
 
@@ -180,6 +178,20 @@
 	72,
 };
 
+void SndCopyToPool(void)
+{
+	uint i;
+
+	for (i = 0; i < _file_count; i++) {
+		FileEntry *orig = &_files[_sound_idx[i]];
+		FileEntry *fe = AllocateFileEntry();
+
+		memcpy(fe, orig, sizeof(*orig));
+		fe->volume = _sound_base_vol[i];
+		fe->priority = 0;
+	}
+}
+
 static void SndPlayScreenCoordFx(SoundFx sound, int x, int y)
 {
 	const Window *w;
@@ -195,9 +207,9 @@
 			int left = (x - vp->virtual_left);
 
 			StartSound(
-				_sound_idx[sound],
+				sound,
 				left / (vp->virtual_width / ((PANNING_LEVELS << 1) + 1)) - PANNING_LEVELS,
-				(_sound_base_vol[sound] * msf.effect_vol * _vol_factor_by_zoom[vp->zoom]) >> 15
+				(GetSound(sound)->volume * msf.effect_vol * _vol_factor_by_zoom[vp->zoom]) >> 15
 			);
 			return;
 		}
@@ -225,8 +237,8 @@
 void SndPlayFx(SoundFx sound)
 {
 	StartSound(
-		_sound_idx[sound],
+		sound,
 		0,
-		(_sound_base_vol[sound] * msf.effect_vol) >> 7
+		(GetSound(sound)->volume * msf.effect_vol) >> 7
 	);
 }
--- a/sound.h	Wed Sep 27 16:07:07 2006 +0000
+++ b/sound.h	Wed Sep 27 18:17:01 2006 +0000
@@ -16,7 +16,18 @@
 
 VARDEF MusicFileSettings msf;
 
+typedef struct FileEntry {
+	uint32 file_offset;
+	uint32 file_size;
+	uint16 rate;
+	uint8 bits_per_sample;
+	uint8 channels;
+	uint8 volume;
+	uint8 priority;
+} FileEntry;
+
 bool SoundInitialize(const char *filename);
+uint GetNumOriginalSounds(void);
 
 typedef enum SoundFx {
 	SND_02_SPLAT,                          //  0 == 0x00 !
@@ -97,5 +108,6 @@
 void SndPlayTileFx(SoundFx sound, TileIndex tile);
 void SndPlayVehicleFx(SoundFx sound, const Vehicle *v);
 void SndPlayFx(SoundFx sound);
+void SndCopyToPool(void);
 
 #endif /* SOUND_H */
--- a/train_cmd.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/train_cmd.c	Wed Sep 27 18:17:01 2006 +0000
@@ -27,6 +27,7 @@
 #include "train.h"
 #include "newgrf_callbacks.h"
 #include "newgrf_engine.h"
+#include "newgrf_sound.h"
 #include "newgrf_text.h"
 #include "direction.h"
 #include "yapf/yapf.h"
@@ -2021,6 +2022,7 @@
 static void HandleLocomotiveSmokeCloud(const Vehicle* v)
 {
 	const Vehicle* u;
+	bool sound = false;
 
 	if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2)
 		return;
@@ -2068,6 +2070,7 @@
 			// steam smoke.
 			if (GB(v->tick_counter, 0, 4) == 0) {
 				CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
+				sound = true;
 			}
 			break;
 
@@ -2075,6 +2078,7 @@
 			// diesel smoke
 			if (u->cur_speed <= 40 && CHANCE16(15, 128)) {
 				CreateEffectVehicleRel(v, 0, 0, 10, EV_DIESEL_SMOKE);
+				sound = true;
 			}
 			break;
 
@@ -2082,10 +2086,13 @@
 			// blue spark
 			if (GB(v->tick_counter, 0, 2) == 0 && CHANCE16(1, 45)) {
 				CreateEffectVehicleRel(v, 0, 0, 10, EV_ELECTRIC_SPARK);
+				sound = true;
 			}
 			break;
 		}
 	} while ((v = v->next) != NULL);
+
+	if (sound) PlayVehicleSound(u, VSE_TRAIN_EFFECT);
 }
 
 static void TrainPlayLeaveStationSound(const Vehicle* v)
@@ -2098,6 +2105,8 @@
 
 	EngineID engtype = v->engine_type;
 
+	if (PlayVehicleSound(v, VSE_START)) return;
+
 	switch (GetEngine(engtype)->railtype) {
 		case RAILTYPE_RAIL:
 		case RAILTYPE_ELECTRIC:
@@ -3262,8 +3271,10 @@
 		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
-		SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
-			SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
+		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
+			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
+				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
+		}
 
 		if (!(v->vehstatus & VS_HIDDEN)) {
 			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
--- a/tunnelbridge_cmd.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/tunnelbridge_cmd.c	Wed Sep 27 18:17:01 2006 +0000
@@ -28,6 +28,7 @@
 #include "water_map.h"
 #include "yapf/yapf.h"
 #include "date.h"
+#include "newgrf_sound.h"
 
 #include "table/bridge_land.h"
 
@@ -1361,8 +1362,9 @@
 
 			if (v->u.rail.track != 0x40 && dir == vdir) {
 				if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) {
-					if (v->spritenum < 4)
+					if (!PlayVehicleSound(v, VSE_TUNNEL) && v->spritenum < 4) {
 						SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
+					}
 					return 0;
 				}
 				if (fc == _tunnel_fractcoord_2[dir]) {
--- a/vehicle.c	Wed Sep 27 16:07:07 2006 +0000
+++ b/vehicle.c	Wed Sep 27 18:17:01 2006 +0000
@@ -33,6 +33,7 @@
 #include "yapf/yapf.h"
 #include "date.h"
 #include "newgrf_engine.h"
+#include "newgrf_sound.h"
 
 #define INVALID_COORD (-0x8000)
 #define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
@@ -646,6 +647,22 @@
 
 	FOR_ALL_VEHICLES(v) {
 		_vehicle_tick_procs[v->type - 0x10](v);
+
+		switch (v->type) {
+			case VEH_Train:
+			case VEH_Road:
+			case VEH_Aircraft:
+			case VEH_Ship:
+				if (v->type == VEH_Train && IsTrainWagon(v)) continue;
+				if (v->type == VEH_Aircraft && v->subtype > 0) continue;
+
+				v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
+				/* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
+				if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
+
+				/* Play an alterate running sound every 16 ticks */
+				if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
+		}
 	}
 
 	// now we handle all the vehicles that entered a depot this tick
--- a/vehicle.h	Wed Sep 27 16:07:07 2006 +0000
+++ b/vehicle.h	Wed Sep 27 18:17:01 2006 +0000
@@ -174,6 +174,7 @@
 	byte subspeed;           // fractional speed
 	byte acceleration;       // used by train & aircraft
 	byte progress;
+	uint32 motion_counter;
 
 	byte vehstatus;          // Status
 	StationID last_station_visited;