From 91f98f71c9809ed82b1689d6b396d23628778e05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 14:58:22 +0000 Subject: [PATCH 1/2] Bump NAudio.Core from 2.3.0 to 3.0.1 --- updated-dependencies: - dependency-name: NAudio.Core dependency-version: 3.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 2 +- src/Beutl.ProjectSystem/Beutl.ProjectSystem.csproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 8d7cb1892..b5a9354ac 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -57,7 +57,7 @@ - + diff --git a/src/Beutl.ProjectSystem/Beutl.ProjectSystem.csproj b/src/Beutl.ProjectSystem/Beutl.ProjectSystem.csproj index 0bd959431..ddb16881f 100644 --- a/src/Beutl.ProjectSystem/Beutl.ProjectSystem.csproj +++ b/src/Beutl.ProjectSystem/Beutl.ProjectSystem.csproj @@ -17,6 +17,7 @@ + From 5d5056ea70a1b2eac6af2b672a32668c9773cf18 Mon Sep 17 00:00:00 2001 From: Yuto Terada Date: Tue, 25 Aug 2026 13:45:03 +0900 Subject: [PATCH 2/2] refactor!: migrate audio providers to NAudio 3 API Migrate Beutl.Engine audio providers and decoder bridges to NAudio 3 Span-based APIs. BREAKING CHANGE: Beutl.Engine consumers implementing NAudio.Wave.ISampleProvider for SampleProviderReader.ReadStereo must replace Read(float[], int, int) with Read(Span). --- .../Audio/Graph/Nodes/ResampleNode.cs | 10 ++--- .../Audio/Graph/Nodes/SpeedNode.cs | 41 +++++++++++-------- .../Media/Decoding/SampleProviderReader.cs | 2 +- .../Decoding/SampleProviderReaderTests.cs | 14 +++---- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/Beutl.Engine/Audio/Graph/Nodes/ResampleNode.cs b/src/Beutl.Engine/Audio/Graph/Nodes/ResampleNode.cs index 2dd457c6b..7fdcfbf5c 100644 --- a/src/Beutl.Engine/Audio/Graph/Nodes/ResampleNode.cs +++ b/src/Beutl.Engine/Audio/Graph/Nodes/ResampleNode.cs @@ -259,7 +259,7 @@ public AudioBuffer Read(int sampleCount) try { var buffer = new float[sampleCount * _channelCount]; - int samplesRead = _wdlResampler!.Read(buffer, 0, buffer.Length); + int samplesRead = _wdlResampler!.Read(buffer.AsSpan()); // Copy interleaved samples back to AudioBuffer for (int ch = 0; ch < _channelCount; ch++) @@ -320,13 +320,13 @@ public void Append(AudioBuffer buffer) _inputBuffers.Enqueue(buffer); } - public int Read(float[] buffer, int offset, int count) + public int Read(Span buffer) { if (_disposed) return 0; int total = 0; - while (total < count) + while (total < buffer.Length) { if (_currentBuffer is null) { @@ -337,7 +337,7 @@ public int Read(float[] buffer, int offset, int count) _position = 0; } - int samplesPerChannel = (count - total) / ChannelCount; + int samplesPerChannel = (buffer.Length - total) / ChannelCount; int availableSamples = _currentBuffer.SampleCount - _position; int samplesToRead = Math.Min(samplesPerChannel, availableSamples); if (samplesToRead <= 0) @@ -351,7 +351,7 @@ public int Read(float[] buffer, int offset, int count) { for (int ch = 0; ch < ChannelCount; ch++) { - buffer[offset + total + i * ChannelCount + ch] + buffer[total + i * ChannelCount + ch] = _currentBuffer.GetChannelData(ch)[_position + i]; } } diff --git a/src/Beutl.Engine/Audio/Graph/Nodes/SpeedNode.cs b/src/Beutl.Engine/Audio/Graph/Nodes/SpeedNode.cs index b24bf9e3c..917daeda5 100644 --- a/src/Beutl.Engine/Audio/Graph/Nodes/SpeedNode.cs +++ b/src/Beutl.Engine/Audio/Graph/Nodes/SpeedNode.cs @@ -443,13 +443,11 @@ private bool BeginStream(double outputStartSeconds, double sourceStartSeconds, b // stream never jumps between chunks. Advances the cursor by what was actually produced (short // only at end-of-source) and returns that count. private int Read( - float[] buffer, - int interleavedOffset, - int count, + Span buffer, AudioProcessContext context, bool draining) { - if (count <= 0) + if (buffer.IsEmpty) return 0; // Derive each sub-range from the exact sample cursor to avoid repeated samples at fractional rates. @@ -465,7 +463,7 @@ private int Read( TimeSpan sourceStart = TimeSpan.FromTicks(sourceStartTicks); var range = new TimeRange( sourceStart, - AudioProcessContext.GetDurationForSampleCount(count, _sampleRate)); + AudioProcessContext.GetDurationForSampleCount(buffer.Length / _channels, _sampleRate)); var subContext = new AudioProcessContext( range, _sampleRate, @@ -480,15 +478,15 @@ private int Read( : _speedNode.Inputs[0].Process(subContext); var leftData = result.GetChannelData(0); var rightData = result.GetChannelData(1); - int samplesToRead = Math.Min(count, result.SampleCount); + int samplesToRead = Math.Min(buffer.Length / _channels, result.SampleCount); for (int i = 0; i < samplesToRead; i++) { - buffer[interleavedOffset + i * _channels] = leftData[i]; - buffer[interleavedOffset + i * _channels + 1] = rightData[i]; + buffer[i * _channels] = leftData[i]; + buffer[i * _channels + 1] = rightData[i]; } _srcReadPos += samplesToRead; - return samplesToRead; + return samplesToRead * _channels; } public AudioBuffer ProcessBuffer( @@ -529,10 +527,17 @@ public AudioBuffer ProcessBuffer( int framesDone = 0; while (framesDone < expectedOut) { - int want = _rs.ResamplePrepare(expectedOut - framesDone, _channels, out float[] inBuf, out int inOff); - int got = Read(inBuf, inOff, want, context, draining); - - int made = _rs.ResampleOut(dst, framesDone * _channels, got, expectedOut - framesDone, _channels); + int want = _rs.ResamplePrepare( + expectedOut - framesDone, + _channels, + out Span inBuf); + int got = Read(inBuf[..(want * _channels)], context, draining) / _channels; + + int made = _rs.ResampleOut( + dst.AsSpan(framesDone * _channels, (expectedOut - framesDone) * _channels), + got, + expectedOut - framesDone, + _channels); // No output and no input means the source is exhausted; the tail-fill below pads the // rest. (got > 0 with made == 0 just means the resampler needs more lookahead.) @@ -590,10 +595,14 @@ public AudioBuffer ProcessBufferWithVariableSpeed( double vAvg = sumSpeed / framesThis; ConfigureVariableResampling(vAvg); - int want = _rs.ResamplePrepare(framesThis, _channels, out float[] inBuf, out int inOff); - int got = Read(inBuf, inOff, want, context, draining); + int want = _rs.ResamplePrepare(framesThis, _channels, out Span inBuf); + int got = Read(inBuf[..(want * _channels)], context, draining) / _channels; - int made = _rs.ResampleOut(dst, framesDone * _channels, got, framesThis, _channels); + int made = _rs.ResampleOut( + dst.AsSpan(framesDone * _channels, framesThis * _channels), + got, + framesThis, + _channels); if (made == 0 && got == 0) break; diff --git a/src/Beutl.Engine/Media/Decoding/SampleProviderReader.cs b/src/Beutl.Engine/Media/Decoding/SampleProviderReader.cs index f77683a20..49a825b1a 100644 --- a/src/Beutl.Engine/Media/Decoding/SampleProviderReader.cs +++ b/src/Beutl.Engine/Media/Decoding/SampleProviderReader.cs @@ -36,7 +36,7 @@ public static Ref ReadStereo(ISampleProvider provider, int sampleRate, int // ToStereo() yields 2 floats per frame, so the provider's element count maps to frames via /2. float[] buffer = new float[length * 2]; - int frames = provider.Read(buffer, 0, buffer.Length) / 2; + int frames = provider.Read(buffer.AsSpan()) / 2; if (frames <= 0) return Ref.Create(new Pcm(sampleRate, 0)); diff --git a/tests/Beutl.UnitTests/Engine/Media/Decoding/SampleProviderReaderTests.cs b/tests/Beutl.UnitTests/Engine/Media/Decoding/SampleProviderReaderTests.cs index 0437e1534..e96e4d75b 100644 --- a/tests/Beutl.UnitTests/Engine/Media/Decoding/SampleProviderReaderTests.cs +++ b/tests/Beutl.UnitTests/Engine/Media/Decoding/SampleProviderReaderTests.cs @@ -29,15 +29,15 @@ public FakeSampleProvider(int totalFrames, int maxReturnFrames = int.MaxValue) public WaveFormat WaveFormat { get; } - public int Read(float[] buffer, int offset, int count) + public int Read(Span buffer) { int available = Math.Max(0, _totalFrames - _position); - int frames = Math.Min(Math.Min(count / 2, available), _maxReturnFrames); + int frames = Math.Min(Math.Min(buffer.Length / 2, available), _maxReturnFrames); for (int i = 0; i < frames; i++) { int idx = _position + i; - buffer[offset + i * 2] = idx; - buffer[offset + i * 2 + 1] = -idx; + buffer[i * 2] = idx; + buffer[i * 2 + 1] = -idx; } _position += frames; return frames * 2; @@ -52,11 +52,11 @@ private sealed class OddFloatProvider : ISampleProvider { public WaveFormat WaveFormat { get; } = WaveFormat.CreateIeeeFloatWaveFormat(SampleRate, 2); - public int Read(float[] buffer, int offset, int count) + public int Read(Span buffer) { - int floats = Math.Min(count, 3); // always 3 floats = 1.5 frames + int floats = Math.Min(buffer.Length, 3); // always 3 floats = 1.5 frames for (int i = 0; i < floats; i++) - buffer[offset + i] = i; + buffer[i] = i; return floats; } }