Slide 1

Slide 1 text

バイナリ読むのにElixirしてみた 2024-10-01

Slide 2

Slide 2 text

自己紹介 ひむら ともひこ 最近してること ● 音声合成入門

Slide 3

Slide 3 text

バイナリ解読にElixir

Slide 4

Slide 4 text

経緯 ● PythonとRustで書き込んだ音声ファイルのそれぞれ挙動が違う ● スピーカーから片方しか音がでてない ● なにが違うのか ○ 書き込み処理を見比べる ○ 書き込まれたファイルを見比べる

Slide 5

Slide 5 text

ファイルの中身をみてみるか

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

眠いときだっので 目と脳がきつい

Slide 8

Slide 8 text

ツールを使おう

Slide 9

Slide 9 text

$ nix-shell -p sox $ soxi output.wav Input File : 'output.wav' Channels : 1 Sample Rate : 24000 Precision : 25-bit Duration : 00:00:00.00 = 1 samples ~ 0.003125 CDDA sectors File Size : 956k Bit Rate : 32bit Sample Encoding: 32-bit Floating Point PCM

Slide 10

Slide 10 text

特に問題が見当たらない

Slide 11

Slide 11 text

自分で書いてみるかー

Slide 12

Slide 12 text

そういえば Erlangはバイナリのパターンマッチができる

Slide 13

Slide 13 text

Elixirでやってみるか

Slide 14

Slide 14 text

その前に基本 << 1 :: 32 >> # => << 0, 0, 0, 1 >> << 1 :: 16 >> # => << 0, 1 >> << 1 :: 8 >> # => << 1 >> << 1 :: 4 >> # => << 1::size(4) >> << 1 :: 2 >> # => << 1::size(2) >> <<1::size(2)>> # => << 1::size(2) >> << 256 :: 32 >> # => << 0, 0, 1, 0 >>

Slide 15

Slide 15 text

例 真ん中の2byteだけ取り出したい << _::8, a::16, _::8 >> = << 256::32 >> # => <<0, 0, 1, 0>> a # => 1 << _::8, a::16, _ >> = << 512::32 >> # => <<0, 0, 2, 0>> a # => 2

Slide 16

Slide 16 text

例 文字列に対しても << a::bitstring-8, b::bitstring-16, _ >> = "goro" a # => “g” b # => “or”

Slide 17

Slide 17 text

例 文字列2 << a::bitstring-8, 111::8, b::bitstring-16 >> = "goro" a #=> g b # => ro # 以下のような書き方もできる << a::bitstring-8, ?o, b::bitstring-16 >> = "goro" << a::bitstring-8, “o”, b::bitstring-16 >> = "goro"

Slide 18

Slide 18 text

例 case def x(v) do case v do << a::bitstring-8, “o”, b :: bitstring-16 >> -> [a,b] << a ::bitstring-8, “a”, b :: bitstring-8 >> -> [a,b] end end x “goro” # => [“g”, “ro”] x “gao” # => [“g”, “o”]

Slide 19

Slide 19 text

WAVの構造を確認しながら 書いてみた

Slide 20

Slide 20 text

filename = Enum.at System.argv, 0 {:ok, data} = File.read filename << id :: bitstring-32, chunk_size :: integer-little-32, riff_type :: bitstring-32, rest >> = data IO.puts """ groupID: #{id} churk size: #{chunk_size} byte (filesize #{chunk_size+8}) riff type: #{riff_type} """

Slide 21

Slide 21 text

case rest do << “fmt “, # 先頭が “fmt “ で始まっていて… format_chunk_size :: integer-little-32, format_tag :: integer-little-16, channel :: unsigned-integer-little-16, sample_per_sec :: integer-little-32, avg_byte_per_sec :: integer-little-32, block_align :: integer-little-16, bits_per_sample :: integer-little-16, _ >> -> IO.puts """ chunkID: fmt format chunk size: #{format_chunk_size} byte format tag: #{format_tag} #{B.tag format_tag} channel: #{B.channel channel} #{channel} sample per sec: #{sample_per_sec} Hz byte per sec: #{avg_byte_per_sec} block align: #{block_align} bits per sample(bitrate): #{bits_per_sample} """ << “fact”, … >> -> # “fact” だったら... end

Slide 22

Slide 22 text

groupID: RIFF churk size: 956210 byte (filesize 956218) riff type: WAVE chunkID: fmt format chunk size: 18 byte format tag: 3 IEEE float channel: mono 1 sample per sec: 24000 Hz byte per sec: 96000 block align: 4 bits per sample(bitrate): 32

Slide 23

Slide 23 text

まとめ

Slide 24

Slide 24 text

まとめというか振り返り - ErlangVMは通信を得意とするのでビット列を扱いやすいはず - 使いやすそうだった - ビット列をパターンマッチできる - 便利さがわかるところまで触れなかった - でも、試す良い機会になった - 今回の用途では構造体をつらつら書くのと大差ない感じになってしまった - 別にElixirでなくても簡単にかけただろう - とはいえ、新しい道具を試すのは大切

Slide 25

Slide 25 text

で問題は解決したのか? ● 昨夜書いてみただけなので、まだ解決はしていない

Slide 26

Slide 26 text

参考文献とか - https://hexdocs.pm/elixir/Kernel.SpecialForms.html#%3C%3C%3 E%3E/1-types - 指定できるtypeの確認に -