How to Play MP3 Files With Ruby, VLC & The FFI Module


I want to answer one simple question…

What is FFI in Ruby?

FFI stands for “Foreign Function Interface”.

It’s a way to use functions defined in other programming languages.

This gives you access to libraries & code that otherwise you would not have.

In Ruby, we often use this to work with C code.

Examples include:

You can write your own programs that use FFI with the help of the FFI module.

Let’s see some code examples!

FFI Basics: Loading & Importing Functions

You can work with FFI by including its functions into a module you create.

Like this:

require 'ffi' module A extend FFI::Library
end

Then, inside the module, we are going to load a library using the ffi_lib method.

A C library, like a “DLL” in Windows, or “SO” in Linux, exports a list of “symbols”, or function definitions that a program makes available for others to use.

Let’s require “libc“, C’s standard library.

Here’s how:

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

Now:

You can import specific functions from this library as Ruby methods.

With the attach_function method.

Here’s an example:

module A extend FFI::Library ffi_lib 'c' attach_function :strlen, [:string], :int
end A.strlen("abc")
# 3

How does this work?

First, we know that there is a function in C called strlen, this function takes chars * as an argument.

chars * translates to String in Ruby.

Then…

This strlen function returns a value, which is the length of the string.

In other words, attach_function takes 3 parameters:

  1. Name of the C function
  2. An array of argument types
  3. A return type

You’ll have to read the documentation to find out which types a particular function works with.

Time for another example!

Build Your Own Media Player

You can find many interesting libraries to play with.

In Linux, most are found under the /usr/lib/ directory, starting with “lib” in their name & ending with the “so” extension.

List them like this:

ls /usr/lib/lib*.so

For this example, we’re going to use VLC’s media player library:

/usr/lib/libvlc.so

Our goal is to load an mp3 file & play it!

Let’s start with this:

require 'ffi' module VLC extend FFI::Library ffi_lib 'vlc' attach_function :libvlc_get_version, [], :string
end

This allows you to test if the library is loading correctly & then print its version.

Like this:

VLC.libvlc_get_version
# "3.0.6 Vetinari"

You can give these methods a custom name by adding a 4th parameter.

Example:

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

Then you can do:

VLC.get_version

What do we need to play an mp3 now?

If we look at the documentation, the libvlc_media_player_play function looks like the way to go.

This function needs a media player “object”.

A media player needs a media, and a media needs a VLC instance.

Here’s the process:

VLC instance -> media -> media_player -> play

I pieced this together by reading the documentation for libVLC.

Memory Pointers in Ruby???

In C you’ll not find any objects, everything is handled with memory pointers.

A memory pointer is a memory address with data.

Let’s see a code example:

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

As a result of calling VLC.new we get a pointer.

We have to pass it along to other C functions that need it.

Now:

The full example & how to use it.

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

That’s enough to play some music:

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)

This will play the music, without opening any kind of visual interface.

You could build your own web interface on top of this.

Give it a try!

Summary

You’ve learned about the amazing power of FFI! A Ruby module that allows you to use external libraries as if they were part of the language itself.

Please share this article so more people can enjoy it 🙂

Thanks for reading!