Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

【Ruby入門】FFIモジュールの使い方を解説!VLCライブラリでMP3を再生してみよう

まず、シンプルな質問に答えたいと思います…

RubyにおけるFFIとは何か?

FFIは「Foreign Function Interface(外部関数インターフェース)」の略です。

これは他のプログラミング言語で定義された関数を利用するための仕組みです。

RubyのFFIモジュールを使えば、通常ではアクセスできない外部ライブラリやコードを扱えるようになります。特にC言語のコードと連携する際によく使われています。

FFIを活用している代表的なgemには以下のようなものがあります:

  • rb-inotify(ファイルシステムの変更監視)
  • ruby-vips(高速な画像処理)
  • ruby-nfc(NFCタグの読み取り)

FFIモジュールを使えば、自分でも外部ライブラリを活用したプログラムを書くことができます。

どのように書くのか?

実際のコード例を見ていきましょう!

FFIの基本:ライブラリのロードと関数のインポート

FFIは、自分で作成したモジュールにその機能を組み込むことで利用できます。

次のように記述します:

require 'ffi'

module A
  extend FFI::Library
end

続いて、モジュール内でffi_libメソッドを使ってライブラリをロードします。

C言語のライブラリは、Windowsでいう「DLL」やLinuxでいう「SO」ファイルのように、「シンボル」と呼ばれる関数定義の一覧をエクスポートしており、他のプログラムから利用できるようになっています。

ここでは、C言語の標準ライブラリである「libc」を読み込んでみましょう。

方法は以下の通りです:

module A
  extend FFI::Library
  ffi_lib 'c'
end

これで準備完了です:

このライブラリから特定の関数を、Rubyのメソッドとしてインポートできるようになります。そのために使うのがattach_functionメソッドです。

実際の例を見てみましょう:

module A
  extend FFI::Library
  ffi_lib 'c'

  attach_function :strlen, [:string], :int
end

A.strlen("abc")
# => 3

これはどう動いているのでしょうか?

まず、C言語にはstrlenという関数があり、引数としてchar *を受け取ることが分かっています。char *はRubyではStringに相当します。

そして…

strlen関数は、文字列の長さを戻り値として返します。

つまり、attach_functionは3つのパラメータを受け取ります:

  1. C関数の名前
  2. 引数の型を表す配列
  3. 戻り値の型

各関数がどんな型を使うのかは、ドキュメントを読んで確認する必要があります。

それでは、もう一つ例を見てみましょう!

自作メディアプレイヤーを作ってみよう

遊んでみると面白いライブラリがたくさん見つかります。

Linuxでは、多くのライブラリが/usr/lib/ディレクトリ配下にあり、「lib」で始まり「so」という拡張子で終わる名前が付けられています。

次のコマンドで一覧表示できます:

ls /usr/lib/lib*.so

この例では、VLCのメディアプレイヤーライブラリを使用します:

/usr/lib/libvlc.so

目標は、mp3ファイルをロードして再生することです!

まずはここから始めましょう:

require 'ffi'

module VLC
  extend FFI::Library
  ffi_lib 'vlc'

  attach_function :libvlc_get_version, [], :string
end

これにより、ライブラリが正しくロードされているかをテストし、バージョンを表示できます。

実行すると次のようになります:

VLC.libvlc_get_version
# => "3.0.6 Vetinari"

4番目のパラメータを追加することで、これらのメソッドにカスタム名を付けることもできます。

:

attach_function :get_version, :libvlc_get_version, [], :string

そうすると、こう呼び出せます:

VLC.get_version

さて、mp3を再生するには何が必要でしょうか?

ドキュメントを確認すると、libvlc_media_player_play関数が使えそうです。

この関数には、メディアプレイヤーの「オブジェクト」が必要です。さらに、メディアプレイヤーにはメディアが必要で、メディアにはVLCインスタンスが必要になります。

処理の流れは以下の通りです:

VLC instance -> media -> media_player -> play

私はlibVLCの公式ドキュメントを読んで、この流れを組み立てました。

Rubyでメモリポインタ?!

C言語にはオブジェクトという概念がなく、すべてがメモリポインタによって処理されます。

メモリポインタとは、データが格納されたメモリアドレスのことです。

コード例を見てみましょう:

module VLC
  extend FFI::Library
  ffi_lib 'vlc'

  attach_function :get_version, :libvlc_get_version, [], :string
  attach_function :new, :libvlc_new, [:int, :int], :pointer
end

VLC.new(0, 0)
# => FFI::Pointer address=0x000055c6f04ae7b0

VLC.newを呼び出すと、ポインタが返されます。このポインタは、それを必要とする他のC関数へ渡していくことになります。

それでは、完全なコード例とその使い方を見ていきましょう。

module VLC
  extend FFI::Library
  ffi_lib 'vlc'

  attach_function :version, :libvlc_get_version, [], :string

  attach_function :new, :libvlc_new, [:int, :int], :pointer
  attach_function :libvlc_media_new_path, [:pointer, :string], :pointer
  attach_function :libvlc_media_player_new_from_media, [:pointer], :pointer

  attach_function :play, :libvlc_media_player_play, [:pointer], :int
  attach_function :stop, :libvlc_media_player_stop, [:pointer], :int
  attach_function :pause, :libvlc_media_player_pause, [:pointer], :int
end

これだけで音楽を再生できます:

vlc    = VLC.new(0, 0)
media  = VLC.libvlc_media_new_path(vlc, "/home/jesus/Downloads/meditation.mp3")
player = VLC.libvlc_media_player_new_from_media(media)

VLC.play(player)

これで、GUIを一切開かずに音楽が再生されます。この上に独自のWebインターフェースを構築することも可能です。ぜひ試してみてください!

まとめ

今回は、FFIの驚くべき力について学びました。外部ライブラリを、まるでRuby言語の一部であるかのように扱える強力なモジュールです。

この記事をぜひシェアして、より多くの人に楽しんでもらえたら嬉しいです🙂

最後までお読みいただきありがとうございました!

  1. Rubyでパーサーを自作する方法!StringScannerを使った実装手順を徹底解説

    パース(構文解析)とは、文字列の集まりから意味を読み取り、プログラムが扱える形のデータへと変換する技術です。正規表現でも文字列の解析は可能ですが、すべての場面に適しているわけではありません。 たとえば、正規表現でHTMLを解析するのはあまり良い方法ではないというのは、プログラミング界隈ではよく知られた話です。 Rubyにはnokogiriという強力なライブラリがあり、HTMLの解析はこれに任せられます。しかし、自分でパーサーを一から作ってみると、文字列処理や構文解析の仕組みについて多くのことを学べます。それでは早速始めていきましょう! Rubyでのパースの基本:StringScannerクラス

  2. Redis GEORADIUSBYMEMBERコマンドの使い方を実例付きで解説 – Redisチュートリアル

    このチュートリアルでは、Redisに保存された地理空間データ(ジオスペーシャル値)の中から、特定の範囲内に含まれる要素を取得する方法を学びます。そのために使用するのが GEORADIUSBYMEMBER コマンドです。 GEORADIUSBYMEMBERコマンドとは GEORADIUSBYMEMBERコマンドは、キーに保存された地理空間値(ソート済みセット)のメンバーのうち、指定したメンバーの経度・緯度と半径の引数から算出される円形エリアの境界内にある1つ以上のメンバーを返すために使用します。このエリアは、指定したメンバーの経度・緯度を円の中心位置とし、指定した単位による半径を円の半径として計