shithub: ft2-clone

Download patch

ref: ee4c8983a2b22d2b0ca25db8dec28c3fa7565e93
parent: a58a7763f01ed81b45b00b323cbffc3050583971
author: Olav Sørensen <olav.sorensen@live.no>
date: Sun Nov 8 12:02:09 EST 2020

Pushed v1.39 code

- Fixed yet another issue where junk could be drawn after the loop end point in the sample data's waveform in the sample editor (if zoomed out)
- Fixed an issue with loading XMs with more than 128 instruments (OpenMPT etc). This only worked if the extra instruments were empty. Now it will properly load the extra instruments and discard them after the loading was complete (since we only support up to 128 instruments)
- Small code cleanup

--- a/src/ft2_audio.c
+++ b/src/ft2_audio.c
@@ -545,7 +545,7 @@
 	}
 }
 
-static void doChannelMixing(int32_t samplesToMix)
+static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix)
 {
 	voice_t *v = voice; // normal voices
 	voice_t *r = &voice[MAX_VOICES]; // volume ramp fadeout-voices
@@ -572,29 +572,17 @@
 				centerMixFlag = v->dVolL == v->dVolR;
 			}
 
-			mixFuncTab[(centerMixFlag * 36) + (volRampFlag * 18) + v->mixFuncOffset](v, samplesToMix);
+			mixFuncTab[(centerMixFlag * 36) + (volRampFlag * 18) + v->mixFuncOffset](v, bufferPosition, samplesToMix);
 		}
 
 		if (r->active) // volume ramp fadeout-voice
 		{
 			const bool centerMixFlag = (r->dDestVolL == r->dDestVolR) && (r->dVolDeltaL == r->dVolDeltaR);
-			mixFuncTab[(centerMixFlag * 36) + 18 + r->mixFuncOffset](r, samplesToMix);
+			mixFuncTab[(centerMixFlag * 36) + 18 + r->mixFuncOffset](r, bufferPosition, samplesToMix);
 		}
 	}
 }
 
-static void mixAudio(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels)
-{
-	assert(sampleBlockLength <= MAX_WAV_RENDER_SAMPLES_PER_TICK);
-	memset(audio.dMixBufferL, 0, sampleBlockLength * sizeof (double));
-	memset(audio.dMixBufferR, 0, sampleBlockLength * sizeof (double));
-
-	doChannelMixing(sampleBlockLength);
-
-	// normalize mix buffer and send to audio stream
-	sendAudSamplesFunc(stream, sampleBlockLength, numAudioChannels);
-}
-
 // used for song-to-WAV renderer
 void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth)
 {
@@ -602,7 +590,7 @@
 	memset(audio.dMixBufferL, 0, samplesToMix * sizeof (double));
 	memset(audio.dMixBufferR, 0, samplesToMix * sizeof (double));
 
-	doChannelMixing(samplesToMix);
+	doChannelMixing(0, samplesToMix);
 
 	// normalize mix buffer and send to audio stream
 	if (bitDepth == 16)
@@ -932,10 +920,17 @@
 	if (editor.wavIsRendering)
 		return;
 
-	int32_t samplesLeft = len / pmpCountDiv;
-	if (samplesLeft <= 0)
+	len /= pmpCountDiv; // bytes -> samples
+	if (len <= 0)
 		return;
 
+	assert(len <= MAX_WAV_RENDER_SAMPLES_PER_TICK);
+	memset(audio.dMixBufferL, 0, len * sizeof (double));
+	memset(audio.dMixBufferR, 0, len * sizeof (double));
+
+	int32_t bufferPosition = 0;
+
+	int32_t samplesLeft = len;
 	while (samplesLeft > 0)
 	{
 		if (audio.dTickSampleCounter <= 0.0)
@@ -962,12 +957,15 @@
 		if (samplesToMix > remainingTick)
 			samplesToMix = remainingTick;
 
-		mixAudio(stream, samplesToMix, pmpChannels);
-		stream += samplesToMix * pmpCountDiv;
+		doChannelMixing(bufferPosition, samplesToMix);
 
+		bufferPosition += samplesToMix;
 		samplesLeft -= samplesToMix;
 		audio.dTickSampleCounter -= samplesToMix;
 	}
+
+	// normalize mix buffer and send to audio stream
+	sendAudSamplesFunc(stream, len, pmpChannels);
 
 	(void)userdata; // make compiler not complain
 }
--- a/src/ft2_header.h
+++ b/src/ft2_header.h
@@ -12,7 +12,7 @@
 #endif
 #include "ft2_replayer.h"
 
-#define PROG_VER_STR "1.38"
+#define PROG_VER_STR "1.39"
 
 // do NOT change these! It will only mess things up...
 
--- a/src/ft2_module_loader.c
+++ b/src/ft2_module_loader.c
@@ -138,7 +138,7 @@
 static int16_t pattLensTmp[MAX_PATTERNS];
 static uint32_t extraSampleLengths[32-MAX_SMP_PER_INST]; // ModPlug Tracker & OpenMPT supports up to 32 samples per instrument in .XM
 static tonTyp *pattTmp[MAX_PATTERNS];
-static instrTyp *instrTmp[1+MAX_INST];
+static instrTyp *instrTmp[1+256];
 static songTyp songTmp;
 
 static void setupLoadedModule(void);
@@ -1724,6 +1724,12 @@
 		goto xmLoadError;
 	}
 
+	if (h.antInstrs > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them
+	{
+		showMsg(0, "System message", "Error loading XM: This file is corrupt.");
+		goto xmLoadError;
+	}
+
 	fseek(f, 60 + h.headerSize, SEEK_SET);
 	if (filelength != 336 && feof(f)) // 336 in length at this point = empty XM
 	{
@@ -1806,11 +1812,27 @@
 
 		for (i = 1; i <= h.antInstrs; i++)
 		{
-			if (!loadInstrHeader(f, i, externalThreadFlag)) goto xmLoadError;
-			if (!loadInstrSample(f, i, externalThreadFlag)) goto xmLoadError;
+			if (!loadInstrHeader(f, i, externalThreadFlag))
+				goto xmLoadError;
+
+			if (!loadInstrSample(f, i, externalThreadFlag))
+				goto xmLoadError;
 		}
 	}
 
+	// if we temporarily loaded more than 128 instruments, clear the extra allocated memory
+	if (h.antInstrs > MAX_INST)
+	{
+		for (i = MAX_INST+1; i <= h.antInstrs; i++)
+		{
+			if (instrTmp[i] != NULL)
+			{
+				free(instrTmp[i]);
+				instrTmp[i] = NULL;
+			}
+		}
+	}
+
 	fclose(f);
 
 	/* We support loading XMs with up to 32 samples per instrument (ModPlug/OpenMPT),
@@ -1937,8 +1959,8 @@
 		}
 	}
 
-	// free all samples
-	for (i = 1; i <= MAX_INST; i++)
+	// free all instruments and samples
+	for (i = 1; i <= 256; i++) // if >128 instruments, we fake-load up to 128 extra (and discard them later)
 	{
 		if (instrTmp[i] != NULL)
 		{
@@ -1996,7 +2018,7 @@
 		return false;
 	}
 
-	if (i <= MAX_INST)
+	if (i <= MAX_INST) // copy over instrument names
 	{
 		// trim off spaces at end of name
 		for (k = 21; k >= 0; k--)
@@ -2013,73 +2035,70 @@
 
 	if (ih.antSamp > 0)
 	{
-		if (i <= MAX_INST)
+		if (!allocateTmpInstr(i))
 		{
-			if (!allocateTmpInstr(i))
-			{
-				showMsg(0, "System message", "Not enough memory!");
-				return false;
-			}
+			showMsg(0, "System message", "Not enough memory!");
+			return false;
+		}
 
-			// copy instrument header elements to our instrument struct
+		// copy instrument header elements to our instrument struct
 
-			ins = instrTmp[i];
-			memcpy(ins->ta, ih.ta, 96);
-			memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
-			memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
-			ins->envVPAnt = ih.envVPAnt;
-			ins->envPPAnt = ih.envPPAnt;
-			ins->envVSust = ih.envVSust;
-			ins->envVRepS = ih.envVRepS;
-			ins->envVRepE = ih.envVRepE;
-			ins->envPSust = ih.envPSust;
-			ins->envPRepS = ih.envPRepS;
-			ins->envPRepE = ih.envPRepE;
-			ins->envVTyp = ih.envVTyp;
-			ins->envPTyp = ih.envPTyp;
-			ins->vibTyp = ih.vibTyp;
-			ins->vibSweep = ih.vibSweep;
-			ins->vibDepth = ih.vibDepth;
-			ins->vibRate = ih.vibRate;
-			ins->fadeOut = ih.fadeOut;
-			ins->midiOn = (ih.midiOn == 1) ? true : false;
-			ins->midiChannel = ih.midiChannel;
-			ins->midiProgram = ih.midiProgram;
-			ins->midiBend = ih.midiBend;
-			ins->mute = (ih.mute == 1) ? true : false;
-			ins->antSamp = ih.antSamp; // used in loadInstrSample()
+		ins = instrTmp[i];
+		memcpy(ins->ta, ih.ta, 96);
+		memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
+		memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
+		ins->envVPAnt = ih.envVPAnt;
+		ins->envPPAnt = ih.envPPAnt;
+		ins->envVSust = ih.envVSust;
+		ins->envVRepS = ih.envVRepS;
+		ins->envVRepE = ih.envVRepE;
+		ins->envPSust = ih.envPSust;
+		ins->envPRepS = ih.envPRepS;
+		ins->envPRepE = ih.envPRepE;
+		ins->envVTyp = ih.envVTyp;
+		ins->envPTyp = ih.envPTyp;
+		ins->vibTyp = ih.vibTyp;
+		ins->vibSweep = ih.vibSweep;
+		ins->vibDepth = ih.vibDepth;
+		ins->vibRate = ih.vibRate;
+		ins->fadeOut = ih.fadeOut;
+		ins->midiOn = (ih.midiOn == 1) ? true : false;
+		ins->midiChannel = ih.midiChannel;
+		ins->midiProgram = ih.midiProgram;
+		ins->midiBend = ih.midiBend;
+		ins->mute = (ih.mute == 1) ? true : false;
+		ins->antSamp = ih.antSamp; // used in loadInstrSample()
 
-			// sanitize stuff for broken/unsupported instruments
-			ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
-			ins->midiBend = CLAMP(ins->midiBend, 0, 36);
+		// sanitize stuff for broken/unsupported instruments
+		ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
+		ins->midiBend = CLAMP(ins->midiBend, 0, 36);
 
-			if (ins->midiChannel > 15) ins->midiChannel = 15;
-			if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
-			if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
-			if (ins->vibTyp > 3) ins->vibTyp = 0;
+		if (ins->midiChannel > 15) ins->midiChannel = 15;
+		if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
+		if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
+		if (ins->vibTyp > 3) ins->vibTyp = 0;
 
-			for (j = 0; j < 96; j++)
-			{
-				if (ins->ta[j] >= MAX_SMP_PER_INST)
-					ins->ta[j] = MAX_SMP_PER_INST-1;
-			}
+		for (j = 0; j < 96; j++)
+		{
+			if (ins->ta[j] >= MAX_SMP_PER_INST)
+				ins->ta[j] = MAX_SMP_PER_INST-1;
+		}
 
-			if (ins->envVPAnt > 12) ins->envVPAnt = 12;
-			if (ins->envVRepS > 11) ins->envVRepS = 11;
-			if (ins->envVRepE > 11) ins->envVRepE = 11;
-			if (ins->envVSust > 11) ins->envVSust = 11;
-			if (ins->envPPAnt > 12) ins->envPPAnt = 12;
-			if (ins->envPRepS > 11) ins->envPRepS = 11;
-			if (ins->envPRepE > 11) ins->envPRepE = 11;
-			if (ins->envPSust > 11) ins->envPSust = 11;
+		if (ins->envVPAnt > 12) ins->envVPAnt = 12;
+		if (ins->envVRepS > 11) ins->envVRepS = 11;
+		if (ins->envVRepE > 11) ins->envVRepE = 11;
+		if (ins->envVSust > 11) ins->envVSust = 11;
+		if (ins->envPPAnt > 12) ins->envPPAnt = 12;
+		if (ins->envPRepS > 11) ins->envPRepS = 11;
+		if (ins->envPRepE > 11) ins->envPRepE = 11;
+		if (ins->envPSust > 11) ins->envPSust = 11;
 
-			for (j = 0; j < 12; j++)
-			{
-				if ((uint16_t)ins->envVP[j][0] > 32767) ins->envVP[j][0] = 32767;
-				if ((uint16_t)ins->envPP[j][0] > 32767) ins->envPP[j][0] = 32767;
-				if ((uint16_t)ins->envVP[j][1] > 64) ins->envVP[j][1] = 64;
-				if ((uint16_t)ins->envPP[j][1] > 63) ins->envPP[j][1] = 63;
-			}
+		for (j = 0; j < 12; j++)
+		{
+			if ((uint16_t)ins->envVP[j][0] > 32767) ins->envVP[j][0] = 32767;
+			if ((uint16_t)ins->envPP[j][0] > 32767) ins->envPP[j][0] = 32767;
+			if ((uint16_t)ins->envVP[j][1] > 64) ins->envVP[j][1] = 64;
+			if ((uint16_t)ins->envPP[j][1] > 63) ins->envPP[j][1] = 63;
 		}
 
 		int32_t sampleHeadersToRead = ih.antSamp;
@@ -2103,43 +2122,40 @@
 			}
 		}
 
-		if (i <= MAX_INST)
+		for (j = 0; j < sampleHeadersToRead; j++)
 		{
-			for (j = 0; j < sampleHeadersToRead; j++)
-			{
-				s = &instrTmp[i]->samp[j];
-				src = &ih.samp[j];
+			s = &instrTmp[i]->samp[j];
+			src = &ih.samp[j];
 
-				// copy sample header elements to our sample struct
+			// copy sample header elements to our sample struct
 
-				s->len = src->len;
-				s->repS = src->repS;
-				s->repL = src->repL;
-				s->vol = src->vol;
-				s->fine = src->fine;
-				s->typ = src->typ;
-				s->pan = src->pan;
-				s->relTon = src->relTon;
-				memcpy(s->name, src->name, 22);
-				s->name[22] = '\0';
+			s->len = src->len;
+			s->repS = src->repS;
+			s->repL = src->repL;
+			s->vol = src->vol;
+			s->fine = src->fine;
+			s->typ = src->typ;
+			s->pan = src->pan;
+			s->relTon = src->relTon;
+			memcpy(s->name, src->name, 22);
+			s->name[22] = '\0';
 
-				// dst->pek is set up later
+			// dst->pek is set up later
 
-				// trim off spaces at end of name
-				for (k = 21; k >= 0; k--)
-				{
-					if (s->name[k] == ' ' || s->name[k] == 0x1A)
-						s->name[k] = '\0';
-					else
-						break;
-				}
+			// trim off spaces at end of name
+			for (k = 21; k >= 0; k--)
+			{
+				if (s->name[k] == ' ' || s->name[k] == 0x1A)
+					s->name[k] = '\0';
+				else
+					break;
+			}
 
-				// sanitize stuff broken/unsupported samples
-				if (s->vol > 64)
-					s->vol = 64;
+			// sanitize stuff broken/unsupported samples
+			if (s->vol > 64)
+				s->vol = 64;
 
-				s->relTon = CLAMP(s->relTon, -48, 71);
-			}
+			s->relTon = CLAMP(s->relTon, -48, 71);
 		}
 	}
 
@@ -2165,90 +2181,102 @@
 
 	showMsg = externalThreadFlag ? okBoxThreadSafe : okBox;
 
-	if (i > MAX_INST || instrTmp[i] == NULL)
-		return true; // yes, let's just pretend they got loaded
+	if (instrTmp[i] == NULL)
+		return true; // empty instrument, let's just pretend it got loaded successfully
 
 	k = instrTmp[i]->antSamp;
 	if (k > MAX_SMP_PER_INST)
 		k = MAX_SMP_PER_INST;
 
-	for (j = 0; j < k; j++)
+	if (i > MAX_INST) // insNum > 128, just skip sample data
 	{
-		s = &instrTmp[i]->samp[j];
-
-		// if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior)
-		if ((s->typ & 3) == 3)
-			s->typ &= 0xFE;
-
-		l = s->len;
-		if (l <= 0)
+		for (j = 0; j < k; j++)
 		{
-			s->pek = NULL;
-			s->len = 0;
-			s->repL = 0;
-			s->repS = 0;
-
-			if (s->typ & 32)
-				s->typ &= ~32; // remove stereo flag
+			s = &instrTmp[i]->samp[j];
+			if (s->len > 0)
+				fseek(f, s->len, SEEK_CUR);
 		}
-		else
+	}
+	else
+	{
+		for (j = 0; j < k; j++)
 		{
-			bytesToSkip = 0;
-			if (l > MAX_SAMPLE_LEN)
-			{
-				bytesToSkip = l - MAX_SAMPLE_LEN;
-				l = MAX_SAMPLE_LEN;
-			}
+			s = &instrTmp[i]->samp[j];
 
-			s->pek = NULL;
-			s->origPek = (int8_t *)malloc(l + LOOP_FIX_LEN);
-			if (s->origPek == NULL)
-			{
-				showMsg(0, "System message", "Not enough memory!");
-				return false;
-			}
+			// if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior)
+			if ((s->typ & 3) == 3)
+				s->typ &= 0xFE;
 
-			s->pek = s->origPek + SMP_DAT_OFFSET;
-
-			const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f);
-			if (bytesRead < l)
+			l = s->len;
+			if (l <= 0)
 			{
-				const int32_t bytesToClear = l - bytesRead;
-				memset(&s->pek[bytesRead], 0, bytesToClear);
+				s->pek = NULL;
+				s->len = 0;
+				s->repL = 0;
+				s->repS = 0;
+
+				if (s->typ & 32)
+					s->typ &= ~32; // remove stereo flag
 			}
+			else
+			{
+				bytesToSkip = 0;
+				if (l > MAX_SAMPLE_LEN)
+				{
+					bytesToSkip = l - MAX_SAMPLE_LEN;
+					l = MAX_SAMPLE_LEN;
+				}
 
-			if (bytesToSkip > 0)
-				fseek(f, bytesToSkip, SEEK_CUR);
+				s->pek = NULL;
+				s->origPek = (int8_t *)malloc(l + LOOP_FIX_LEN);
+				if (s->origPek == NULL)
+				{
+					showMsg(0, "System message", "Not enough memory!");
+					return false;
+				}
 
-			delta2Samp(s->pek, l, s->typ);
+				s->pek = s->origPek + SMP_DAT_OFFSET;
 
-			if (s->typ & 32) // stereo sample - already downmixed to mono in delta2samp()
-			{
-				s->typ &= ~32; // remove stereo flag
+				const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f);
+				if (bytesRead < l)
+				{
+					const int32_t bytesToClear = l - bytesRead;
+					memset(&s->pek[bytesRead], 0, bytesToClear);
+				}
 
-				s->len >>= 1;
-				s->repL >>= 1;
-				s->repS >>= 1;
+				if (bytesToSkip > 0)
+					fseek(f, bytesToSkip, SEEK_CUR);
 
-				newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
-				if (newPtr != NULL)
+				delta2Samp(s->pek, l, s->typ);
+
+				if (s->typ & 32) // stereo sample - already downmixed to mono in delta2samp()
 				{
-					s->origPek = newPtr;
-					s->pek = s->origPek + SMP_DAT_OFFSET;
+					s->typ &= ~32; // remove stereo flag
+
+					s->len >>= 1;
+					s->repL >>= 1;
+					s->repS >>= 1;
+
+					newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN);
+					if (newPtr != NULL)
+					{
+						s->origPek = newPtr;
+						s->pek = s->origPek + SMP_DAT_OFFSET;
+					}
 				}
 			}
-		}
 
-		// NON-FT2 FIX: Align to 2-byte if 16-bit sample
-		if (s->typ & 16)
-		{
-			s->repL &= 0xFFFFFFFE;
-			s->repS &= 0xFFFFFFFE;
-			s->len &= 0xFFFFFFFE;
-		}
+			// NON-FT2 FIX: Align to 2-byte if 16-bit sample
+			if (s->typ & 16)
+			{
+				s->repL &= 0xFFFFFFFE;
+				s->repS &= 0xFFFFFFFE;
+				s->len &= 0xFFFFFFFE;
+			}
 
-		checkSampleRepeat(s);
-		fixSample(s);
+			checkSampleRepeat(s);
+			fixSample(s);
+		}
 	}
 
 	if (instrTmp[i]->antSamp > MAX_SMP_PER_INST)
--- a/src/ft2_replayer.c
+++ b/src/ft2_replayer.c
@@ -2943,7 +2943,7 @@
 	if (ins == NULL)
 		return;
 
-	assert(stmm < MAX_VOICES && inst < MAX_INST && ton <= 97);
+	assert(stmm < MAX_VOICES && inst <= MAX_INST && ton <= 97);
 	ch = &stm[stmm];
 
 	// FT2 bugfix: Don't play tone if certain requirements are not met
@@ -3010,7 +3010,7 @@
 	editor.curPlayInstr = 255;
 	editor.curPlaySmp = 255;
 
-	assert(stmm < MAX_VOICES && inst < MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97);
+	assert(stmm < MAX_VOICES && inst <= MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97);
 	ch = &stm[stmm];
 
 	memcpy(&instr[130]->samp[0], &instr[inst]->samp[smpNr], sizeof (sampleTyp));
@@ -3066,7 +3066,7 @@
 	editor.curPlayInstr = 255;
 	editor.curPlaySmp = 255;
 
-	assert(stmm < MAX_VOICES && inst < MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97);
+	assert(stmm < MAX_VOICES && inst <= MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97);
 
 	ch = &stm[stmm];
 	s = &instr[130]->samp[0];
--- a/src/ft2_sample_ed.c
+++ b/src/ft2_sample_ed.c
@@ -1069,7 +1069,7 @@
 	}
 }
 
-// for scanning sample data peak where loopEnd+NUM_FIXED_TAP_SAMPLES is within scan range (fixed interpolation tap samples)
+// for scanning sample data peak where loopEnd+SINC_RIGHT_TAPS is within scan range (fixed interpolation tap samples)
 static void getSpecialMinMax16(sampleTyp *s, int32_t index, int32_t scanEnd, int16_t *min16, int16_t *max16)
 {
 	int16_t minVal, maxVal, minVal2, maxVal2;
@@ -1079,19 +1079,22 @@
 	minVal =  32767;
 	maxVal = -32768;
 
-	// read peak samples before fixed samples
+	// read samples before fixed samples (if needed)
 	if (index < s->fixedPos)
 	{
 		getMinMax16(&ptr16[index], s->fixedPos-index, &minVal, &maxVal);
-		index += s->fixedPos-index;
+		index = s->fixedPos;
 	}
 
-	// read fixed samples
-	int32_t tmpScanEnd = index+SINC_RIGHT_TAPS;
+	// read fixed samples (we are guaranteed to be within the fixed samples here)
+	const int32_t tapIndex = index-s->fixedPos;
+	const int32_t scanLength = SINC_RIGHT_TAPS-tapIndex;
+
+	int32_t tmpScanEnd = index+scanLength;
 	if (tmpScanEnd > scanEnd)
 		tmpScanEnd = scanEnd;
 
-	const int16_t *smpReadPtr = s->fixedSmp;
+	const int16_t *smpReadPtr = s->fixedSmp + tapIndex;
 	for (; index < tmpScanEnd; index++)
 	{
 		const int16_t smp16 = *smpReadPtr++;
@@ -1099,7 +1102,7 @@
 		if (smp16 > maxVal) maxVal = smp16;
 	}
 
-	// read peak samples after fixed samples
+	// read samples after fixed samples (if needed)
 	if (index < scanEnd)
 	{
 		getMinMax16(&ptr16[index], scanEnd-index, &minVal2, &maxVal2);
@@ -1111,7 +1114,7 @@
 	*max16 = maxVal;
 }
 
-// for scanning sample data peak where loopEnd+NUM_FIXED_TAP_SAMPLES is within scan range (fixed interpolation tap samples)
+// for scanning sample data peak where loopEnd+SINC_RIGHT_TAPS is within scan range (fixed interpolation tap samples)
 static void getSpecialMinMax8(sampleTyp *s, int32_t index, int32_t scanEnd, int8_t *min8, int8_t *max8)
 {
 	int8_t minVal, maxVal, minVal2, maxVal2;
@@ -1121,19 +1124,22 @@
 	minVal =  127;
 	maxVal = -128;
 
-	// read peak samples before fixed samples
+	// read samples before fixed samples (if needed)
 	if (index < s->fixedPos)
 	{
 		getMinMax8(&ptr8[index], s->fixedPos-index, &minVal, &maxVal);
-		index += s->fixedPos-index;
+		index = s->fixedPos;
 	}
 
-	// read fixed samples
-	int32_t tmpScanEnd = index+SINC_RIGHT_TAPS;
+	// read fixed samples (we are guaranteed to be within the fixed samples here)
+	const int32_t tapIndex = index-s->fixedPos;
+	const int32_t scanLength = SINC_RIGHT_TAPS-tapIndex;
+
+	int32_t tmpScanEnd = index+scanLength;
 	if (tmpScanEnd > scanEnd)
 		tmpScanEnd = scanEnd;
 
-	const int16_t *smpReadPtr = (const int16_t *)s->fixedSmp;
+	const int16_t *smpReadPtr = (const int16_t *)s->fixedSmp + tapIndex;
 	for (; index < tmpScanEnd; index++)
 	{
 		const int8_t smp8 = (int8_t)(*smpReadPtr++);
@@ -1141,7 +1147,7 @@
 		if (smp8 > maxVal) maxVal = smp8;
 	}
 
-	// read peak samples after fixed samples
+	// read samples after fixed samples (if needed)
 	if (index < scanEnd)
 	{
 		getMinMax8(&ptr8[index], scanEnd-index, &minVal2, &maxVal2);
--- a/src/ft2_scopes.c
+++ b/src/ft2_scopes.c
@@ -153,6 +153,8 @@
 	chanLookup = chansPerRow - 1;
 	scopeLens = scopeLenTab[chanLookup];
 
+	// get x,y,len for scope according to channel (we must do it this way since 'len' can differ!)
+
 	x = 2;
 	y = 94;
 
--- a/src/mixer/ft2_center_mix.c
+++ b/src/mixer/ft2_center_mix.c
@@ -10,7 +10,7 @@
 /*                      8-BIT CENTER MIXING ROUTINES                       */
 /* ----------------------------------------------------------------------- */
 
-void centerMix8bNoLoop(voice_t *v, uint32_t numSamples)
+void centerMix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -52,7 +52,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bLoop(voice_t *v, uint32_t numSamples)
+void centerMix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -94,7 +94,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bBidiLoop(voice_t *v, uint32_t numSamples)
+void centerMix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -138,7 +138,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -180,7 +180,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
@@ -247,7 +247,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
@@ -317,7 +317,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -359,7 +359,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -401,7 +401,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -445,7 +445,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampNoLoop(voice_t *v, uint32_t numSamples)
+void centerMix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -495,7 +495,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampLoop(voice_t *v, uint32_t numSamples)
+void centerMix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -545,7 +545,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampBidiLoop(voice_t *v, uint32_t numSamples)
+void centerMix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -597,7 +597,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -647,7 +647,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
@@ -727,7 +727,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
@@ -809,7 +809,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -859,7 +859,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -909,7 +909,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -965,7 +965,7 @@
 /*                      16-BIT CENTER MIXING ROUTINES                      */
 /* ----------------------------------------------------------------------- */
 
-void centerMix16bNoLoop(voice_t *v, uint32_t numSamples)
+void centerMix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1007,7 +1007,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bLoop(voice_t *v, uint32_t numSamples)
+void centerMix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1049,7 +1049,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bBidiLoop(voice_t *v, uint32_t numSamples)
+void centerMix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1093,7 +1093,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1135,7 +1135,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1202,7 +1202,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1271,7 +1271,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1313,7 +1313,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1355,7 +1355,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1400,7 +1400,7 @@
 }
 
 
-void centerMix16bRampNoLoop(voice_t *v, uint32_t numSamples)
+void centerMix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1450,7 +1450,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampLoop(voice_t *v, uint32_t numSamples)
+void centerMix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1500,7 +1500,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampBidiLoop(voice_t *v, uint32_t numSamples)
+void centerMix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1552,7 +1552,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1602,7 +1602,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1682,7 +1682,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1764,7 +1764,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1814,7 +1814,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1864,7 +1864,7 @@
 	SET_BACK_MIXER_POS
 }
 
-void centerMix16bRampBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+void centerMix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
--- a/src/mixer/ft2_center_mix.h
+++ b/src/mixer/ft2_center_mix.h
@@ -6,47 +6,47 @@
 // no volume ramping
 
 // 8-bit
-void centerMix8bNoLoop(voice_t *v, uint32_t numSamples);
-void centerMix8bLoop(voice_t *v, uint32_t numSamples);
-void centerMix8bBidiLoop(voice_t *v, uint32_t numSamples);
-void centerMix8bNoLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bBidiLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t numSamples);
+void centerMix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
 
 // 16-bit
-void centerMix16bNoLoop(voice_t *v, uint32_t numSamples);
-void centerMix16bLoop(voice_t *v, uint32_t numSamples);
-void centerMix16bBidiLoop(voice_t *v, uint32_t numSamples);
-void centerMix16bNoLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bBidiLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t numSamples);
+void centerMix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
 
 // volume ramping
 
 // 8-bit
-void centerMix8bRampNoLoop(voice_t *v, uint32_t numSamples);
-void centerMix8bRampLoop(voice_t *v, uint32_t numSamples);
-void centerMix8bRampBidiLoop(voice_t *v, uint32_t numSamples);
-void centerMix8bRampNoLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bRampLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bRampBidiLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t numSamples);
+void centerMix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
 
 // 16bit
-void centerMix16bRampNoLoop(voice_t *v, uint32_t numSamples);
-void centerMix16bRampLoop(voice_t *v, uint32_t numSamples);
-void centerMix16bRampBidiLoop(voice_t *v, uint32_t numSamples);
-void centerMix16bRampNoLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bRampLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bRampBidiLoopSIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t numSamples);
-void centerMix16bRampBidiLoopLIntrp(voice_t *v, uint32_t numSamples);
+void centerMix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
+void centerMix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples);
--- a/src/mixer/ft2_mix.c
+++ b/src/mixer/ft2_mix.c
@@ -31,7 +31,7 @@
 /*                          8-BIT MIXING ROUTINES                          */
 /* ----------------------------------------------------------------------- */
 
-static void mix8bNoLoop(voice_t *v, uint32_t numSamples)
+static void mix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -73,7 +73,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bLoop(voice_t *v, uint32_t numSamples)
+static void mix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -115,7 +115,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bBidiLoop(voice_t *v, uint32_t numSamples)
+static void mix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -158,7 +158,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -200,7 +200,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
@@ -267,7 +267,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
@@ -336,7 +336,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -378,7 +378,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -420,7 +420,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -464,7 +464,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampNoLoop(voice_t *v, uint32_t numSamples)
+static void mix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -514,7 +514,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampLoop(voice_t *v, uint32_t numSamples)
+static void mix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -564,7 +564,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampBidiLoop(voice_t *v, uint32_t numSamples)
+static void mix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -616,7 +616,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -666,7 +666,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	int8_t *smpTapPtr;
@@ -746,7 +746,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	int8_t *smpTapPtr;
@@ -828,7 +828,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -878,7 +878,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -928,7 +928,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix8bRampBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int8_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -984,7 +984,7 @@
 /*                          16-BIT MIXING ROUTINES                         */
 /* ----------------------------------------------------------------------- */
 
-static void mix16bNoLoop(voice_t *v, uint32_t numSamples)
+static void mix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1026,7 +1026,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bLoop(voice_t *v, uint32_t numSamples)
+static void mix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1068,7 +1068,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bBidiLoop(voice_t *v, uint32_t numSamples)
+static void mix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1112,7 +1112,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1154,7 +1154,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1221,7 +1221,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1290,7 +1290,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1332,7 +1332,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1374,7 +1374,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1418,7 +1418,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampNoLoop(voice_t *v, uint32_t numSamples)
+static void mix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1468,7 +1468,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampLoop(voice_t *v, uint32_t numSamples)
+static void mix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1518,7 +1518,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampBidiLoop(voice_t *v, uint32_t numSamples)
+static void mix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1570,7 +1570,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampNoLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1620,7 +1620,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1700,7 +1700,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampBidiLoopSIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	int16_t *smpTapPtr;
@@ -1782,7 +1782,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampNoLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1832,7 +1832,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
@@ -1882,7 +1882,7 @@
 	SET_BACK_MIXER_POS
 }
 
-static void mix16bRampBidiLoopLIntrp(voice_t *v, uint32_t numSamples)
+static void mix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples)
 {
 	const int16_t *base, *revBase, *smpPtr;
 	double dSample, *dMixBufferL, *dMixBufferR;
--- a/src/mixer/ft2_mix.h
+++ b/src/mixer/ft2_mix.h
@@ -3,6 +3,6 @@
 #include <stdint.h>
 #include "../ft2_audio.h"
 
-typedef void (*mixFunc)(void *, int32_t);
+typedef void (*mixFunc)(void *, uint32_t, uint32_t);
 
 extern const mixFunc mixFuncTab[72]; // ft2_mix.c
--- a/src/mixer/ft2_mix_macros.h
+++ b/src/mixer/ft2_mix_macros.h
@@ -31,15 +31,15 @@
 
 #define GET_MIXER_VARS \
 	const uint64_t delta = v->delta; \
-	dMixBufferL = audio.dMixBufferL; \
-	dMixBufferR = audio.dMixBufferR; \
+	dMixBufferL = audio.dMixBufferL + bufferPos; \
+	dMixBufferR = audio.dMixBufferR + bufferPos; \
 	pos = v->pos; \
 	posFrac = v->posFrac; \
 
 #define GET_MIXER_VARS_RAMP \
 	const uint64_t delta = v->delta; \
-	dMixBufferL = audio.dMixBufferL; \
-	dMixBufferR = audio.dMixBufferR; \
+	dMixBufferL = audio.dMixBufferL + bufferPos; \
+	dMixBufferR = audio.dMixBufferR + bufferPos; \
 	dVolLDelta = v->dVolDeltaL; \
 	dVolRDelta = v->dVolDeltaR; \
 	pos = v->pos; \
@@ -47,8 +47,8 @@
 
 #define GET_MIXER_VARS_MONO_RAMP \
 	const uint64_t delta = v->delta; \
-	dMixBufferL = audio.dMixBufferL; \
-	dMixBufferR = audio.dMixBufferR; \
+	dMixBufferL = audio.dMixBufferL + bufferPos; \
+	dMixBufferR = audio.dMixBufferR + bufferPos; \
 	dVolLDelta = v->dVolDeltaL; \
 	pos = v->pos; \
 	posFrac = v->posFrac; \