Packages
livebook
0.14.0-rc.0
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/helpers/codec_helpers.ex
defmodule LivebookWeb.CodecHelpers do
@wav_header_size 44
@doc """
Returns the size of a WAV binary that would wrap PCM data of the
given size.
This size matches the result of `encode_pcm_as_wav_stream!/6`.
"""
@spec pcm_as_wav_size(pos_integer()) :: pos_integer()
def pcm_as_wav_size(pcm_size) do
@wav_header_size + pcm_size
end
@doc """
Encodes PCM float-32 in native endianness into a WAV binary.
Accepts a range of the WAV binary that should be returned. Returns
a stream, where the PCM binary is streamed from the given file.
"""
@spec encode_pcm_as_wav_stream!(
Path.t(),
non_neg_integer(),
pos_integer(),
pos_integer(),
non_neg_integer(),
pos_integer()
) :: Enumerable.t()
def encode_pcm_as_wav_stream!(path, file_size, num_channels, sampling_rate, offset, length) do
header_enum =
if offset < @wav_header_size do
header = encode_pcm_as_wav_header(file_size, num_channels, sampling_rate)
header_length = min(@wav_header_size - offset, length)
header_slice = binary_slice(header, offset, header_length)
[header_slice]
else
[]
end
file_offset = max(offset - @wav_header_size, 0)
file_stream = File.stream!(path, 64_000, [{:read_offset, file_offset}])
file_stream =
case System.endianness() do
:little ->
file_stream
:big ->
Stream.map(file_stream, fn binary ->
for <<x::32-float-big <- binary>>, reduce: <<>> do
acc -> <<acc::binary, x::32-float-little>>
end
end)
end
Stream.concat(header_enum, file_stream)
end
defp encode_pcm_as_wav_header(pcm_size, num_channels, sampling_rate) do
# See http://soundfile.sapp.org/doc/WaveFormat
num_frames = div(pcm_size, 4)
bytes_per_sample = 4
block_align = num_channels * bytes_per_sample
byte_rate = sampling_rate * block_align
data_size = num_frames * block_align
<<
"RIFF",
36 + data_size::32-unsigned-integer-little,
"WAVE",
"fmt ",
16::32-unsigned-integer-little,
# 3 indicates 32-bit float PCM
3::16-unsigned-integer-little,
num_channels::16-unsigned-integer-little,
sampling_rate::32-unsigned-integer-little,
byte_rate::32-unsigned-integer-little,
block_align::16-unsigned-integer-little,
bytes_per_sample * 8::16-unsigned-integer-little,
"data",
data_size::32-unsigned-integer-little
>>
end
@doc """
Builds a single binary with JSON-serialized `meta` and `binary`.
"""
@spec encode_annotated_binary!(term(), binary()) :: binary()
def encode_annotated_binary!(meta, binary) do
meta = Jason.encode!(meta)
meta_size = byte_size(meta)
<<meta_size::size(32), meta::binary, binary::binary>>
end
@doc """
Decodes binary annotated with JSON-serialized metadata.
"""
@spec decode_annotated_binary!(binary()) :: {term(), binary()}
def decode_annotated_binary!(raw) do
<<meta_size::size(32), meta::binary-size(meta_size), binary::binary>> = raw
meta = Jason.decode!(meta)
{meta, binary}
end
end