Skip to content

Commit

Permalink
Add button to normalize BGM volume (#454)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonko0493 authored Jan 25, 2025
1 parent 12d9503 commit d8b98b3
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/SerialLoops.Lib/Items/BackgroundMusicItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,4 @@ public IWaveProvider GetWaveProvider(ILogger log, bool loop)
}
}
}
}
}
10 changes: 9 additions & 1 deletion src/SerialLoops.Lib/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using HaruhiChokuretsuLib.Font;
using HaruhiChokuretsuLib.Util;
using HaruhiChokuretsuLib.Util.Exceptions;
using NAudio.Wave;
using SerialLoops.Lib.Items;
using SerialLoops.Lib.Script.Parameters;
using SerialLoops.Lib.Util;
Expand Down Expand Up @@ -105,6 +106,8 @@ public partial class Project
public Dictionary<int, GraphicsFile> LayoutFiles { get; set; } = [];
[JsonIgnore]
public Dictionary<ChessFile.ChessPiece, SKBitmap> ChessPieceImages { get; private set; }
[JsonIgnore]
public float AverageBgmMaxAmplitude { get; private set; }

// Localization function to make localizing accessible from the lib
[JsonIgnore]
Expand Down Expand Up @@ -566,11 +569,16 @@ public LoadProjectResult LoadArchives(ILogger log, IProgressTracker tracker)
{
string[] bgmFiles = SoundDS.BgmSection.AsParallel().Where(bgm => bgm is not null).Select(bgm => Path.Combine(IterativeDirectory, "rom", "data", bgm)).ToArray();
tracker.Focus("BGM Tracks", bgmFiles.Length);
List<float> maxes = [];
Items.AddRange(bgmFiles.AsParallel().Select((bgm, i) =>
{
tracker.Finished++;
return new BackgroundMusicItem(bgm, i, this);
BackgroundMusicItem bgmItem = new(bgm, i, this);
maxes.Add(bgmItem.GetWaveProvider(log, false).GetMaxAmplitude());
return bgmItem;
}));

AverageBgmMaxAmplitude = maxes.Average();
}
catch (Exception ex)
{
Expand Down
20 changes: 20 additions & 0 deletions src/SerialLoops.Lib/Util/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using HaruhiChokuretsuLib.Archive.Event;
using HaruhiChokuretsuLib.Archive.Graphics;
using HaruhiChokuretsuLib.Font;
using NAudio.Wave;
using SerialLoops.Lib.Items;
using SerialLoops.Lib.Script.Parameters;
using SkiaSharp;
Expand All @@ -36,6 +37,25 @@ public static string GetGraphicInfoFile(this GraphicsFile grp)
return JsonSerializer.Serialize(new GraphicInfo(grp));
}

public static float GetMaxAmplitude(this IWaveProvider waveProvider)
{
return waveProvider.ToSampleProvider().GetMaxAmplitude();
}

public static float GetMaxAmplitude(this ISampleProvider sampleProvider)
{
float max = 0;
float[] buffer = new float[sampleProvider.WaveFormat.SampleRate];
int read;
do
{
read = sampleProvider.Read(buffer, 0, buffer.Length);
max = Math.Max(max, buffer.Max());
} while (read > 0);

return max;
}

public static string GetSubstitutedString(this string line, Project project)
{
if (project.LangCode != "ja")
Expand Down
9 changes: 9 additions & 0 deletions src/SerialLoops/Assets/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/SerialLoops/Assets/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3107,4 +3107,7 @@ This action is irreversible.</value>
<data name="FADED_GRAY" xml:space="preserve">
<value>Faded Gray</value>
</data>
<data name="Normalize" xml:space="preserve">
<value>Normalize</value><comment>As in "normalize the audio volume so that max volume is similar to all other audio volumes"</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using SerialLoops.Assets;
using SerialLoops.Lib.Util;
using SerialLoops.Lib.Util.WaveformRenderer;
using SerialLoops.Models;
using SerialLoops.ViewModels.Controls;
Expand All @@ -25,34 +26,52 @@ public class BgmVolumePropertiesDialogViewModel : ViewModelBase
[Reactive]
public SKBitmap Waveform { get; set; }
public SoundPlayerPanelViewModel VolumePreviewPlayer { get; set; }
[Reactive]
public double Volume { get; set; } = 100;
public ICommand VolumeSliderValueChangedCommand { get; set; }

private double _volume;
public double Volume
{
get => _volume;
set
{
this.RaiseAndSetIfChanged(ref _volume, value);
_wav.Seek(0, SeekOrigin.Begin);
VolumePreview.SetVolume(Volume);
_wav.Seek(0, SeekOrigin.Begin);
Waveform = WaveformRenderer.Render(VolumePreview.Provider, _waveLength, WaveFormRendererSettings.StandardSettings);
_wav.Seek(0, SeekOrigin.Begin);
VolumePreviewPlayer.Stop();
}
}
public ICommand NormalizeCommand { get; set; }
public ICommand SaveCommand { get; set; }
public ICommand CancelCommand { get; set; }

public BgmVolumePropertiesDialogViewModel(WaveStream wav, string title, ILogger log)
private readonly float _maxAmplitude;

public BgmVolumePropertiesDialogViewModel(WaveStream wav, string title, float maxAmplitude, ILogger log)
{
_log = log;
Title = string.Format(Strings._0____Adjust_Volume, title);
_wav = wav;
_volume = 100;
Waveform = WaveformRenderer.Render(wav, WaveFormRendererSettings.StandardSettings);
_waveLength = wav.Length;
VolumePreview = new(wav);
VolumePreviewPlayer = new(VolumePreview, _log, null);
_maxAmplitude = maxAmplitude;

VolumeSliderValueChangedCommand = ReactiveCommand.Create(VolumeSlider_ValueChanged);
NormalizeCommand = ReactiveCommand.Create(Normalize);
SaveCommand = ReactiveCommand.Create<BgmVolumePropertiesDialog>((dialog) => dialog.Close(VolumePreview));
CancelCommand = ReactiveCommand.Create<BgmVolumePropertiesDialog>((dialog) => dialog.Close());
}

private void VolumeSlider_ValueChanged()
private void Normalize()
{
_wav.Seek(0, SeekOrigin.Begin);
VolumePreview.SetVolume(Volume);
_wav.Seek(0, SeekOrigin.Begin);
Waveform = WaveformRenderer.Render(VolumePreview.Provider, _waveLength, WaveFormRendererSettings.StandardSettings);
_wav.Seek(0, SeekOrigin.Begin);
VolumePreviewPlayer.Stop();
float volumeNormalizationFactor = _maxAmplitude / VolumePreview.Provider.GetMaxAmplitude();
if (volumeNormalizationFactor < 1)
{
Volume *= volumeNormalizationFactor;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ private async Task AdjustVolume_Executed()
}
string volumeAdjustedWav = Path.Combine(Path.GetDirectoryName(_bgmCachedFile), $"{Path.GetFileNameWithoutExtension(_bgmCachedFile)}-volume.wav");
File.Copy(_bgmCachedFile, volumeAdjustedWav, true);
using WaveFileReader reader = new(volumeAdjustedWav);
BgmVolumePropertiesDialogViewModel volumeDialog = new(reader, Bgm.Name, _log);
await using WaveFileReader reader = new(volumeAdjustedWav);
BgmVolumePropertiesDialogViewModel volumeDialog = new(reader, Bgm.Name, _project.AverageBgmMaxAmplitude, _log);
BgmVolumePreviewItem volumePreview = await new BgmVolumePropertiesDialog { DataContext = volumeDialog }.ShowDialog<BgmVolumePreviewItem>(Window.Window);
if (volumePreview is not null)
{
Expand Down
18 changes: 7 additions & 11 deletions src/SerialLoops/Views/Dialogs/BgmVolumePropertiesDialog.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
xmlns:vm="using:SerialLoops.ViewModels.Dialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:i="using:Avalonia.Xaml.Interactivity"
xmlns:ia="using:Avalonia.Xaml.Interactions.Core"
xmlns:assets="using:SerialLoops.Assets"
xmlns:controls="using:SerialLoops.Controls"
xmlns:utility="using:SerialLoops.Utility"
Width="800" Height="400"
x:DataType="vm:BgmVolumePropertiesDialogViewModel"
x:Class="SerialLoops.Views.Dialogs.BgmVolumePropertiesDialog"
Title="{Binding Title}"
Expand All @@ -17,18 +16,15 @@
<Window.Resources>
<utility:IntAdditionConverter x:Key="AdditionConverter"/>
</Window.Resources>

<StackPanel Orientation="Vertical" Spacing="5" Margin="10">
<StackPanel Orientation="Horizontal" Spacing="5">
<controls:SoundPlayerPanel DataContext="{Binding VolumePreviewPlayer}"/>
<Slider Name="VolumeSlider" Minimum="0" Maximum="200" Height="{Binding Waveform.Height}"
Orientation="Vertical" Value="{Binding Volume}">
<i:Interaction.Behaviors>
<ia:EventTriggerBehavior EventName="ValueChanged" SourceObject="{Binding #VolumeSlider}">
<ia:InvokeCommandAction Command="{Binding VolumeSliderValueChangedCommand}"/>
</ia:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Slider>
<StackPanel Orientation="Vertical" Spacing="5">
<Slider Name="VolumeSlider" Minimum="0" Maximum="200" Height="{Binding Waveform.Height}"
Orientation="Vertical" Value="{Binding Volume}" />
<Button Content="{x:Static assets:Strings.Normalize}" Command="{Binding NormalizeCommand}"/>
</StackPanel>
<Image Source="{Binding Waveform, Converter={x:Static utility:SLConverters.SKBitmapToAvaloniaConverter}}"
Width="{Binding Waveform.Width}" Height="{Binding Waveform.Height}"/>
</StackPanel>
Expand Down

0 comments on commit d8b98b3

Please sign in to comment.