From 598c7ce42b0d87252658118dc334e7b31cf59b93 Mon Sep 17 00:00:00 2001 From: MikiraSora Date: Fri, 1 Nov 2024 23:56:57 +0800 Subject: [PATCH] support change music play speed --- OngekiFumenEditor/App.config | 6 + OngekiFumenEditor/Costura64/SoundTouch.dll | Bin 0 -> 129536 bytes OngekiFumenEditor/FodyWeavers.xml | 8 +- .../Kernel/Audio/IAudioManager.cs | 1 + .../NAudioImpl/Music/DefaultMusicPlayer.cs | 21 ++- .../Kernel/Audio/NAudioImpl/NAudioManager.cs | 41 +++- .../Audio/NAudioImpl/SoundTouch/SoundTouch.cs | 139 ++++++++++++++ .../SoundTouch/SoundTouchInterop64.cs | 178 ++++++++++++++++++ .../SoundTouch/SoundTouchProfile.cs | 15 ++ .../SoundTouch/SoundTouchSettings.cs | 46 +++++ .../SoundTouch/VarispeedSampleProvider.cs | 149 +++++++++++++++ .../AudioPlayerToolViewerViewModel.cs | 10 + .../Views/AudioPlayerToolViewerView.xaml | 3 +- OngekiFumenEditor/OngekiFumenEditor.csproj | 6 +- .../Properties/AudioSetting.Designer.cs | 24 +++ .../Properties/AudioSetting.settings | 6 + 16 files changed, 636 insertions(+), 17 deletions(-) create mode 100644 OngekiFumenEditor/Costura64/SoundTouch.dll create mode 100644 OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouch.cs create mode 100644 OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchInterop64.cs create mode 100644 OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchProfile.cs create mode 100644 OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchSettings.cs create mode 100644 OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/VarispeedSampleProvider.cs diff --git a/OngekiFumenEditor/App.config b/OngekiFumenEditor/App.config index 72a84600..7a22c519 100644 --- a/OngekiFumenEditor/App.config +++ b/OngekiFumenEditor/App.config @@ -54,6 +54,12 @@ True + + False + + + 50 + diff --git a/OngekiFumenEditor/Costura64/SoundTouch.dll b/OngekiFumenEditor/Costura64/SoundTouch.dll new file mode 100644 index 0000000000000000000000000000000000000000..f0b4fc43c4781e4768c99ab0c0faf451138b4ca4 GIT binary patch literal 129536 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 literal 0 HcmV?d00001 diff --git a/OngekiFumenEditor/FodyWeavers.xml b/OngekiFumenEditor/FodyWeavers.xml index 0a08fb25..a00db090 100644 --- a/OngekiFumenEditor/FodyWeavers.xml +++ b/OngekiFumenEditor/FodyWeavers.xml @@ -1,7 +1,5 @@  - - - - - + + + diff --git a/OngekiFumenEditor/Kernel/Audio/IAudioManager.cs b/OngekiFumenEditor/Kernel/Audio/IAudioManager.cs index 69afb484..436afa6d 100644 --- a/OngekiFumenEditor/Kernel/Audio/IAudioManager.cs +++ b/OngekiFumenEditor/Kernel/Audio/IAudioManager.cs @@ -8,6 +8,7 @@ public partial interface IAudioManager : IDisposable { float SoundVolume { get; set; } float MusicVolume { get; set; } + float MusicSpeed { get; set; } Task LoadSoundAsync(string filePath); Task LoadAudioAsync(string filePath); diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/Music/DefaultMusicPlayer.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/Music/DefaultMusicPlayer.cs index bbeec325..c6d12197 100644 --- a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/Music/DefaultMusicPlayer.cs +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/Music/DefaultMusicPlayer.cs @@ -3,6 +3,7 @@ using NAudio.Wave.SampleProviders; using OngekiFumenEditor.Kernel.Audio.NAudioImpl; using OngekiFumenEditor.Kernel.Audio.NAudioImpl.Music; +using OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch; using OngekiFumenEditor.Kernel.Audio.NAudioImpl.Utils; using OngekiFumenEditor.Kernel.Scheduler; using OngekiFumenEditor.Utils; @@ -112,18 +113,19 @@ public async Task Load(string audio_file, int targetSampleRate) } } - public void Seek(TimeSpan time, bool pause) + public void Seek(TimeSpan seekTime, bool pause) { - time = MathUtils.Max(TimeSpan.FromMilliseconds(0), MathUtils.Min(time, Duration)); + seekTime = MathUtils.Max(TimeSpan.FromMilliseconds(0), MathUtils.Min(seekTime, Duration)); - audioFileReader.Seek((long)(audioFileReader.WaveFormat.AverageBytesPerSecond * time.TotalSeconds), SeekOrigin.Begin); - baseOffset = time; + audioFileReader.Seek((long)(audioFileReader.WaveFormat.AverageBytesPerSecond * seekTime.TotalSeconds), SeekOrigin.Begin); + //more accurate + baseOffset = audioFileReader.CurrentTime; finishProvider.StartListen(); - UpdatePropsManually(); if (!pause) Play(); + UpdatePropsManually(); } public async void Play() @@ -131,6 +133,9 @@ public async void Play() IsPlaying = true; sw.Restart(); musicMixer.AddMixerInput(finishProvider); + UpdatePropsManually(); + manager.Reposition(); + await IoC.Get().AddScheduler(this); } @@ -138,8 +143,9 @@ private TimeSpan GetTime() { if (!IsPlaying) return pauseTime; - var offset = TimeSpan.FromTicks(sw.ElapsedTicks); - var actualTime = offset + baseOffset; + var offset = TimeSpan.FromTicks(sw.ElapsedTicks) * manager.MusicSpeed; + var adjustedTime = offset + baseOffset - TimeSpan.FromMilliseconds(manager.SpeedCostDelayMs); + var actualTime = MathUtils.Max(TimeSpan.Zero, adjustedTime); return actualTime; } @@ -149,6 +155,7 @@ public async void Stop() musicMixer.RemoveMixerInput(finishProvider); await IoC.Get().RemoveScheduler(this); Seek(TimeSpan.FromMilliseconds(0), true); + UpdatePropsManually(); } public async void Pause() diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/NAudioManager.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/NAudioManager.cs index 17c01ce6..7bb7071e 100644 --- a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/NAudioManager.cs +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/NAudioManager.cs @@ -5,6 +5,7 @@ using NAudio.Wave.SampleProviders; using OngekiFumenEditor.Kernel.Audio.DefaultImp.Music; using OngekiFumenEditor.Kernel.Audio.NAudioImpl.Sound; +using OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch; using OngekiFumenEditor.Kernel.Audio.NAudioImpl.Utils; using OngekiFumenEditor.Properties; using OngekiFumenEditor.Utils; @@ -24,6 +25,7 @@ public class NAudioManager : PropertyChangedBase, IAudioManager private HashSet> ownAudioPlayerRefs = new(); private bool enableSoundMultiPlay; private int targetSampleRate; + private bool enableVarspeed; private readonly IWavePlayer audioOutputDevice; private readonly MixingSampleProvider masterMixer; @@ -36,6 +38,11 @@ public class NAudioManager : PropertyChangedBase, IAudioManager private readonly VolumeSampleProvider soundVolumeWrapper; private readonly VolumeSampleProvider musicVolumeWrapper; + private VarispeedSampleProvider speedProvider; + + public bool EnableVarspeed => speedProvider is not null; + public int SpeedCostDelayMs { get; init; } + public float SoundVolume { get => soundVolumeWrapper.Volume; @@ -62,6 +69,21 @@ public float MusicVolume } } + public float MusicSpeed + { + get => EnableVarspeed ? speedProvider.PlaybackRate : 1; + set + { + if (EnableVarspeed) + { + //we can able to change speed when all player is not playing + if (!ownAudioPlayerRefs.Any(x => x.TryGetTarget(out var player) ? player.IsPlaying : false)) + speedProvider.PlaybackRate = value; + } + NotifyOfPropertyChange(() => MusicSpeed); + } + } + public IEnumerable<(string fileExt, string extDesc)> SupportAudioFileExtensionList { get; } = new[] { (".mp3","Audio File"), (".wav","Audio File"), @@ -73,10 +95,14 @@ public NAudioManager() var audioOutputType = (AudioOutputType)AudioSetting.Default.AudioOutputType; enableSoundMultiPlay = AudioSetting.Default.EnableSoundMultiPlay; targetSampleRate = AudioSetting.Default.AudioSampleRate; + enableVarspeed = AudioSetting.Default.EnableVarspeed; + SpeedCostDelayMs = AudioSetting.Default.VarspeedReadDurationMs; Log.LogDebug($"targetSampleRate: {targetSampleRate}"); Log.LogDebug($"audioOutputType: {audioOutputType}"); Log.LogDebug($"enableSoundMultiPlay: {enableSoundMultiPlay}"); + Log.LogDebug($"enableVarspeed: {enableVarspeed}"); + Log.LogDebug($"SpeedCostDelayMs: {SpeedCostDelayMs}"); try { @@ -112,7 +138,15 @@ public NAudioManager() //setup music musicMixer = new MixingSampleProvider(format); musicMixer.ReadFully = true; - musicVolumeWrapper = new VolumeSampleProvider(musicMixer); + if (enableVarspeed) + { + speedProvider = new VarispeedSampleProvider(musicMixer, SpeedCostDelayMs, new SoundTouchProfile(true, false)); + musicVolumeWrapper = new VolumeSampleProvider(speedProvider); + } + else + { + musicVolumeWrapper = new VolumeSampleProvider(musicMixer); + } masterMixer.AddMixerInput(musicVolumeWrapper); MusicVolume = AudioSetting.Default.MusicVolume; @@ -252,5 +286,10 @@ public void StopLoopSound(ILoopHandle h) //Log.LogDebug($"handle hashcode = {handle.GetHashCode()}"); RemoveSoundMixerInput(handle.Provider, true); } + + public void Reposition() + { + speedProvider?.Reposition(); + } } } diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouch.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouch.cs new file mode 100644 index 00000000..539fdd9e --- /dev/null +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouch.cs @@ -0,0 +1,139 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch +{ + class SoundTouch : IDisposable + { + private IntPtr handle; + private string versionString; + public SoundTouch() + { + handle = SoundTouchInterop64.soundtouch_createInstance(); + } + + public string VersionString + { + get + { + if (versionString == null) + { + var s = new StringBuilder(100); + SoundTouchInterop64.soundtouch_getVersionString2(s, s.Capacity); + versionString = s.ToString(); + } + return versionString; + } + } + + public void SetPitchOctaves(float pitchOctaves) + { + SoundTouchInterop64.soundtouch_setPitchOctaves(handle, pitchOctaves); + } + + public void SetSampleRate(int sampleRate) + { + SoundTouchInterop64.soundtouch_setSampleRate(handle, (uint)sampleRate); + } + + public void SetChannels(int channels) + { + SoundTouchInterop64.soundtouch_setChannels(handle, (uint)channels); + } + + private void DestroyInstance() + { + if (handle != IntPtr.Zero) + { + SoundTouchInterop64.soundtouch_destroyInstance(handle); + handle = IntPtr.Zero; + } + } + + public void Dispose() + { + DestroyInstance(); + GC.SuppressFinalize(this); + } + + ~SoundTouch() + { + DestroyInstance(); + } + + public void PutSamples(float[] samples, int numSamples) + { + SoundTouchInterop64.soundtouch_putSamples(handle, samples, numSamples); + } + + public int ReceiveSamples(float[] outBuffer, int maxSamples) + { + return (int)SoundTouchInterop64.soundtouch_receiveSamples(handle, outBuffer, (uint)maxSamples); + } + + public bool IsEmpty + { + get + { + return SoundTouchInterop64.soundtouch_isEmpty(handle) != 0; + } + } + + public int NumberOfSamplesAvailable + { + get + { + return (int)SoundTouchInterop64.soundtouch_numSamples(handle); + } + } + + public int NumberOfUnprocessedSamples + { + get + { + return SoundTouchInterop64.soundtouch_numUnprocessedSamples(handle); + } + } + + public void Flush() + { + SoundTouchInterop64.soundtouch_flush(handle); + } + + public void Clear() + { + SoundTouchInterop64.soundtouch_clear(handle); + } + + public void SetRate(float newRate) + { + SoundTouchInterop64.soundtouch_setRate(handle, newRate); + } + + public void SetTempo(float newTempo) + { + SoundTouchInterop64.soundtouch_setTempo(handle, newTempo); + } + + public int GetUseAntiAliasing() + { + return SoundTouchInterop64.soundtouch_getSetting(handle, SoundTouchSettings.UseAaFilter); + } + + public void SetUseAntiAliasing(bool useAntiAliasing) + { + SoundTouchInterop64.soundtouch_setSetting(handle, SoundTouchSettings.UseAaFilter, useAntiAliasing ? 1 : 0); + } + + public void SetUseQuickSeek(bool useQuickSeek) + { + SoundTouchInterop64.soundtouch_setSetting(handle, SoundTouchSettings.UseQuickSeek, useQuickSeek ? 1 : 0); + } + + public int GetUseQuickSeek() + { + return SoundTouchInterop64.soundtouch_getSetting(handle, SoundTouchSettings.UseQuickSeek); + } + } +} diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchInterop64.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchInterop64.cs new file mode 100644 index 00000000..246fda9c --- /dev/null +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchInterop64.cs @@ -0,0 +1,178 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch +{ + class SoundTouchInterop64 + { + /// + /// Create a new instance of SoundTouch processor. + /// + [DllImport("SoundTouch")] + public static extern IntPtr soundtouch_createInstance(); + + /// + /// Destroys a SoundTouch processor instance. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_destroyInstance(IntPtr h); + + /// + /// Get SoundTouch library version string - alternative function for + /// environments that can't properly handle character string as return value + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_getVersionString2(StringBuilder versionString, int bufferSize); + + /// + /// Get SoundTouch library version Id + /// + [DllImport("SoundTouch")] + public static extern uint soundtouch_getVersionId(); + + /// + /// Sets new rate control value. Normal rate = 1.0, smaller values + /// represent slower rate, larger faster rates. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setRate(IntPtr h, float newRate); + + /// + /// Sets new tempo control value. Normal tempo = 1.0, smaller values + /// represent slower tempo, larger faster tempo. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setTempo(IntPtr h, float newTempo); + + /// + /// Sets new rate control value as a difference in percents compared + /// to the original rate (-50 .. +100 %); + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setRateChange(IntPtr h, float newRate); + + /// + /// Sets new tempo control value as a difference in percents compared + /// to the original tempo (-50 .. +100 %); + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setTempoChange(IntPtr h, float newTempo); + + /// + /// Sets new pitch control value. Original pitch = 1.0, smaller values + /// represent lower pitches, larger values higher pitch. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setPitch(IntPtr h, float newPitch); + + /// + /// Sets pitch change in octaves compared to the original pitch + /// (-1.00 .. +1.00); + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setPitchOctaves(IntPtr h, float newPitch); + + /// + /// Sets pitch change in semi-tones compared to the original pitch + /// (-12 .. +12); + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setPitchSemiTones(IntPtr h, float newPitch); + + /// + /// Sets the number of channels, 1 = mono, 2 = stereo + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setChannels(IntPtr h, uint numChannels); + + /// + /// Sets sample rate. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_setSampleRate(IntPtr h, uint srate); + + /// + /// Flushes the last samples from the processing pipeline to the output. + /// Clears also the internal processing buffers. + /// + /// Note: This function is meant for extracting the last samples of a sound + /// stream. This function may introduce additional blank samples in the end + /// of the sound stream, and thus it's not recommended to call this function + /// in the middle of a sound stream. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_flush(IntPtr h); + + /// + /// Adds 'numSamples' pcs of samples from the 'samples' memory position into + /// the input of the object. Notice that sample rate _has_to_ be set before + /// calling this function, otherwise throws a runtime_error exception. + /// + /// Handle + /// Pointer to sample buffer. + /// Number of samples in buffer. Notice that in case of stereo-sound a single sample contains data for both channels. + [DllImport("SoundTouch")] + public static extern void soundtouch_putSamples(IntPtr h, [MarshalAs(UnmanagedType.LPArray)] float[] samples, int numSamples); + + /// + /// Clears all the samples in the object's output and internal processing + /// buffers. + /// + [DllImport("SoundTouch")] + public static extern void soundtouch_clear(IntPtr h); + + /// + /// Changes a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + /// Handle + /// Setting ID number, see SETTING_... defines. + /// New setting value. + /// 'TRUE' if the setting was succesfully changed + [DllImport("SoundTouch")] + public static extern bool soundtouch_setSetting(IntPtr h, SoundTouchSettings settingId, int value); + + /// + /// Reads a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + /// Handle + /// Setting ID number, see SETTING_... defines. + /// The setting value + [DllImport("SoundTouch")] + public static extern int soundtouch_getSetting(IntPtr h, SoundTouchSettings settingId); + + /// + /// Returns number of samples currently unprocessed. + /// + [DllImport("SoundTouch")] + public static extern int soundtouch_numUnprocessedSamples(IntPtr h); + + /// + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + /// + /// Handle + /// Buffer where to copy output samples. + /// How many samples to receive at max. + [DllImport("SoundTouch")] + public static extern uint soundtouch_receiveSamples(IntPtr h, [MarshalAs(UnmanagedType.LPArray)] float[] outBuffer, uint maxSamples); + + /// + /// Returns number of samples currently available. + /// + [DllImport("SoundTouch")] + public static extern uint soundtouch_numSamples(IntPtr h); + + /// + /// Returns nonzero if there aren't any samples available for outputting. + /// + [DllImport("SoundTouch")] + public static extern int soundtouch_isEmpty(IntPtr h); + + } +} \ No newline at end of file diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchProfile.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchProfile.cs new file mode 100644 index 00000000..5a2cda57 --- /dev/null +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchProfile.cs @@ -0,0 +1,15 @@ +namespace OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch +{ + internal class SoundTouchProfile + { + public bool UseTempo { get; set; } + public bool UseAntiAliasing { get; set; } + public bool UseQuickSeek { get; set; } + + public SoundTouchProfile(bool useTempo, bool useAntiAliasing) + { + UseTempo = useTempo; + UseAntiAliasing = useAntiAliasing; + } + } +} \ No newline at end of file diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchSettings.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchSettings.cs new file mode 100644 index 00000000..691f6ce4 --- /dev/null +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/SoundTouchSettings.cs @@ -0,0 +1,46 @@ +namespace OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch +{ + enum SoundTouchSettings + { + /// + /// Available setting IDs for the 'setSetting' and 'get_setting' functions. + /// Enable/disable anti-alias filter in pitch transposer (0 = disable) + /// + UseAaFilter = 0, + + /// + /// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) + /// + AaFilterLength = 1, + + /// + /// Enable/disable quick seeking algorithm in tempo changer routine + /// (enabling quick seeking lowers CPU utilization but causes a minor sound + /// quality compromising) + /// + UseQuickSeek = 2, + + /// + /// Time-stretch algorithm single processing sequence length in milliseconds. This determines + /// to how long sequences the original sound is chopped in the time-stretch algorithm. + /// See "STTypes.h" or README for more information. + /// + SequenceMs = 3, + + /// + /// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the + /// best possible overlapping location. This determines from how wide window the algorithm + /// may look for an optimal joining location when mixing the sound sequences back together. + /// See "STTypes.h" or README for more information. + /// + SeekWindowMs = 4, + + /// + /// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences + /// are mixed back together, to form a continuous sound stream, this parameter defines over + /// how long period the two consecutive sequences are let to overlap each other. + /// See "STTypes.h" or README for more information. + /// + OverlapMs = 5 + }; +} \ No newline at end of file diff --git a/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/VarispeedSampleProvider.cs b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/VarispeedSampleProvider.cs new file mode 100644 index 00000000..b71b5405 --- /dev/null +++ b/OngekiFumenEditor/Kernel/Audio/NAudioImpl/SoundTouch/VarispeedSampleProvider.cs @@ -0,0 +1,149 @@ +using System; +using NAudio.Wave; +using OngekiFumenEditor.Utils; + +namespace OngekiFumenEditor.Kernel.Audio.NAudioImpl.SoundTouch +{ + class VarispeedSampleProvider : ISampleProvider, IDisposable + { + private readonly ISampleProvider sourceProvider; + private readonly SoundTouch soundTouch; + private readonly float[] sourceReadBuffer; + private readonly float[] soundTouchReadBuffer; + private readonly int channelCount; + private float playbackRate = 1.0f; + private SoundTouchProfile currentSoundTouchProfile; + private bool repositionRequested; + + public VarispeedSampleProvider(ISampleProvider sourceProvider, int readDurationMilliseconds, SoundTouchProfile soundTouchProfile) + { + soundTouch = new SoundTouch(); + + Log.LogDebug($"SoundTouch Version {soundTouch.VersionString}"); + Log.LogDebug($"Use QuickSeek: {soundTouch.GetUseQuickSeek()}"); + Log.LogDebug($"Use AntiAliasing: {soundTouch.GetUseAntiAliasing()}"); + + SetSoundTouchProfile(soundTouchProfile); + this.sourceProvider = sourceProvider; + soundTouch.SetSampleRate(WaveFormat.SampleRate); + channelCount = WaveFormat.Channels; + soundTouch.SetChannels(channelCount); + sourceReadBuffer = new float[(WaveFormat.SampleRate * channelCount * (long)readDurationMilliseconds) / 1000]; + soundTouchReadBuffer = new float[sourceReadBuffer.Length * 10]; // support down to 0.1 speed + } + + public int Read(float[] buffer, int offset, int count) + { + if (playbackRate == 0) // play silence + { + for (int n = 0; n < count; n++) + { + buffer[offset++] = 0; + } + return count; + } + + if (repositionRequested) + { + soundTouch.Clear(); + repositionRequested = false; + } + + int samplesRead = 0; + bool reachedEndOfSource = false; + while (samplesRead < count) + { + if (soundTouch.NumberOfSamplesAvailable == 0) + { + var readFromSource = sourceProvider.Read(sourceReadBuffer, 0, sourceReadBuffer.Length); + if (readFromSource > 0) + { + soundTouch.PutSamples(sourceReadBuffer, readFromSource / channelCount); + } + else + { + reachedEndOfSource = true; + // we've reached the end, tell SoundTouch we're done + soundTouch.Flush(); + } + } + var desiredSampleFrames = (count - samplesRead) / channelCount; + + var received = soundTouch.ReceiveSamples(soundTouchReadBuffer, desiredSampleFrames) * channelCount; + // use loop instead of Array.Copy due to WaveBuffer + for (int n = 0; n < received; n++) + { + buffer[offset + samplesRead++] = soundTouchReadBuffer[n]; + } + if (received == 0 && reachedEndOfSource) break; + } + return samplesRead; + } + + public WaveFormat WaveFormat => sourceProvider.WaveFormat; + + public float PlaybackRate + { + get + { + return playbackRate; + } + set + { + if (playbackRate != value) + { + UpdatePlaybackRate(value); + playbackRate = value; + } + } + } + + private void UpdatePlaybackRate(float value) + { + if (value != 0) + { + if (currentSoundTouchProfile.UseTempo) + { + soundTouch.SetTempo(value); + } + else + { + soundTouch.SetRate(value); + } + } + } + + public void Dispose() + { + soundTouch.Dispose(); + } + + public void SetSoundTouchProfile(SoundTouchProfile soundTouchProfile) + { + if (currentSoundTouchProfile != null && + playbackRate != 1.0f && + soundTouchProfile.UseTempo != currentSoundTouchProfile.UseTempo) + { + if (soundTouchProfile.UseTempo) + { + soundTouch.SetRate(1.0f); + soundTouch.SetPitchOctaves(0f); + soundTouch.SetTempo(playbackRate); + } + else + { + soundTouch.SetTempo(1.0f); + soundTouch.SetRate(playbackRate); + } + } + this.currentSoundTouchProfile = soundTouchProfile; + soundTouch.SetUseAntiAliasing(soundTouchProfile.UseAntiAliasing); + soundTouch.SetUseQuickSeek(soundTouchProfile.UseQuickSeek); + } + + public void Reposition() + { + repositionRequested = true; + } + } +} \ No newline at end of file diff --git a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs index b0ab2eb0..4e3fade9 100644 --- a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs +++ b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/ViewModels/AudioPlayerToolViewerViewModel.cs @@ -135,6 +135,16 @@ public float MusicVolume } } + public float MusicSpeed + { + get => IoC.Get().MusicSpeed; + set + { + IoC.Get().MusicSpeed = value; + NotifyOfPropertyChange(() => MusicSpeed); + } + } + public bool IsAudioButtonEnabled => AudioPlayer is not null; public AudioPlayerToolViewerViewModel() diff --git a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml index 8d2b9c0e..f78094cf 100644 --- a/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml +++ b/OngekiFumenEditor/Modules/AudioPlayerToolViewer/Views/AudioPlayerToolViewerView.xaml @@ -256,6 +256,7 @@ + Value="{Binding MusicSpeed}"> + @@ -102,6 +103,8 @@ + + @@ -142,9 +145,6 @@ runtime;compile; build; native; contentfiles; analyzers; buildtransitive - - - diff --git a/OngekiFumenEditor/Properties/AudioSetting.Designer.cs b/OngekiFumenEditor/Properties/AudioSetting.Designer.cs index 2b5dd136..fd55e476 100644 --- a/OngekiFumenEditor/Properties/AudioSetting.Designer.cs +++ b/OngekiFumenEditor/Properties/AudioSetting.Designer.cs @@ -94,5 +94,29 @@ public bool EnableSoundMultiPlay { this["EnableSoundMultiPlay"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool EnableVarspeed { + get { + return ((bool)(this["EnableVarspeed"])); + } + set { + this["EnableVarspeed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50")] + public int VarspeedReadDurationMs { + get { + return ((int)(this["VarspeedReadDurationMs"])); + } + set { + this["VarspeedReadDurationMs"] = value; + } + } } } diff --git a/OngekiFumenEditor/Properties/AudioSetting.settings b/OngekiFumenEditor/Properties/AudioSetting.settings index f06f73f2..b39fa391 100644 --- a/OngekiFumenEditor/Properties/AudioSetting.settings +++ b/OngekiFumenEditor/Properties/AudioSetting.settings @@ -20,5 +20,11 @@ True + + False + + + 50 + \ No newline at end of file