defmodule SuperCollider.SynthDef.Parser do
@moduledoc """
This is a helper function to parse values from a .scsyndef file.
"""
## PARSER HELPERS
@doc """
Helper function for parsing pstrings.
A pstring is SuperColliders string format, which starts with a 8-bit integer holding the length of the string (`string_length`), followed by a binary of `string_length` with the string data.
Returns a tuple with string as the first element, and the remainder of the binary data as the second parameter, e.g.:
`{int_value, binary_data}`.
"""
def parse_pstring(binary) do
<<
string_length::big-integer-8,
string_value::binary-size(string_length),
rest::binary
>> = binary
{string_value, rest}
end
@doc """
Helper function for parsing 32 bit integers.
Returns a tuple with a 32-big integer as the first element, and the remainder of the binary data as the second parameter, e.g.:
`{int_value, binary_data}`.
"""
def parse_integer_32(binary) do
<<
value::big-signed-32,
rest::binary
>> = binary
{value, rest}
end
@doc """
Helper function for parsing 16 bit integers.
Returns a tuple with a 16-bit integer as the first element, and the remainder of the binary data as the second parameter, e.g.:
`{int_value, binary_data}`.
"""
def parse_integer_16(binary) do
<<
value::big-signed-16,
rest::binary
>> = binary
{value, rest}
end
@doc """
Helper function for parsing multiple big-float-32s in a sequence.
* binary: hold the binary data
* number: number of floats to parse in a sequenece
Parsed floats are rounded to 3 decimal places.
Returns a tuple with a list of the floats as the first element, and the remainder of the binary data as the second parameter, e.g.:
`{float_list, binary_data}`.
"""
def parse_floats(binary, number) do
parse_floats(binary, number, 0, [])
end
defp parse_floats(binary, number, const_index, acc) when const_index < number do
<<constant_value::big-float-32, rest_binary::binary>> = binary
# constant = {const_index, constant_value |> Float.round(3)}
constant = constant_value |> Float.round(3)
parse_floats(rest_binary, number, const_index + 1, [constant] ++ acc)
end
defp parse_floats(binary, _number, _const_index, acc) do
{acc |> Enum.reverse(), binary}
end
@doc """
Helper function for parsing multiple key-value pairs in a sequence, where the key is a string and the value is an integer.
* binary: hold the binary data
* number: number of key-integer value pairs to parse in a sequenece.
Returns a tuple with a list of the floats as the first element, and the remainder of the binary data as the second parameter, e.g.:
`{float_list, binary_data}`.
"""
def parse_name_integer_pairs(binary, number) do
parse_name_integer_pairs(binary, number, 0, [])
end
defp parse_name_integer_pairs(binary, number, count, acc) when count < number do
<<
param_name_length::big-integer-8,
param_name::binary-size(param_name_length),
param_index_value::big-integer-32,
rest_binary::binary
>> = binary
# param = %{_enum_index: count, parameter_name: param_name, parameter_index: param_index_value}
param = %{parameter_name: param_name, parameter_index: param_index_value}
parse_name_integer_pairs(rest_binary, number, count + 1, [param] ++ acc)
end
defp parse_name_integer_pairs(binary, _number, _count, acc) do
{acc |> Enum.reverse(), binary}
end
@doc """
Helper function for parsing multiple key-value pairs in a sequence, where the key is a string and the value is a a float.
* binary: hold the binary data
* number: number of key-float value pairs to parse in a sequenece.
Returns a tuple with a list of the floats as the first element, and the remainder of the binary data as the second parameter, e.g.:
`{float_list, binary_data}`.
"""
def parse_name_float_pairs(binary, number) do
parse_name_float_pairs(binary, [], number)
end
defp parse_name_float_pairs(binary, acc, number) when number > 0 do
<<
name_length::big-integer-8,
name::binary-size(name_length),
index_value::big-float-32,
rest_binary::binary
>> = binary
param = %{name: name, index_value: index_value |> Float.round(3)}
parse_name_float_pairs(rest_binary, [param] ++ acc, number-1)
end
defp parse_name_float_pairs(binary, acc, 0) do
{acc |> Enum.reverse(), binary}
end
end