Packages
vtc
0.16.5
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.9
0.16.8
0.16.7
0.16.6
0.16.5
0.16.4
0.16.2
0.16.1
0.16.0
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.15
0.13.14
0.13.13
0.13.12
0.13.11
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.10
0.10.9
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
A SMPTE timecode library for Elixir
Current section
Files
Jump to
Current section
Files
lib/sources/frames/feet_and_frames.ex
defmodule Vtc.Source.Frames.FeetAndFrames do
@moduledoc """
Holds Feet+Frames information.
## Fields
- `feet`: The amount of film in Feet that would run through the camera in a given
amount of time.
- `feet`: The number of frames left over after `feet` of film has run.
- `film_format`: The type of film this value represents. Default: `:ff35mm_4perf`.
## String Conversion
[FeetAndFrames](`Vtc.Source.Frames.FeetAndFrames`) can be converted into a string
through the `String.Chars.to_string/1` function.
## Examples
```elixir
iex> alias Vtc.Source.Frames.FeetAndFrames
iex>
iex> String.Chars.to_string(%FeetAndFrames{feet: 10, frames: 4})
"10+04"
```
"""
alias Vtc.FilmFormat
alias Vtc.Framestamp
alias Vtc.Utils.Parse
@enforce_keys [:feet, :frames]
defstruct [:feet, :frames, film_format: :ff35mm_4perf]
@typedoc """
Contains only a single field for wrapping the underlying string.
"""
@type t() :: %__MODULE__{feet: integer(), frames: integer(), film_format: FilmFormat.t()}
@ff_regex ~r/(?P<negative>-)?(?P<feet>[0-9]+)\+(?P<frames>[0-9]+)/
@doc """
Parses a `FeetAndFrames` value from a string.
"""
@spec from_string(String.t(), film_format: FilmFormat.t()) :: {:ok, t()} | {:error, Framestamp.ParseError.t()}
def from_string(ff_string, opts \\ []) do
film_format = Keyword.get(opts, :film_format, :ff35mm_4perf)
with {:ok, groups} <- Parse.apply_regex(@ff_regex, ff_string) do
negative? = Map.fetch!(groups, "negative") == "-"
multiplier = if negative?, do: -1, else: 1
feet = groups |> Map.fetch!("feet") |> String.to_integer()
feet = feet * multiplier
frames = groups |> Map.fetch!("frames") |> String.to_integer()
frames = frames * multiplier
{:ok, %__MODULE__{feet: feet, frames: frames, film_format: film_format}}
end
end
@doc """
Parses a `FeetAndFrames` value from a string.
"""
@spec from_string!(String.t(), film_format: FilmFormat.t()) :: t()
def from_string!(ff_string, opts \\ []) do
case from_string(ff_string, opts) do
{:ok, parsed} -> parsed
{:error, error} -> raise error
end
end
@doc false
@spec from_framestamp(
Framestamp.t(),
opts :: [film_format: FilmFormat.t(), round: Framestamp.round()]
) :: t()
def from_framestamp(framestamp, opts) do
film_format = Keyword.get(opts, :film_format, :ff35mm_4perf)
frames_opts = Keyword.take(opts, [:round])
perfs_per_foot = FilmFormat.perfs_per_foot(film_format)
perfs_per_frame = FilmFormat.perfs_per_frame(film_format)
total_frames = Framestamp.frames(framestamp, frames_opts)
perfs = perfs_per_frame * total_frames
feet = div(perfs, perfs_per_foot)
frames = perfs |> rem(perfs_per_foot) |> div(perfs_per_frame)
%__MODULE__{feet: feet, frames: frames, film_format: film_format}
end
end
defimpl String.Chars, for: Vtc.Source.Frames.FeetAndFrames do
alias Vtc.Source.Frames.FeetAndFrames
@spec to_string(FeetAndFrames.t()) :: binary()
def to_string(feet_frames) do
sign = if feet_frames.feet < 0, do: "-", else: ""
feet = feet_frames.feet |> abs() |> Integer.to_string()
frames = feet_frames.frames |> abs() |> Integer.to_string() |> String.pad_leading(2, "0")
"#{sign}#{feet}+#{frames}"
end
end
defimpl Inspect, for: Vtc.Source.Frames.FeetAndFrames do
alias Vtc.Source.Frames.FeetAndFrames
@spec inspect(FeetAndFrames.t(), Inspect.Opts.t()) :: String.t()
def inspect(feet_frames, _), do: "<#{feet_frames} #{inspect(feet_frames.film_format)}>"
end
defimpl Vtc.Source.Frames, for: Vtc.Source.Frames.FeetAndFrames do
@moduledoc """
Implements [Seconds](`Vtc.Source.Seconds`) protocol for Premiere ticks.
"""
alias Vtc.FilmFormat
alias Vtc.Framerate
alias Vtc.Source.Frames
alias Vtc.Source.Frames.FeetAndFrames
@spec frames(FeetAndFrames.t(), Framerate.t()) :: Frames.result()
def frames(feet_frames, rate) do
perfs_per_foot = FilmFormat.perfs_per_foot(feet_frames.film_format)
perfs_per_frame = FilmFormat.perfs_per_frame(feet_frames.film_format)
perfs = feet_frames.feet * perfs_per_foot + feet_frames.frames * perfs_per_frame
frames = div(perfs, perfs_per_frame)
Frames.frames(frames, rate)
end
end