// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // Model of a HandBrake Audio Track and it's associated behaviours. // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.Model.Audio { using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using Caliburn.Micro; using HandBrake.ApplicationServices.Interop; using HandBrake.ApplicationServices.Interop.Model; using HandBrake.ApplicationServices.Interop.Model.Encoding; using Newtonsoft.Json; using Services.Encode.Model.Models; using Utilities; /// /// Model of a HandBrake Audio Track and it's associated behaviours. /// public class AudioBehaviourTrack : PropertyChangedBase { private int bitrate; private double drc; private AudioEncoder encoder; private int gain; private HBMixdown mixDown; private double sampleRate; private bool isDefault; private IEnumerable bitrates; private IEnumerable encoderQualityValues; private AudioEncoderRateType encoderRateType; private double? quality; private IEnumerable mixdowns; /// /// Initializes a new instance of the class. /// public AudioBehaviourTrack() { // Default Values this.Encoder = AudioEncoder.ffaac; this.MixDown = HandBrakeEncoderHelpers.Mixdowns.FirstOrDefault(m => m.ShortName == "dpl2"); this.SampleRate = 48; this.Bitrate = 160; this.DRC = 0; this.EncoderRateType = AudioEncoderRateType.Bitrate; this.SetupLimits(); } /// /// Initializes a new instance of the class. /// Copy Constructor /// /// /// The track. /// public AudioBehaviourTrack(AudioBehaviourTrack track) { this.bitrate = track.Bitrate; this.drc = track.DRC; this.encoder = track.Encoder; this.gain = track.Gain; this.mixDown = track.MixDown; this.sampleRate = track.SampleRate; this.Quality = track.Quality; this.encoderRateType = track.EncoderRateType; this.SetupLimits(); } #region Track Properties /// /// Gets or sets Dynamic Range Compression /// public double DRC { get { return this.drc; } set { if (!Equals(value, this.drc)) { this.drc = value; this.NotifyOfPropertyChange(() => this.DRC); } } } /// /// Gets or sets the Gain for the audio track /// public int Gain { get { return this.gain; } set { if (!Equals(value, this.gain)) { this.gain = value; this.NotifyOfPropertyChange(() => this.Gain); } } } /// /// Gets or sets Audio Mixdown (ShortName) /// public HBMixdown MixDown { get { return this.IsPassthru ? null : this.mixDown; } set { if (!object.Equals(this.mixDown, value)) { this.mixDown = value; this.NotifyOfPropertyChange(() => this.MixDown); this.SetupLimits(); } } } /// /// Gets or sets Audio Encoder /// public AudioEncoder Encoder { get { return this.encoder; } set { if (object.Equals(this.encoder, value)) { return; } this.encoder = value; this.NotifyOfPropertyChange(() => this.Encoder); this.NotifyOfPropertyChange(() => this.IsPassthru); this.NotifyOfPropertyChange(() => this.IsBitrateVisible); this.NotifyOfPropertyChange(() => this.IsQualityVisible); this.NotifyOfPropertyChange(() => this.IsRateTypeVisible); this.NotifyOfPropertyChange(() => this.TrackReference); this.GetDefaultMixdownIfNull(); this.SetupLimits(); // Refresh the available encoder rate types. this.NotifyOfPropertyChange(() => this.AudioEncoderRateTypes); if (!this.AudioEncoderRateTypes.Contains(this.EncoderRateType)) { this.EncoderRateType = AudioEncoderRateType.Bitrate; // Default to bitrate. } } } /// /// Gets or sets Audio SampleRate /// public double SampleRate { get { return this.sampleRate; } set { this.sampleRate = value; this.NotifyOfPropertyChange(() => this.SampleRate); this.SetupLimits(); } } /// /// Gets or sets the encoder rate type. /// public AudioEncoderRateType EncoderRateType { get { return this.encoderRateType; } set { this.encoderRateType = value; this.SetupLimits(); this.NotifyOfPropertyChange(() => this.EncoderRateType); this.NotifyOfPropertyChange(() => this.IsBitrateVisible); this.NotifyOfPropertyChange(() => this.IsQualityVisible); if (!this.Quality.HasValue) { HBAudioEncoder hbAudioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(this.Encoder)); this.Quality = HandBrakeEncoderHelpers.GetDefaultQuality(hbAudioEncoder); } } } /// /// Gets or sets Audio Bitrate /// public int Bitrate { get { return this.bitrate; } set { this.bitrate = value; this.NotifyOfPropertyChange(() => this.Bitrate); } } /// /// Gets or sets Audio quality /// public double? Quality { get { return this.quality; } set { this.quality = value; this.NotifyOfPropertyChange(() => this.quality); } } #endregion /// /// Gets AudioEncoderDisplayValue. /// [JsonIgnore] public string AudioEncoderDisplayValue { get { return EnumHelper.GetDisplay(this.Encoder); } } /// /// Gets the The UI display value for bit rate /// [JsonIgnore] public string BitRateDisplayValue { get { if (this.Encoder == AudioEncoder.Ac3Passthrough || this.Encoder == AudioEncoder.DtsPassthrough || this.Encoder == AudioEncoder.DtsHDPassthrough) { return "Auto"; } return this.Bitrate.ToString(); } } /// /// Gets or sets a value indicating whether is default. /// TODO - Can this be removed? May have been added as a quick fix for a styling quirk. /// [JsonIgnore] public bool IsDefault { get { return this.isDefault; } set { this.isDefault = value; } } /// /// Gets or sets the The UI display value for sample rate /// [JsonIgnore] public string SampleRateDisplayValue { get { return this.SampleRate == 0 ? "Auto" : this.SampleRate.ToString(CultureInfo.InvariantCulture); } set { // TODO change this to be a converted field if (string.IsNullOrEmpty(value)) { return; } double samplerate; double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out samplerate); this.SampleRate = samplerate; } } /// /// Gets a value indicating whether IsPassthru. /// [JsonIgnore] public bool IsPassthru { get { if (this.Encoder == AudioEncoder.Ac3Passthrough || this.Encoder == AudioEncoder.DtsPassthrough || this.Encoder == AudioEncoder.DtsHDPassthrough || this.Encoder == AudioEncoder.AacPassthru || this.Encoder == AudioEncoder.Mp3Passthru || this.Encoder == AudioEncoder.Passthrough || this.Encoder == AudioEncoder.EAc3Passthrough || this.Encoder == AudioEncoder.TrueHDPassthrough || this.Encoder == AudioEncoder.FlacPassthru) { return true; } return false; } } /// /// Gets the bitrates. /// [JsonIgnore] public IEnumerable Bitrates { get { return this.bitrates; } } [JsonIgnore] public IEnumerable Mixdowns { get { return this.mixdowns; } } /// /// Gets the quality compression values. /// [JsonIgnore] public IEnumerable EncoderQualityValues { get { return this.encoderQualityValues; } } /// /// Gets the audio encoder rate types. /// [JsonIgnore] public IEnumerable AudioEncoderRateTypes { get { IList types = EnumHelper.GetEnumList().ToList(); HBAudioEncoder hbaenc = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(this.Encoder)); if (hbaenc == null || !hbaenc.SupportsQuality) { types.Remove(AudioEncoderRateType.Quality); } return types; } } /// /// Gets a value indicating whether can set bitrate. /// [JsonIgnore] public bool IsBitrateVisible { get { if (this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24) { return false; } return Equals(this.EncoderRateType, AudioEncoderRateType.Bitrate); } } /// /// Gets a value indicating whether is quality visible. /// [JsonIgnore] public bool IsQualityVisible { get { if (this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24) { return false; } return Equals(this.EncoderRateType, AudioEncoderRateType.Quality); } } /// /// Gets a value indicating whether is rate type visible. /// [JsonIgnore] public bool IsRateTypeVisible { get { if (this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24) { return false; } return true; } } /// /// Gets a value indicating whether IsLossless. /// [JsonIgnore] public bool IsLossless { get { return this.IsPassthru || this.Encoder == AudioEncoder.ffflac || this.Encoder == AudioEncoder.ffflac24; } } /// /// Gets TrackReference. /// [JsonIgnore] public AudioBehaviourTrack TrackReference { get { return this; } } #region Handler Methods /// /// The setup limits. /// private void SetupLimits() { this.SetupBitrateLimits(); this.SetupQualityCompressionLimits(); this.SetupMixdowns(); } /// /// The calculate bitrate limits. /// private void SetupBitrateLimits() { // Base set of bitrates available. List audioBitrates = HandBrakeEncoderHelpers.AudioBitrates; // Defaults int max = 256; int low = 32; // Based on the users settings, find the high and low bitrates. HBAudioEncoder hbaenc = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(this.Encoder)); HBRate rate = HandBrakeEncoderHelpers.AudioSampleRates.FirstOrDefault(t => t.Name == this.SampleRate.ToString(CultureInfo.InvariantCulture)); HBMixdown mixdown = this.mixDown ?? HandBrakeEncoderHelpers.GetMixdown("dpl2"); BitrateLimits limits = HandBrakeEncoderHelpers.GetBitrateLimits(hbaenc, rate != null ? rate.Rate : 48000, mixdown); if (limits != null) { max = limits.High; low = limits.Low; } // Return the subset of available bitrates. List subsetBitrates = audioBitrates.Where(b => b <= max && b >= low).ToList(); this.bitrates = subsetBitrates; this.NotifyOfPropertyChange(() => this.Bitrates); // If the subset does not contain the current bitrate, request the default. if (!subsetBitrates.Contains(this.Bitrate)) { this.Bitrate = HandBrakeEncoderHelpers.GetDefaultBitrate(hbaenc, rate != null ? rate.Rate : 48000, mixdown); } } /// /// The setup quality compression limits. /// private void SetupQualityCompressionLimits() { HBAudioEncoder hbAudioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(this.Encoder)); if (hbAudioEncoder.SupportsQuality) { RangeLimits limits = null; if (hbAudioEncoder.SupportsQuality) { limits = hbAudioEncoder.QualityLimits; } if (limits != null) { double value = limits.Ascending ? limits.Low : limits.High; List values = new List { value }; if (limits.Ascending) { while (value < limits.High) { value += limits.Granularity; values.Add(value); } } else { while (value > limits.Low) { value -= limits.Granularity; values.Add(value); } } this.encoderQualityValues = values; } else { this.encoderQualityValues = new List(); } } else { this.encoderQualityValues = new List(); } // Default the audio quality value if it's out of range. if (Equals(this.EncoderRateType, AudioEncoderRateType.Quality)) { if (this.Quality.HasValue && !this.encoderQualityValues.Contains(this.Quality.Value)) { this.Quality = HandBrakeEncoderHelpers.GetDefaultQuality(hbAudioEncoder); } } this.NotifyOfPropertyChange(() => this.EncoderQualityValues); } /// /// Restrict the available mixdowns to those that the enocder actually supports. /// private void SetupMixdowns() { this.mixdowns = new BindingList(HandBrakeEncoderHelpers.Mixdowns.ToList()); HBAudioEncoder audioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(this.Encoder)); BindingList mixdownList = new BindingList(); foreach (HBMixdown mixdown in HandBrakeEncoderHelpers.Mixdowns) { if (HandBrakeEncoderHelpers.MixdownHasCodecSupport(mixdown, audioEncoder)) { mixdownList.Add(mixdown); } } this.mixdowns = new BindingList(mixdownList); this.NotifyOfPropertyChange(() => this.Mixdowns); // If the mixdown isn't supported, downgrade it to the best available. if (!this.Mixdowns.Contains(this.MixDown)) { this.MixDown = this.Mixdowns.LastOrDefault(); } } /// /// Set the default mixdown when the mixdown is null or "none" /// private void GetDefaultMixdownIfNull() { // if (this.ScannedTrack == null) // { // return; // } // HBAudioEncoder aencoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(this.encoder)); // HBMixdown currentMixdown = HandBrakeEncoderHelpers.GetMixdown(this.mixDown); // HBMixdown sanitisedMixdown = HandBrakeEncoderHelpers.SanitizeMixdown(currentMixdown, aencoder, (uint)this.ScannedTrack.ChannelLayout); // HBMixdown defaultMixdown = HandBrakeEncoderHelpers.GetDefaultMixdown(aencoder, (uint)this.ScannedTrack.ChannelLayout); // if (this.mixDown == null || this.mixDown == "none") // { // this.MixDown = defaultMixdown.ShortName; // } // else if (sanitisedMixdown != null) // { // this.MixDown = sanitisedMixdown.ShortName; // } } #endregion } }