shithub: dumb

Download patch

ref: f230355b7f90105f13ca56343c4d2e2c1763d68f
parent: 684f4672fe54d3e82a6912a8896cf812ca0acbdb
author: Chris Moeller <kode54@gmail.com>
date: Mon Jan 11 04:00:56 EST 2010

{7/24/2007 3:48:08 PM}2007-01-26 22:50 UTC - kode54
- Moved IT S70-2 effects alongside the rest of S7x so they all trigger after
  paired notes.
- Integrated note on/cut ramping with volume transition ramping to reduce
  setup/cleanup overhead of calling render_playing and the resampler functions
  for single samples.
- Note on/cut ramping scaled from 7 sample logarithmic and 256 sample linear to
  .75ms and 5ms respectively, both linear.
- Integrated the pattern looping changes from DUMB 0.9.3, since a few files
  seemed to be broken. The XM-only features still needed to be flagged for XM
  so they won't break MOD.

git-tfs-id: [http://localhost:8080/tfs/DefaultCollection/]$/foobar2000/files/plugins.root;C181

--- a/dumb/include/internal/it.h
+++ b/dumb/include/internal/it.h
@@ -626,7 +626,7 @@
 	unsigned char lastS;
 	unsigned char pat_loop_row;
 	unsigned char pat_loop_count;
-	//unsigned char pat_loop_end_row; /* Used to catch infinite pattern loops */
+	unsigned char pat_loop_end_row; /* Used to catch infinite pattern loops */
 	unsigned char lastW;
 
 	unsigned char xm_lastE1;
--- a/dumb/src/helpers/resamp2.inc
+++ b/dumb/src/helpers/resamp2.inc
@@ -109,8 +109,8 @@
 		lvolr = (int)(volume_left->volume * 16777216.0); \
 		lvold = (int)(volume_left->delta * 16777216.0); \
 		lvolt = (int)(volume_left->target * 16777216.0); \
-		lvolm = (int)(volume_left->mix * 65536.0); \
-		lvol = MULSC( lvolr >> 8, lvolm ); \
+		lvolm = (int)(volume_left->mix * 16777216.0); \
+		lvol = MULSCV( lvolr, lvolm ); \
 		if ( lvolr == lvolt ) volume_left = NULL; \
 	} else { \
 		lvol = 0; \
@@ -120,8 +120,8 @@
 		rvolr = (int)(volume_right->volume * 16777216.0); \
 		rvold = (int)(volume_right->delta * 16777216.0); \
 		rvolt = (int)(volume_right->target * 16777216.0); \
-		rvolm = (int)(volume_right->mix * 65536.0); \
-		rvol = MULSC( rvolr >> 8, rvolm ); \
+		rvolm = (int)(volume_right->mix * 16777216.0); \
+		rvol = MULSCV( rvolr, rvolm ); \
 		if ( rvolr == rvolt ) volume_right = NULL; \
 	} else { \
 		rvol = 0; \
--- a/dumb/src/helpers/resample.c
+++ b/dumb/src/helpers/resample.c
@@ -82,6 +82,7 @@
 
 //#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16))
 //#define MULSC(a, b) ((a) * ((b) >> 2) >> 14)
+#define MULSCV(a, b) ((int)((LONG_LONG)(a) * (b) >> 32))
 #define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
 #define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32))
 
--- a/dumb/src/helpers/resample.inc
+++ b/dumb/src/helpers/resample.inc
@@ -90,9 +90,9 @@
 			(vol##d > 0 && vol##r >= vol##t)) {                    \
 			pvol->volume = pvol->target;                           \
 			pvol = NULL;                                           \
-			vol = MULSC( vol##t >> 8, vol##m );                    \
+			vol = MULSCV( vol##t, vol##m );                        \
 		} else {                                                   \
-			vol = MULSC( vol##r >> 8, vol##m );                    \
+			vol = MULSCV( vol##r, vol##m );                        \
 		}                                                          \
 	}                                                              \
 }
@@ -113,8 +113,8 @@
 		volr = (int)(volume->volume * 16777216.0); \
 		vold = (int)(volume->delta * 16777216.0); \
 		volt = (int)(volume->target * 16777216.0); \
-		volm = (int)(volume->mix * 65536.0); \
-		vol = MULSC( volr >> 8, volm ); \
+		volm = (int)(volume->mix * 16777216.0); \
+		vol = MULSCV( volr, volm ); \
 		if ( volr == volt ) volume = NULL; \
 	} else { \
 		vol = 0; \
@@ -184,8 +184,8 @@
 		lvolr = (int)(volume_left->volume * 16777216.0); \
 		lvold = (int)(volume_left->delta * 16777216.0); \
 		lvolt = (int)(volume_left->target * 16777216.0); \
-		lvolm = (int)(volume_left->mix * 65536.0); \
-		lvol = MULSC( lvolr >> 8, lvolm ); \
+		lvolm = (int)(volume_left->mix * 16777216.0); \
+		lvol = MULSCV( lvolr, lvolm ); \
 		if ( lvolr == lvolt ) volume_left = NULL; \
 	} else { \
 		lvol = 0; \
@@ -195,8 +195,8 @@
 		rvolr = (int)(volume_right->volume * 16777216.0); \
 		rvold = (int)(volume_right->delta * 16777216.0); \
 		rvolt = (int)(volume_right->target * 16777216.0); \
-		rvolm = (int)(volume_right->mix * 65536.0); \
-		rvol = MULSC( rvolr >> 8, rvolm ); \
+		rvolm = (int)(volume_right->mix * 16777216.0); \
+		rvol = MULSCV( rvolr, rvolm ); \
 		if ( rvolr == rvolt ) volume_right = NULL; \
 	} else { \
 		rvol = 0; \
--- a/dumb/src/it/itread.c
+++ b/dumb/src/it/itread.c
@@ -1188,6 +1188,9 @@
 		switch (component[n].type) {
 
 			case IT_COMPONENT_SONG_MESSAGE:
+				if ( n < n_components ) {
+					message_length = min( message_length, component[n+1].offset - component[n].offset );
+				}
 				sigdata->song_message = malloc(message_length + 1);
 				if (sigdata->song_message) {
 					if (dumbfile_getnc(sigdata->song_message, message_length, f) < message_length) {
--- a/dumb/src/it/itrender.c
+++ b/dumb/src/it/itrender.c
@@ -202,7 +202,7 @@
 	dst->lastS = src->lastS;
 	dst->pat_loop_row = src->pat_loop_row;
 	dst->pat_loop_count = src->pat_loop_count;
-	//dst->pat_loop_end_row = src->pat_loop_end_row;
+	dst->pat_loop_end_row = src->pat_loop_end_row;
 	dst->lastW = src->lastW;
 
 	dst->xm_lastE1 = src->xm_lastE1;
@@ -486,6 +486,7 @@
 
 
 
+#if 0
 #define LOG10 2.30258509299
 
 /* IMPORTANT: This function expects one extra sample in 'src' so it can apply
@@ -493,6 +494,7 @@
  * output starting at dst[pos]. The pos parameter is required for getting
  * click removal right.
  */
+
 static void it_filter(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance)
 {
 	sample_t currsample = state->currsample;
@@ -595,8 +597,8 @@
 	state->currsample = currsample;
 	state->prevsample = prevsample;
 }
-
 #undef LOG10
+#endif
 
 
 
@@ -1171,56 +1173,6 @@
 						effectvalue = channel->lastS;
 					channel->lastS = effectvalue;
 					switch (effectvalue >> 4) {
-#if 1
-						case IT_S7:
-							{
-								if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
-								{
-									int i;
-									switch (effectvalue & 15)
-									{
-									case 0: /* cut background notes */
-										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
-										{
-											IT_PLAYING * playing = sigrenderer->playing[i];
-											if (playing && channel == playing->channel)
-											{
-#ifdef RAMP_DOWN
-												playing->declick_stage = 2;
-#else
-												free(playing);
-												sigrenderer->playing[i] = NULL;
-#endif
-												if (channel->playing == playing) channel->playing = NULL;
-											}
-										}
-										break;
-									case 1: /* release background notes */
-										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
-										{
-											IT_PLAYING * playing = sigrenderer->playing[i];
-											if (playing && channel == playing->channel && !(playing->flags & IT_PLAYING_SUSTAINOFF))
-											{
-												it_note_off(playing);
-											}
-										}
-										break;
-									case 2: /* fade background notes */
-										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
-										{
-											IT_PLAYING * playing = sigrenderer->playing[i];
-											if (playing && channel == playing->channel)
-											{
-												//playing->flags &= IT_PLAYING_SUSTAINOFF;
-												playing->flags |= IT_PLAYING_FADING;
-											}
-										}
-										break;
-									}
-								}
-							}
-							break;
-#endif
 						case IT_S_PATTERN_LOOP:
 							{
 								unsigned char v = effectvalue & 15;
@@ -1254,7 +1206,19 @@
 #endif
 										channel->pat_loop_count = v;
 										sigrenderer->breakrow = channel->pat_loop_row;
-										if (!(sigrenderer->sigdata->flags & IT_WAS_AN_XM) || ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) ) {
+										if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+											/* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */
+											if ((sigrenderer->processrow|0xC00) < 0xFFFE) {
+												/* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */
+												if (sigrenderer->processrow < channel->pat_loop_end_row)
+													sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */
+												else
+													sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */
+												channel->pat_loop_end_row = sigrenderer->processrow;
+												sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */
+											}
+										} else {
+											/* IT files do this regardless of other flow control effects seen here. */
 											sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */
 											sigrenderer->processrow = 0xFFFE;
 										}
@@ -1268,16 +1232,38 @@
 										}
 #endif
 										sigrenderer->breakrow = channel->pat_loop_row;
-										if (!(sigrenderer->sigdata->flags & IT_WAS_AN_XM) || ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) ) {
+										if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+											/* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */
+											if ((sigrenderer->processrow|0xC00) < 0xFFFE) {
+												/* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */
+												if (sigrenderer->processrow < channel->pat_loop_end_row)
+													sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */
+												else
+													sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */
+												channel->pat_loop_end_row = sigrenderer->processrow;
+												sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */
+											}
+										} else {
+											/* IT files do this regardless of other flow control effects seen here. */
 											sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */
 											sigrenderer->processrow = 0xFFFE;
 										}
 										return 1;
 									} else if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
-										sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */
-									} else {
+										channel->pat_loop_end_row = 0;
+										// TODO
+										/* Findings:
+										- If a pattern loop completes successfully, and then the pattern terminates, then the next pattern will start on the row corresponding to the E60.
+										- If a pattern loop doesn't do any loops, and then the pattern terminates, then the next pattern will start on the first row.
+										- If a break appears to the left of the pattern loop, it jumps into the relevant position in the next pattern, and that's it.
+										- If a break appears to the right of the pattern loop, it jumps to the start of the next pattern, and that's it.
+										- If we jump, then effect a loop using an old E60, and then the pattern ends, the next pattern starts on the row corresponding to the E60.
+										- Theory: breakrow is not cleared when it's a pattern loop effect!
+										*/
+										//if (sigrenderer->processrow < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :(
+										//	sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */
+									} else
 										channel->pat_loop_row = sigrenderer->processrow + 1;
-									}
 #ifdef BIT_ARRAY_BULLSHIT
 									/*channel->played_patjump_order |= 0x8000;*/
 									if (channel->played_patjump_order == sigrenderer->order) {
@@ -2359,8 +2345,46 @@
 							{
 								if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
 								{
+									int i;
 									switch (effectvalue & 15)
 									{
+									case 0: /* cut background notes */
+										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+										{
+											IT_PLAYING * playing = sigrenderer->playing[i];
+											if (playing && channel == playing->channel)
+											{
+#ifdef RAMP_DOWN
+												playing->declick_stage = 2;
+#else
+												free(playing);
+												sigrenderer->playing[i] = NULL;
+#endif
+												if (channel->playing == playing) channel->playing = NULL;
+											}
+										}
+										break;
+									case 1: /* release background notes */
+										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+										{
+											IT_PLAYING * playing = sigrenderer->playing[i];
+											if (playing && channel == playing->channel && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+											{
+												it_note_off(playing);
+											}
+										}
+										break;
+									case 2: /* fade background notes */
+										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+										{
+											IT_PLAYING * playing = sigrenderer->playing[i];
+											if (playing && channel == playing->channel)
+											{
+												//playing->flags &= IT_PLAYING_SUSTAINOFF;
+												playing->flags |= IT_PLAYING_FADING;
+											}
+										}
+										break;
 									case 3:
 										channel->new_note_action = NNA_NOTE_CUT;
 										break;
@@ -3765,8 +3789,12 @@
 				int n;
 				int processorder = sigrenderer->processorder;
 
-				sigrenderer->processrow = sigrenderer->breakrow;
-				sigrenderer->breakrow = 0;
+				if ((sigrenderer->processrow|0xC00) == 0xFFFE + 1) { /* It was incremented above! */
+					sigrenderer->processrow = sigrenderer->breakrow;
+					sigrenderer->breakrow = 0;
+					for (n = 0; n < DUMB_IT_N_CHANNELS; n++) sigrenderer->channel[n].pat_loop_end_row = 0;
+				} else
+					sigrenderer->processrow = sigrenderer->breakrow;
 
 				if (sigrenderer->processorder == 0xFFFF)
 					sigrenderer->processorder = sigrenderer->order - 1;
@@ -4197,8 +4225,193 @@
 }
 
 #ifdef END_RAMPING
+#if 1
+static long render_playing_part(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, DUMB_VOLUME_RAMP_INFO * lvol, DUMB_VOLUME_RAMP_INFO * rvol, int bits, float delta, long pos, long size, sample_t **samples, int store_end_sample, int cr_record_which)
+{
+	long size_rendered = 0;
+
+	if (sigrenderer->n_channels == 2) {
+		if (playing->sample->flags & IT_SAMPLE_STEREO) {
+			if ((cr_record_which & 1) && sigrenderer->click_remover) {
+				sample_t click[2];
+				dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click);
+				dumb_record_click(sigrenderer->click_remover[0], pos, click[0]);
+				dumb_record_click(sigrenderer->click_remover[1], pos, click[1]);
+			}
+			size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta);
+			if (store_end_sample) {
+				sample_t click[2];
+				dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click);
+				samples[0][(pos + size_rendered) * 2] = click[0];
+				samples[0][(pos + size_rendered) * 2 + 1] = click[1];
+			}
+			if ((cr_record_which & 2) && sigrenderer->click_remover) {
+				sample_t click[2];
+				dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click);
+				dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]);
+				dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]);
+			}
+		} else {
+			if ((cr_record_which & 1) && sigrenderer->click_remover) {
+				sample_t click[2];
+				dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click);
+				dumb_record_click(sigrenderer->click_remover[0], pos, click[0]);
+				dumb_record_click(sigrenderer->click_remover[1], pos, click[1]);
+			}
+			size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta);
+			if (store_end_sample) {
+				sample_t click[2];
+				dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click);
+				samples[0][(pos + size_rendered) * 2] = click[0];
+				samples[0][(pos + size_rendered) * 2 + 1] = click[1];
+			}
+			if ((cr_record_which & 2) && sigrenderer->click_remover) {
+				sample_t click[2];
+				dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click);
+				dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]);
+				dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]);
+			}
+		}
+	} else {
+		if (playing->sample->flags & IT_SAMPLE_STEREO) {
+			if ((cr_record_which & 1) && sigrenderer->click_remover) {
+				sample_t click;
+				dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click);
+				dumb_record_click(sigrenderer->click_remover[0], pos, click);
+			}
+			size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, lvol, rvol, delta);
+			if (store_end_sample)
+				dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &samples[0][pos + size_rendered]);
+			if ((cr_record_which & 2) && sigrenderer->click_remover) {
+				sample_t click;
+				dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click);
+				dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click);
+			}
+		} else {
+			if ((cr_record_which & 1) && sigrenderer->click_remover) {
+				sample_t click;
+				dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &click);
+				dumb_record_click(sigrenderer->click_remover[0], pos, click);
+			}
+			size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, lvol, delta);
+			if (store_end_sample)
+				dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &samples[0][pos + size_rendered]);
+			if ((cr_record_which & 2) && sigrenderer->click_remover) {
+				sample_t click;
+				dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &click);
+				dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click);
+			}
+		}
+	}
+	return size_rendered;
+}
+
 static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style)
 {
+	int bits;
+
+	long size_rendered;
+
+	DUMB_VOLUME_RAMP_INFO lvol, rvol;
+
+	if (!size) return 0;
+
+	if (playing->flags & IT_PLAYING_DEAD)
+		return 0;
+
+	if (*left_to_mix <= 0)
+		volume = 0;
+
+	{
+		int quality = sigrenderer->resampling_quality;
+		if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+			quality = playing->sample->max_resampling_quality;
+		playing->resampler.quality = quality;
+	}
+
+	bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8;
+	size_rendered = size;
+
+	if (volume == 0) {
+		if (playing->declick_stage < 2) {
+			if (playing->sample->flags & IT_SAMPLE_STEREO)
+				size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta);
+			else
+				size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta);
+		} else {
+			playing->declick_stage = 3;
+		}
+	} else {
+		lvol.volume = playing->ramp_volume [0];
+		rvol.volume = playing->ramp_volume [1];
+		lvol.delta  = playing->ramp_delta [0] * main_delta;
+		rvol.delta  = playing->ramp_delta [1] * main_delta;
+		lvol.target = playing->float_volume [0];
+		rvol.target = playing->float_volume [1];
+		rvol.mix = lvol.mix = volume;
+		if (ramp_style) {
+			if (playing->declick_stage == 1) {
+				size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3);
+			} else if (playing->declick_stage == 0 || playing->declick_stage == 2) {
+				float declick_count = ((ramp_style == 2) ? 327.68 : 49.152) / main_delta; /* 5ms / 0.75ms */
+				float declick_remain = playing->declick_volume * declick_count;
+				float declick_dir = -1;
+				float declick_target;
+				int remain;
+				DUMB_VOLUME_RAMP_INFO declick_lvol, declick_rvol;
+				if (playing->declick_stage == 0) {
+					declick_remain = declick_count - declick_remain;
+					declick_dir = 1;
+				}
+				if (size < declick_remain) declick_remain = size;
+				remain = declick_remain;
+				declick_target = playing->declick_volume + declick_dir / declick_count * declick_remain;
+				declick_lvol.volume = lvol.volume * playing->declick_volume;
+				declick_rvol.volume = rvol.volume * playing->declick_volume;
+				lvol.volume += lvol.delta * declick_remain;
+				rvol.volume += rvol.delta * declick_remain;
+				declick_lvol.target = lvol.volume * declick_target;
+				declick_rvol.target = rvol.volume * declick_target;
+				declick_lvol.delta = (declick_lvol.target - declick_lvol.volume) / declick_remain;
+				declick_rvol.delta = (declick_rvol.target - declick_rvol.volume) / declick_remain;
+				declick_lvol.mix = declick_rvol.mix = volume;
+				if (remain < size) {
+					size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, 0, 1);
+					if (size_rendered == remain) {
+						if (playing->declick_stage == 0) {
+							size_rendered += render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos + remain, size - remain, samples, store_end_sample, 2);
+						} else {
+							size_rendered = size;
+						}
+					}
+					playing->declick_stage++;
+					playing->declick_volume = 1;
+				} else {
+					size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, store_end_sample, 3);
+					playing->declick_volume = declick_target;
+				}
+			} else /*if (playing->declick_stage == 3)*/ {
+				(*left_to_mix)++;
+			}
+		} else if (playing->declick_stage < 2) {
+			size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3);
+		} else {
+			playing->declick_stage = 3;
+			(*left_to_mix)++;
+		}
+		playing->ramp_volume [0] = lvol.volume;
+		playing->ramp_volume [1] = rvol.volume;
+		(*left_to_mix)--;
+	}
+
+	if (playing->resampler.dir == 0)
+		playing->flags |= IT_PLAYING_DEAD;
+
+	return size_rendered;
+}
+#else
+static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style)
+{
 	long rv, trv;
 	int l_t_m_temp, cr_which;
 	if (!size) return 0;
@@ -4286,6 +4499,7 @@
 
 	return rv + render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, cr_which);
 }
+#endif
 #else
 #define render_playing_ramp(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, ramp_style) render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, 3)
 #endif
@@ -4577,6 +4791,7 @@
 		channel->lastS = 0;
 		channel->pat_loop_row = 0;
 		channel->pat_loop_count = 0;
+		channel->pat_loop_end_row = 0;
 		channel->lastW = 0;
 		channel->xm_lastE1 = 0;
 		channel->xm_lastE2 = 0;
--- a/dumb/src/it/reads3m.c
+++ b/dumb/src/it/reads3m.c
@@ -51,12 +51,18 @@
 
 	type = dumbfile_getc(f);
 
+	dumbfile_getnc(sample->filename, 12, f);
+	sample->filename[12] = 0;
+
 	if (type > 1) {
 		/** WARNING: no adlib support */
+		dumbfile_skip(f, 3 + 12 + 1 + 1 + 2 + 2 + 2 + 12);
+		dumbfile_getnc(sample->name, 28, f);
+		sample->name[28] = 0;
+		dumbfile_skip(f, 4);
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+		return dumbfile_error(f);
 	}
-
-	dumbfile_getnc(sample->filename, 12, f);
-	sample->filename[12] = 0;
 
 	*offset = dumbfile_getc(f) << 20;
 	*offset += dumbfile_igetw(f) << 4;
--- a/dumb/src/it/readxm.c
+++ b/dumb/src/it/readxm.c
@@ -660,6 +660,7 @@
 	DUMB_IT_SIGDATA *sigdata;
 	char id_text[18];
 
+	int header_size;
 	int flags;
 	int n_channels;
 	int total_samples;
@@ -712,7 +713,8 @@
 	*/
 
 	/* header size */
-	if (dumbfile_igetl(f) != 0x0114) {
+	header_size = dumbfile_igetl(f);
+	if (header_size < (4 + 2*8 + 1) || header_size > 0x114) {
 		TRACE("XM error: unexpected header size\n");
 		free(sigdata);
 		return NULL;
@@ -740,7 +742,8 @@
 
 	/* sanity checks */
 	// XXX
-	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > 256 || sigdata->n_patterns > 256 || sigdata->n_instruments > 256 || n_channels > DUMB_IT_N_CHANNELS) {
+	i = header_size - 4 - 2 * 8; /* Maximum number of orders expected */
+	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > i || sigdata->n_patterns > 256 || sigdata->n_instruments > 256 || n_channels > DUMB_IT_N_CHANNELS) {
 		_dumb_it_unload_sigdata(sigdata);
 		return NULL;
 	}
@@ -755,7 +758,7 @@
 		return NULL;
 	}
 	dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
-	dumbfile_skip(f, 256 - sigdata->n_orders);
+	dumbfile_skip(f, i - sigdata->n_orders);
 
 	if (dumbfile_error(f)) {
 		_dumb_it_unload_sigdata(sigdata);
--- a/dumb/vc6/dumb/dumb.vcproj
+++ b/dumb/vc6/dumb/dumb.vcproj
@@ -174,6 +174,7 @@
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="2"
+			WholeProgramOptimization="1"
 			>
 			<Tool
 				Name="VCPreBuildEventTool"