mirror of
https://github.com/unclshura/ABStemPlayer.git
synced 2026-08-07 00:43:38 +00:00
New test mp3. Much faster StemWaveformService
This commit is contained in:
parent
5142bb61f2
commit
7efd223a6c
@ -254,9 +254,7 @@ public sealed partial class PlaybackViewModel : ObservableObject
|
||||
|
||||
var decoder = _decoderFactory.Create(stem);
|
||||
|
||||
var waveform = await _waveformService.ComputeWaveformAsync(stem, decoder, 200);
|
||||
|
||||
stem.Waveform = waveform;
|
||||
stem.Waveform = await _waveformService.ComputeWaveformAsync(stem, decoder, 200).ConfigureAwait(false);
|
||||
|
||||
// Update progress safely
|
||||
var done = Interlocked.Increment(ref completed);
|
||||
|
||||
@ -27,8 +27,8 @@ public sealed class Htdemucs6sSeparator : IStemSeparator
|
||||
return existingStems;
|
||||
|
||||
var mix = LoadStereoFloatWave(request.SourceFilePath, out var sr);
|
||||
if (sr != _sampleRate)
|
||||
throw new InvalidOperationException($"Input must be {_sampleRate} Hz");
|
||||
//if (sr != _sampleRate)
|
||||
// throw new InvalidOperationException($"Input must be {_sampleRate} Hz");
|
||||
|
||||
var totalSamples = mix.GetLength(1);
|
||||
|
||||
|
||||
@ -152,17 +152,19 @@ public sealed class RubberBandTimeStretchEngine : ITimeStretchEngine, IAsyncDisp
|
||||
try { _stdin ?.Close(); } catch { }
|
||||
try { _ff ?.Dispose(); } catch { }
|
||||
|
||||
if (_readerTask != null)
|
||||
if (_cts != null)
|
||||
{
|
||||
Debug.Assert(_cts != null);
|
||||
|
||||
_cts.Cancel();
|
||||
try { await _readerTask.ConfigureAwait(false); } catch { }
|
||||
_readerTask = null;
|
||||
_cts.Dispose();
|
||||
_cts = null;
|
||||
}
|
||||
|
||||
if (_readerTask != null)
|
||||
{
|
||||
try { await _readerTask.ConfigureAwait(false); } catch { }
|
||||
_readerTask = null;
|
||||
}
|
||||
|
||||
_ff = null;
|
||||
_stdin = null;
|
||||
_stdout = null;
|
||||
|
||||
@ -104,16 +104,17 @@ public sealed class StemPlaybackEngine : IStemPlaybackEngine, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public Task PlayAsync()
|
||||
public async Task PlayAsync()
|
||||
{
|
||||
// TODO: fix the pause mode
|
||||
lock (_stateLock)
|
||||
{
|
||||
if (IsPlaying || _session is null)
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
|
||||
if (_pipeline is not null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
return;
|
||||
}
|
||||
|
||||
_pipeline = new PipelineState
|
||||
@ -130,13 +131,14 @@ public sealed class StemPlaybackEngine : IStemPlaybackEngine, IDisposable
|
||||
d.Seek(_pendingSeekFrames);
|
||||
}
|
||||
|
||||
|
||||
_decodedFramePosition = _pendingSeekFrames;
|
||||
_outputFramesWritten = (long)(_decodedFramePosition / Math.Max(_currentSpeed, 0.0001f));
|
||||
|
||||
_pipeline.RenderTask = Task.Run(() => RenderLoopAsync(_pipeline, _pipeline.Cts.Token));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
await _timeStretchEngine.Configure(_session.Speed, _pipeline.Cts.Token).ConfigureAwait(false);
|
||||
_pipeline.RenderTask = Task.Run(() => RenderLoopAsync(_pipeline, _pipeline.Cts.Token));
|
||||
}
|
||||
|
||||
public Task PauseAsync()
|
||||
@ -223,7 +225,9 @@ public sealed class StemPlaybackEngine : IStemPlaybackEngine, IDisposable
|
||||
_outputFramesWritten = (long)(_decodedFramePosition / Math.Max(_currentSpeed, 0.0001f));
|
||||
}
|
||||
|
||||
Debug.Assert(_pipeline?.Cts != null);
|
||||
if (_pipeline?.Cts is null)
|
||||
return;
|
||||
|
||||
await _timeStretchEngine.Configure(settings, _pipeline!.Cts!.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@ -12,56 +12,63 @@ public sealed class StemWaveformService : IStemWaveformService
|
||||
public async Task<float[]> ComputeWaveformAsync(StemTrack stem, IStemDecoder decoder, int segments = 200)
|
||||
{
|
||||
if (segments <= 0)
|
||||
{
|
||||
return Array.Empty<float>();
|
||||
}
|
||||
|
||||
var value = await TryReadingFromCache(stem, segments);
|
||||
if (value != null)
|
||||
return value;
|
||||
var cached = await TryReadingFromCache(stem, segments);
|
||||
if (cached != null)
|
||||
return cached;
|
||||
|
||||
var totalFrames = (long)(decoder.Stem.Duration.TotalSeconds * decoder.Stem.SampleRate);
|
||||
var totalFrames = (long)(stem.Duration.TotalSeconds * stem.SampleRate);
|
||||
var framesPerSegment = Math.Max(1, totalFrames / segments);
|
||||
|
||||
var result = new float[segments];
|
||||
var sums = new float[segments];
|
||||
var counts = new int[segments];
|
||||
|
||||
decoder.Reset();
|
||||
|
||||
for (var i = 0; i < segments; i++)
|
||||
long globalFramePos = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var segmentStart = framesPerSegment * i;
|
||||
decoder.Seek(segmentStart);
|
||||
|
||||
var sum = 0f;
|
||||
var count = 0;
|
||||
|
||||
// Decode only one block per segment
|
||||
var block = await decoder.DecodeNextBlockAsync(CancellationToken.None);
|
||||
if (block != null)
|
||||
{
|
||||
if (block == null)
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
var span = block.Value.Span;
|
||||
var channels = decoder.Stem.Channels;
|
||||
var channels = stem.Channels;
|
||||
var frames = block.Value.Frames;
|
||||
|
||||
for (var s = 0; s < span.Length; s++)
|
||||
for (int f = 0; f < frames; f++)
|
||||
{
|
||||
var v = span[s];
|
||||
sum += Math.Abs(v);
|
||||
}
|
||||
long frameIndex = globalFramePos + f;
|
||||
int segmentIndex = (int)(frameIndex / framesPerSegment);
|
||||
|
||||
count = span.Length;
|
||||
if (segmentIndex >= segments)
|
||||
break;
|
||||
|
||||
// accumulate absolute amplitude across channels
|
||||
float sum = 0f;
|
||||
int baseIndex = f * channels;
|
||||
|
||||
for (int c = 0; c < channels; c++)
|
||||
sum += Math.Abs(span[baseIndex + c]);
|
||||
|
||||
sums[segmentIndex] += sum;
|
||||
counts[segmentIndex] += channels;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
globalFramePos += block.Value.Frames;
|
||||
block.Value.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
result[i] = count > 0 ? sum / count : 0f;
|
||||
|
||||
await Task.Yield();
|
||||
}
|
||||
var result = new float[segments];
|
||||
for (int i = 0; i < segments; i++)
|
||||
result[i] = counts[i] > 0 ? sums[i] / counts[i] : 0f;
|
||||
|
||||
await SaveToCache(stem, result);
|
||||
return result;
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user