// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}