Experimental refactor to a more functional style
This commit is contained in:
parent
395a6881ef
commit
d7d9b2e079
|
@ -20,50 +20,35 @@ defmodule MPEGAudioFrameParser.Frame do
|
||||||
when is_binary(header)
|
when is_binary(header)
|
||||||
and bit_size(header) == @header_length
|
and bit_size(header) == @header_length
|
||||||
do
|
do
|
||||||
frame = %Frame{data: header}
|
with version_id = parse_version(header),
|
||||||
|> Map.put(:version_id, parse_version(header))
|
layer = parse_layer(header),
|
||||||
|> Map.put(:layer, parse_layer(header))
|
crc_protection = parse_crc_protection(header),
|
||||||
|> Map.put(:crc_protection, parse_crc_protection(header))
|
bitrate = parse_bitrate(header),
|
||||||
|> Map.put(:bitrate, parse_bitrate(header))
|
sample_rate = parse_sample_rate(header),
|
||||||
|> Map.put(:sample_rate, parse_sample_rate(header))
|
padding = parse_padding(header),
|
||||||
|> Map.put(:padding, parse_padding(header))
|
valid = valid?(version_id, layer, bitrate, sample_rate),
|
||||||
|
frame_length = frame_length(version_id, layer, bitrate, sample_rate, padding)
|
||||||
%{frame | valid: header_valid?(frame), length: frame_length(frame)}
|
|
||||||
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
|
do
|
||||||
true
|
%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{}), 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
|
end
|
||||||
|
|
||||||
def frame_length(%Frame{}), do: 0
|
|
||||||
|
|
||||||
def add_bytes(frame, packet) do
|
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, bytes, rest} = split_packet(packet, limit)
|
||||||
{:ok, %{frame | data: frame.data <> bytes}, rest}
|
{:ok, %{frame | data: frame.data <> bytes}, rest}
|
||||||
end
|
end
|
||||||
|
|
||||||
def bytes_missing(frame) do
|
|
||||||
(frame_length(frame) - byte_size(frame.data))
|
|
||||||
|> max(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Private Functions
|
# Private Functions
|
||||||
|
|
||||||
defp split_packet(packet, limit) do
|
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 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(_, :layer1), do: 384
|
||||||
defp samples_per_frame(%Frame{layer: :layer2}), do: 1152
|
defp samples_per_frame(_, :layer2), do: 1152
|
||||||
defp samples_per_frame(%Frame{layer: :layer3, version_id: :version1}), do: 1152
|
defp samples_per_frame(:version1, :layer3), do: 1152
|
||||||
defp samples_per_frame(%Frame{layer: :layer3, version_id: _}), do: 576
|
defp samples_per_frame(_, :layer3), do: 576
|
||||||
defp samples_per_frame(%Frame{}), do: 0
|
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
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
defmodule MPEGAudioFrameParser.FrameTest do
|
defmodule MPEGAudioFrameParser.FrameTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
alias MPEGAudioFrameParser.Frame
|
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
|
# 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)>>
|
@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
|
end
|
||||||
|
|
||||||
test "header validity" do
|
test "header validity" do
|
||||||
assert from_header(@header1) |> header_valid?
|
assert from_header(@header1).valid
|
||||||
assert from_header(@header2) |> header_valid?
|
assert from_header(@header2).valid
|
||||||
assert from_header(@header3) |> header_valid?
|
assert from_header(@header3).valid
|
||||||
assert from_header(@header4) |> header_valid?
|
assert from_header(@header4).valid
|
||||||
assert from_header(@header5) |> header_valid?
|
assert from_header(@header5).valid
|
||||||
assert from_header(@header6) |> header_valid?
|
assert from_header(@header6).valid
|
||||||
refute from_header(@header7) |> header_valid?
|
refute from_header(@header7).valid
|
||||||
end
|
end
|
||||||
|
|
||||||
test "frame_length" do
|
test "frame_length" do
|
||||||
assert from_header(@header1) |> frame_length == 417
|
assert from_header(@header1).length == 417
|
||||||
assert from_header(@header2) |> frame_length == 768
|
assert from_header(@header2).length == 768
|
||||||
assert from_header(@header3) |> frame_length == 105
|
assert from_header(@header3).length == 105
|
||||||
assert from_header(@header4) |> frame_length == 835
|
assert from_header(@header4).length == 835
|
||||||
assert from_header(@header5) |> frame_length == 208
|
assert from_header(@header5).length == 208
|
||||||
assert from_header(@header6) |> frame_length == 360
|
assert from_header(@header6).length == 360
|
||||||
end
|
|
||||||
|
|
||||||
test "bytes missing" do
|
|
||||||
assert from_header(@header1) |> bytes_missing == 413
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue