diff --git a/ABStemPlayer/Views/PlaybackControls.axaml b/ABStemPlayer/Views/PlaybackControls.axaml
index 8122128..cf8fa64 100644
--- a/ABStemPlayer/Views/PlaybackControls.axaml
+++ b/ABStemPlayer/Views/PlaybackControls.axaml
@@ -194,49 +194,33 @@
Margin="0,0,6,0"/>
diff --git a/AudioCore/Impl/AudioMixer.cs b/AudioCore/Impl/AudioMixer.cs
index 1765f0a..74ce338 100644
--- a/AudioCore/Impl/AudioMixer.cs
+++ b/AudioCore/Impl/AudioMixer.cs
@@ -50,18 +50,34 @@ public sealed class AudioMixer : IAudioMixer
for (var i = 0; i < frames; i++)
{
- if (i * inChannels + 1 >= inSpan.Length)
+ var baseIndex = i * inChannels;
+
+ // If there are no samples for this frame, skip
+ if (baseIndex >= inSpan.Length)
{
outSpan[i * 2 + 0] = 0f;
outSpan[i * 2 + 1] = 0f;
continue;
}
- var l = inChannels > 1 ? inSpan[i * inChannels + 0] : inSpan[i];
- var r = inChannels > 1 ? inSpan[i * inChannels + 1] : inSpan[i];
+ if (inChannels == 1)
+ {
+ var inp = inSpan[baseIndex];
+ outSpan[i * 2 + 0] += inp * leftGain;
+ outSpan[i * 2 + 1] += inp * rightGain;
+ }
+ else
+ {
+ // ensure both left and right samples exist for multi-channel input
+ if (baseIndex + 1 >= inSpan.Length)
+ continue; // or treat missing right as 0, depending on desired behavior
- outSpan[i * 2 + 0] += l * leftGain;
- outSpan[i * 2 + 1] += r * rightGain;
+ var l = inSpan[baseIndex + 0];
+ var r = inSpan[baseIndex + 1];
+
+ outSpan[i * 2 + 0] += l * leftGain;
+ outSpan[i * 2 + 1] += r * rightGain;
+ }
}
}
diff --git a/AudioCore/Impl/StemPlaybackEngine.cs b/AudioCore/Impl/StemPlaybackEngine.cs
index c1ae79e..ccfc513 100644
--- a/AudioCore/Impl/StemPlaybackEngine.cs
+++ b/AudioCore/Impl/StemPlaybackEngine.cs
@@ -343,9 +343,17 @@ public sealed class StemPlaybackEngine : IStemPlaybackEngine, IDisposable
if (loopEnabled && loopEnd > loopStart && nextPosition >= loopEnd)
{
+ // Rewind decoders to the loop start and continue decoding so playback loops
+ foreach (var d in decodersSnapshot)
+ {
+ try { d.Seek(loopStart); } catch { }
+ }
+
lock (_stateLock)
- _decodedFramePosition = loopEnd;
- break;
+ _decodedFramePosition = loopStart;
+
+ // continue decoding from the loop start
+ continue;
}
lock (_stateLock)
diff --git a/AudioCore_Tests/AudioMixer_Tests.cs b/AudioCore_Tests/AudioMixer_Tests.cs
index 7256a44..caec258 100644
--- a/AudioCore_Tests/AudioMixer_Tests.cs
+++ b/AudioCore_Tests/AudioMixer_Tests.cs
@@ -147,7 +147,7 @@ public sealed class AudioMixer_Tests
[TestMethod]
[TestCategory("ProductionBugSuspected")]
- [Ignore("ProductionBugSuspected")]
+ //[Ignore("ProductionBugSuspected")]
public void Mixer_Handles_Mono_Stem()
{
// mono block
diff --git a/AudioCore_Tests/StemPlaybackEngine_Tests.cs b/AudioCore_Tests/StemPlaybackEngine_Tests.cs
index f40eb95..f52f9b1 100644
--- a/AudioCore_Tests/StemPlaybackEngine_Tests.cs
+++ b/AudioCore_Tests/StemPlaybackEngine_Tests.cs
@@ -302,7 +302,7 @@ public sealed class StemPlaybackEngine_Tests
public async Task LoopRegion_SeeksBackOnBoundary()
{
var pool = new AudioBufferPool();
- var decoderFactory = new MockDecoderFactory(pool, 1024, 5);
+ var decoderFactory = new MockDecoderFactory(pool, 44100, 5);
var output = new MockOutput();
var mixer = new MockMixer(pool);
var stretch = new MockTimeStretch();
@@ -320,7 +320,7 @@ public sealed class StemPlaybackEngine_Tests
await engine.LoadSessionAsync(session, new DummyProgressReporter());
await engine.PlayAsync();
- await Task.Delay(TimeSpan.FromSeconds(3));
+ await Task.Delay(TimeSpan.FromSeconds(6));
await engine.StopAsync();