diff --git a/BassAudio.Tests/Integration/AudioEncoderTests.cs b/BassAudio.Tests/Integration/AudioEncoderTests.cs index 57e8c10b..568f853c 100644 --- a/BassAudio.Tests/Integration/AudioEncoderTests.cs +++ b/BassAudio.Tests/Integration/AudioEncoderTests.cs @@ -218,6 +218,27 @@ public async Task Start_Bitrate_CreateFileOfSize(EncodeFormat format, int bitrat var fileLength = FileSystem.FileInfo.New(file.Destination).Length; Output.WriteLine(fileLength.ToString()); } + + [Theory] + [InlineData(0)] + [InlineData(32)] + [InlineData(128)] + [InlineData(256)] + [InlineData(320)] + public async Task Start_FixedBitrate_CreateFileOfSize(int bitrate) + { + var file = CreateSourceShort(EncodeFormat.Mp3); + FileSystem.DeleteFileSilent(file.Destination); + Settings.Format = EncodeFormat.Mp3; + Settings.FixedBitrate = true; + Settings.Bitrate = bitrate; + + await Model.StartAsync(file, Settings); + + Assert.True(FileSystem.File.Exists(file.Destination)); + var fileLength = FileSystem.FileInfo.New(file.Destination).Length; + Output.WriteLine(fileLength.ToString()); + } [Theory] [MemberData(nameof(GetAllSampleRates))] diff --git a/BassAudio/AudioEncoder.cs b/BassAudio/AudioEncoder.cs index e44371dd..ee7cc8fe 100644 --- a/BassAudio/AudioEncoder.cs +++ b/BassAudio/AudioEncoder.cs @@ -3,6 +3,7 @@ using ManagedBass.Enc; using ManagedBass.Fx; using ManagedBass.Mix; + // ReSharper disable StringLiteralTypo namespace HanumanInstitute.BassAudio; @@ -46,10 +47,10 @@ public void Start(ProcessingItem file, EncodeSettings settings, CancellationToke { file.Pitch ??= _pitchDetector.GetPitch(file.Path); } - var pitch = settings.AutoDetectPitch ? - settings.PitchTo / file.Pitch!.Value : + var pitch = settings.AutoDetectPitch ? + settings.PitchTo / file.Pitch!.Value : settings.Pitch; - + // Create channel. Bass.Configure(Configuration.SRCQuality, 4); Bass.Configure(Configuration.FloatDSP, true); @@ -59,12 +60,12 @@ public void Start(ProcessingItem file, EncodeSettings settings, CancellationToke try { var tags = new TagsReader(chan); - + var chanInfo = Bass.ChannelGetInfo(chan); var bitrate = (int)Bass.ChannelGetAttribute(chan, ChannelAttribute.Bitrate); bitrate = BitrateRoundUp(bitrate); var length = Bass.ChannelGetLength(chan); - + // Add mix effect. var sampleRate = settings.SampleRate > 0 ? settings.SampleRate : chanInfo.Frequency; sampleRate = EnsureSampleRateValid(settings.Format, sampleRate); @@ -83,7 +84,7 @@ public void Start(ProcessingItem file, EncodeSettings settings, CancellationToke // Bass.ChannelSetAttribute(chan, ChannelAttribute.Tempo, (1.0 / pitch * settings.Speed - 1.0) * 100.0); // Bass.ChannelSetAttribute(chan, ChannelAttribute.TempoFrequency, // chanInfo.Frequency * pitch * settings.Rate); - + // Optimized pitch shifting for increased quality // 1. Rate shift to Output * Pitch (rounded) // 2. Resample to Output (48000Hz) @@ -183,11 +184,13 @@ private string GetOptions(EncodeSettings settings, int sourceBitrate, ChannelInf return settings.Format switch { - EncodeFormat.Mp3 => $"--abr {bitrate} -q {settings.Mp3QualitySpeed} --add-id3v2" - .AddTag("--tt", tags.Title).AddTag("--ta", tags.Artist).AddTag("--tl", tags.Album).AddTag("--ty", tags.Year) + EncodeFormat.Mp3 => (settings.FixedBitrate ? "-b" : "--abr") + $" {bitrate} -q {settings.Mp3QualitySpeed} --add-id3v2" + .AddTag("--tt", tags.Title).AddTag("--ta", tags.Artist).AddTag("--tl", tags.Album) + .AddTag("--ty", tags.Year) .AddTag("--tc", tags.Comment).AddTag("--tn", tags.Track).AddTag("--tg", tags.Genre), EncodeFormat.Flac => $"--compression-level-{settings.FlacCompression}" - .AddTag("-T title=", tags.Title).AddTag("-T artist=", tags.Artist).AddTag("-T album=", tags.Album).AddTag("--date", tags.Year) + .AddTag("-T title=", tags.Title).AddTag("-T artist=", tags.Artist).AddTag("-T album=", tags.Album) + .AddTag("--date", tags.Year) .AddTag("-T comment=", tags.Comment).AddTag("-T tracknum=", tags.Track).AddTag("-T genre=", tags.Genre), EncodeFormat.Ogg => $"--bitrate {bitrate}" .AddTag("--title", tags.Title).AddTag("--artist", tags.Artist).AddTag("--album", tags.Album).AddTag("--date", tags.Year) diff --git a/BassAudio/Models/EncodeSettings.cs b/BassAudio/Models/EncodeSettings.cs index d3f570db..105746ae 100644 --- a/BassAudio/Models/EncodeSettings.cs +++ b/BassAudio/Models/EncodeSettings.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using ReactiveUI; namespace HanumanInstitute.BassAudio; @@ -20,11 +20,17 @@ public class EncodeSettings : ReactiveObject [Reactive] public int Bitrate { get; set; } + /// + /// Gets or sets whether to encode as fixed bitrate. + /// + [Reactive] + public bool FixedBitrate { get; set; } + /// /// Gets or sets the encoding bits per sample for WAV or FLAC formats. Valid values are 0 (auto), 8, 16, 24 or 32. /// [Reactive] - public int BitsPerSample { get; set; } + public int BitsPerSample { get; set; } = 16; /// /// Gets or sets the encoding sample rate. diff --git a/Common.Avalonia.App/Common.Avalonia.App.csproj b/Common.Avalonia.App/Common.Avalonia.App.csproj index 8bb8babf..ecb8b85d 100644 --- a/Common.Avalonia.App/Common.Avalonia.App.csproj +++ b/Common.Avalonia.App/Common.Avalonia.App.csproj @@ -49,12 +49,5 @@ - - - - - AboutView.axaml - Code - diff --git a/Common.Avalonia/Converters/BooleanToBrushConverter.cs b/Common.Avalonia/Converters/BooleanToBrushConverter.cs index 7e0dbfc9..a60d3b32 100644 --- a/Common.Avalonia/Converters/BooleanToBrushConverter.cs +++ b/Common.Avalonia/Converters/BooleanToBrushConverter.cs @@ -3,7 +3,7 @@ namespace HanumanInstitute.Common.Avalonia; /// -/// Converts boolean values to string while allowing to configure true and false values. +/// Converts boolean values to brush while allowing to configure true and false values. /// public sealed class BooleanToBrushConverter : BooleanConverter { diff --git a/Converter432Hz/ViewModels/MainViewModel.cs b/Converter432Hz/ViewModels/MainViewModel.cs index 4b975b96..1827111c 100644 --- a/Converter432Hz/ViewModels/MainViewModel.cs +++ b/Converter432Hz/ViewModels/MainViewModel.cs @@ -45,6 +45,8 @@ public MainViewModel(ISettingsProvider settings, IAppUpdateServ _isBitrateVisible = this.WhenAnyValue(x => x.FormatsList.SelectedValue, x => x != EncodeFormat.Flac && x != EncodeFormat.Wav) .ToProperty(this, x => x.IsBitrateVisible); + _isToggleBitrateVisible = this.WhenAnyValue(x => x.FormatsList.SelectedValue, x => x == EncodeFormat.Mp3) + .ToProperty(this, x => x.IsToggleBitrateVisible); _isBitsPerSampleVisible = this.WhenAnyValue(x => x.FormatsList.SelectedValue, x => x == EncodeFormat.Flac || x == EncodeFormat.Wav) .ToProperty(this, x => x.IsBitsPerSampleVisible); _isQualitySpeedVisible = this.WhenAnyValue(x => x.FormatsList.SelectedValue, x => x == EncodeFormat.Mp3 || x == EncodeFormat.Flac) @@ -95,6 +97,12 @@ private void Bind(Expression> expr1, Expression public bool IsBitrateVisible => _isBitrateVisible.Value; private readonly ObservableAsPropertyHelper _isBitrateVisible; + /// + /// Gets or sets whether Toggle Bitrate button should be visible. + /// + public bool IsToggleBitrateVisible => _isToggleBitrateVisible.Value; + private readonly ObservableAsPropertyHelper _isToggleBitrateVisible; + /// /// Gets whether Bits Per Sample control should be visible. /// @@ -256,6 +264,16 @@ private void FillSampleRateList(EncodeFormat format) { FileExistsAction.Rename, "Rename" }, { FileExistsAction.Cancel, "Cancel" } }; + + /// + /// Enables or disables the Fixed Bitrate option. + /// + public RxCommandUnit ToggleFixedBitrate => _toggleFixedBitrate ??= ReactiveCommand.Create(ToggleFixedBitrateImpl); + private RxCommandUnit? _toggleFixedBitrate; + private void ToggleFixedBitrateImpl() + { + Settings.Encode.FixedBitrate = !Settings.Encode.FixedBitrate; + } /// /// Starts the batch encoding job. diff --git a/Converter432Hz/Views/MainView.axaml b/Converter432Hz/Views/MainView.axaml index 6c8c3585..f4b87976 100644 --- a/Converter432Hz/Views/MainView.axaml +++ b/Converter432Hz/Views/MainView.axaml @@ -18,6 +18,7 @@ d:DataContext="{x:Static local:ViewModelLocator.Main}" x:DataType="vm:MainViewModel"> + @@ -101,7 +102,12 @@ - + + +