lib/membrane_aac_format/aac.ex

defmodule Membrane.AAC do
  @moduledoc """
  Capabilities for [Advanced Audio Codec](https://wiki.multimedia.cx/index.php/Understanding_AAC).
  """

  @type profile_t :: :main | :LC | :SSR | :LTP | :HE | :HEv2
  @type mpeg_version_t :: 2 | 4
  @type samples_per_frame_t :: 1024 | 960

  @typedoc """
  Indicates whether stream contains AAC frames only or are they encapsulated
  in [ADTS](https://wiki.multimedia.cx/index.php/ADTS)
  """
  @type encapsulation_t :: :none | :ADTS

  @typedoc """
  Identifiers of [MPEG Audio Object Types](https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Audio_Object_Types)
  """
  @type audio_object_type_id_t :: 1..5 | 29

  @typedoc """
  Identifiers of [MPEG Audio sampling frequencies](https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Sampling_Frequencies)
  """
  @type sampling_frequency_id_t :: 0..12 | 15

  @typedoc """
  Identifiers of [MPEG Audio channel configurations](https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Channel_Configurations)
  """
  @type channel_config_id_t :: 0..7

  @typedoc """
  AAC frame length identifiers.

  `0` indicates 1024 samples/frame and `1` - 960 samples/frame.
  """
  @type frame_length_id_t :: 0 | 1

  @type t :: %__MODULE__{
          profile: profile_t,
          mpeg_version: mpeg_version_t,
          sample_rate: pos_integer,
          channels: pos_integer,
          samples_per_frame: 1024 | 960,
          frames_per_buffer: pos_integer,
          encapsulation: encapsulation_t
        }

  @enforce_keys [
    :profile,
    :sample_rate,
    :channels
  ]
  defstruct @enforce_keys ++
              [
                mpeg_version: 2,
                samples_per_frame: 1024,
                frames_per_buffer: 1,
                encapsulation: :none
              ]

  @audio_object_type BiMap.new(%{
                       1 => :main,
                       2 => :LC,
                       3 => :SSR,
                       4 => :LTP,
                       5 => :HE,
                       29 => :HEv2
                     })

  @sampling_frequency BiMap.new(%{
                        0 => 96000,
                        1 => 88200,
                        2 => 64000,
                        3 => 48000,
                        4 => 44100,
                        5 => 32000,
                        6 => 24000,
                        7 => 22050,
                        8 => 16000,
                        9 => 12000,
                        10 => 11025,
                        11 => 8000,
                        12 => 7350,
                        15 => :explicit
                      })

  @channel_config BiMap.new(%{
                    0 => :AOT_specific,
                    1 => 1,
                    2 => 2,
                    3 => 3,
                    4 => 4,
                    5 => 5,
                    6 => 6,
                    7 => 8
                  })

  @frame_length BiMap.new(%{
                  0 => 1024,
                  1 => 960
                })

  @spec aot_id_to_profile(audio_object_type_id_t) :: profile_t
  def aot_id_to_profile(audio_object_type_id),
    do: BiMap.fetch!(@audio_object_type, audio_object_type_id)

  @spec profile_to_aot_id(profile_t) :: audio_object_type_id_t
  def profile_to_aot_id(profile), do: BiMap.fetch_key!(@audio_object_type, profile)

  @spec sampling_frequency_id_to_sample_rate(sampling_frequency_id_t) :: pos_integer
  def sampling_frequency_id_to_sample_rate(sampling_frequency_id),
    do: BiMap.fetch!(@sampling_frequency, sampling_frequency_id)

  @spec sample_rate_to_sampling_frequency_id(sample_rate :: pos_integer | :explicit) ::
          sampling_frequency_id_t
  def sample_rate_to_sampling_frequency_id(sample_rate),
    do: BiMap.fetch_key!(@sampling_frequency, sample_rate)

  @spec channel_config_id_to_channels(channel_config_id_t) :: pos_integer | :AOT_specific
  def channel_config_id_to_channels(channel_config_id),
    do: BiMap.fetch!(@channel_config, channel_config_id)

  @spec channels_to_channel_config_id(channels :: pos_integer | :AOT_specific) ::
          channel_config_id_t
  def channels_to_channel_config_id(channels), do: BiMap.fetch_key!(@channel_config, channels)

  @spec frame_length_id_to_samples_per_frame(frame_length_id_t) :: samples_per_frame_t
  def frame_length_id_to_samples_per_frame(frame_length_id),
    do: BiMap.fetch!(@frame_length, frame_length_id)

  @spec samples_per_frame_to_frame_length_id(samples_per_frame_t) :: pos_integer
  def samples_per_frame_to_frame_length_id(samples_per_frame),
    do: BiMap.fetch_key!(@frame_length, samples_per_frame)
end