diff --git a/lib/mpeg_audio_frame_parser/frame.ex b/lib/mpeg_audio_frame_parser/frame.ex index 3ee857b..180d47f 100644 --- a/lib/mpeg_audio_frame_parser/frame.ex +++ b/lib/mpeg_audio_frame_parser/frame.ex @@ -20,50 +20,35 @@ defmodule MPEGAudioFrameParser.Frame do when is_binary(header) and bit_size(header) == @header_length do - frame = %Frame{data: header} - |> Map.put(:version_id, parse_version(header)) - |> Map.put(:layer, parse_layer(header)) - |> Map.put(:crc_protection, parse_crc_protection(header)) - |> Map.put(:bitrate, parse_bitrate(header)) - |> Map.put(:sample_rate, parse_sample_rate(header)) - |> Map.put(:padding, parse_padding(header)) - - %{frame | valid: header_valid?(frame), length: frame_length(frame)} + with version_id = parse_version(header), + layer = parse_layer(header), + crc_protection = parse_crc_protection(header), + bitrate = parse_bitrate(header), + sample_rate = parse_sample_rate(header), + padding = parse_padding(header), + valid = valid?(version_id, layer, bitrate, sample_rate), + frame_length = frame_length(version_id, layer, bitrate, sample_rate, padding) + do + %Frame{ + data: header, + version_id: version_id, + layer: layer, + crc_protection: crc_protection, + bitrate: bitrate, + sample_rate: sample_rate, + padding: padding, + valid: valid, + length: frame_length, + } + end end - def header_valid?(%Frame{version_id: version_id, layer: layer, bitrate: bitrate, sample_rate: sample_rate}) - when version_id != :reserved - and layer != :reserved - and bitrate != :bad - and sample_rate != :bad - do - true - end - - def header_valid?(%Frame{}), do: false - - def frame_length(%Frame{bitrate: bitrate, sample_rate: sample_rate} = frame) - when is_integer(bitrate) - and is_integer(sample_rate) - do - bits_per_frame = samples_per_frame(frame) / 8 - (bits_per_frame * (frame.bitrate * 1000) / frame.sample_rate + frame.padding) - |> trunc - end - - def frame_length(%Frame{}), do: 0 - def add_bytes(frame, packet) do - limit = bytes_missing(frame) + limit = frame.length - byte_size(frame.data) |> max(0) {:ok, bytes, rest} = split_packet(packet, limit) {:ok, %{frame | data: frame.data <> bytes}, rest} end - def bytes_missing(frame) do - (frame_length(frame) - byte_size(frame.data)) - |> max(0) - end - # Private Functions defp split_packet(packet, limit) do @@ -202,9 +187,31 @@ defmodule MPEGAudioFrameParser.Frame do defp parse_padding(<<@sync_word::size(11), _::size(11), padding_bit::size(1), _::bits>>), do: padding_bit - defp samples_per_frame(%Frame{layer: :layer1}), do: 384 - defp samples_per_frame(%Frame{layer: :layer2}), do: 1152 - defp samples_per_frame(%Frame{layer: :layer3, version_id: :version1}), do: 1152 - defp samples_per_frame(%Frame{layer: :layer3, version_id: _}), do: 576 - defp samples_per_frame(%Frame{}), do: 0 + defp samples_per_frame(_, :layer1), do: 384 + defp samples_per_frame(_, :layer2), do: 1152 + defp samples_per_frame(:version1, :layer3), do: 1152 + defp samples_per_frame(_, :layer3), do: 576 + defp samples_per_frame(_, _), do: 0 + + defp valid?(version_id, layer, bitrate, sample_rate) + when version_id != :reserved + and layer != :reserved + and bitrate != :bad + and sample_rate != :bad + do + true + end + + defp valid?(_version_id, _layer, _bitrate, _sample_rate), do: false + + defp frame_length(version_id, layer, bitrate, sample_rate, padding) + when is_integer(bitrate) + and is_integer(sample_rate) + do + bits_per_frame = samples_per_frame(version_id, layer) / 8 + (bits_per_frame * (bitrate * 1000) / sample_rate + padding) + |> trunc + end + + defp frame_length(_version_id, _layer, _bitrate, _sample_rate, _padding), do: 0 end diff --git a/test/mpeg_audio_frame_parser/frame_test.exs b/test/mpeg_audio_frame_parser/frame_test.exs index f5dfe21..d6adaf3 100644 --- a/test/mpeg_audio_frame_parser/frame_test.exs +++ b/test/mpeg_audio_frame_parser/frame_test.exs @@ -1,7 +1,7 @@ defmodule MPEGAudioFrameParser.FrameTest do use ExUnit.Case alias MPEGAudioFrameParser.Frame - import Frame, only: [from_header: 1, header_valid?: 1, frame_length: 1, bytes_missing: 1] + import Frame, only: [from_header: 1] # MP3, 128kbps, no CRC protection, 44100hz, no padding, stereo @header1 <<0b11111111111_11_01_0_1001_00_0_0_00_00_0_0_00::size(32)>> @@ -80,25 +80,21 @@ defmodule MPEGAudioFrameParser.FrameTest do end test "header validity" do - assert from_header(@header1) |> header_valid? - assert from_header(@header2) |> header_valid? - assert from_header(@header3) |> header_valid? - assert from_header(@header4) |> header_valid? - assert from_header(@header5) |> header_valid? - assert from_header(@header6) |> header_valid? - refute from_header(@header7) |> header_valid? + assert from_header(@header1).valid + assert from_header(@header2).valid + assert from_header(@header3).valid + assert from_header(@header4).valid + assert from_header(@header5).valid + assert from_header(@header6).valid + refute from_header(@header7).valid end test "frame_length" do - assert from_header(@header1) |> frame_length == 417 - assert from_header(@header2) |> frame_length == 768 - assert from_header(@header3) |> frame_length == 105 - assert from_header(@header4) |> frame_length == 835 - assert from_header(@header5) |> frame_length == 208 - assert from_header(@header6) |> frame_length == 360 - end - - test "bytes missing" do - assert from_header(@header1) |> bytes_missing == 413 + assert from_header(@header1).length == 417 + assert from_header(@header2).length == 768 + assert from_header(@header3).length == 105 + assert from_header(@header4).length == 835 + assert from_header(@header5).length == 208 + assert from_header(@header6).length == 360 end end