Bemix Notes

From SlugWiki
Revision as of 01:34, 5 February 2006 by Rob (Talk) (Player)

Jump to: navigation, search

Bemix is a web-based media player. It supports multiple soundcards and various networked client types, allowing a group to use a centralized server to control several speakers. Commands are issued through URLs, where the URL portion of the address represents the command to be issued, while the query portion of the URL passes in parameters to the server. Media is played via mplayer, enabling any media format to be played provided it is supported in mplayer.

Overview

Bemix requires python2.3, mplayer, and an instance of MySQL server running on the local machine to function properly. The MySQL server must contain a "bemix" database with tables "playlist" and "track." The server is designed to serve HTTP requests over port 9087. Any number of clients may gain access to the state of the bemix system and may issue commands to the server through query strings passed to the server in URIs. Communication between the server and clients is governed by the bemix client protocol.

The server acts as a central manager for any reasonable number of slaves that have joined with the server. The server itself supports multiple soundcards running on the same slave, which the individual slaves have the option of supporting or not supporting this feature. Communication between the server and the slaves are governed by the bemix slave protocol.

Philosophy and Design Goals

The central philosphy of the bemix system is that music should be a group experience. To this end, the bemix system was designed to support a variety of music sources and control interfaces. Fundamental to this effort is a simple and reliable message delivery system. HTTP was chosen as the method of choice for sending messages due to its relative simplicity, established dependability, and a prevalence of pre-existing HTTP libraries for a variety of environments. Communicating with any component in the bemix system is as simple as downloading or writing a webpage.

The server-slave architecture enables numerous remote machines to register themselves with a single server, thereby providing a single common interface for access to a wide variety of audio sources. Due to the simple nature of the server-slave architecture, a variety of slaves may be implemented. The base implementation utilizes mplayer for audio playback, but other slaves using xmms, winamp, or other audio players is possible as well. The only requirement for a slave is that it fully implement the bemix slave protocol.

The client-slave architecture is even more modularly designed, providing a variety of output formats for different types of clients. Currently html and plaintext 'client' output formats are planned, but as time permits an RDF output format is possible as well. Clients are designed to be either human or machine controlled. For instance, while a typical command-line client exists to allow users to manually send commands to the server, a computer-controlled alarm clock could easily be implemented as well which would instruct a specific client to play a specific playlist after a certain amount of time has elapsed. The server would have no internal notion of an "alarm clock," but assuming a bemix bot is capable of sending the appropriate URI to the server at the appropriate time, the server will respond to the command regardless of where it originates or how it is generated.

Bemix Server

Of all the components of the bemix system, the server is the least extensible or replaceable. This is likely not a drawback, however, as the bemix system is meant to allow a variety of slave and client types to communicate well with one another across a single server. For most situations, it is counter-productive for there to be multiple bemix servers to be active at the same time, becuase the central design goal of bemix is to encourage a communal media experience.

Source files of interest:

  • bemix/bemix-server.py - a small script that is run from the command line to begin the server processes
  • bemix/serverlib.py - a comprehensive library containing various classes and support code for running the bemix server, communicating with slaves, and formatting output for clients

Configuration

The bemix server needs an open port to listen on; by default this port is 9087. It also requires read and write access to a database named 'bemix' where it stores and loads various configuration parameters. In the future an external configuration file and/or a suite of configuration utilities are planned; currently all configuration must be done manually (and at times through direct manipulation of the source code).

Root Music Directory

The bemix server must have access to all the music that is available to any client on the bemix system. In other words, the server controls what music is and is not available to all the slaves connected to it. This decision ensures that a wide variety of music is available to all the slaves on the bemix system, and ensures a 'barrier-free' music experience, allowing clients to queue up any song onto any player associated with any slave on the system without needing to know what files are available to the specific slave in question. Therefore, the music source must be available to both the server and all of its slaves through a network filesystem such as NFS, AFS, or CIFS. The server requires the files to live in or to be nested below a single directory on the filesystem, although this requirement can be met by providing symlinks to audio files and directories residing anywhere in the local filesystem hierarchy. Setting up the music shares is outside the scope of this document.

It is important to note that the server does not actually transfer audio files to the slaves, and is not capable of providing audio files for download to clients. Each slave must be configured in a manner similar to the server; the slaves must have access to the shared audio files in their local filesystem hierarchy as well.

MySQL Table Schema

The bemix server utilizes a MySQL database to manage playlists, slaves, and other information about the configuration of the server. The following tables must be in place and accessible for the bemix-server to function properly.


Tables that are required by bemix:

+-----------------+
| Tables_in_bemix |
+-----------------+
| data            |
| playlist        |
| slave           |
| track           |
+-----------------+

The data table scheme:

This table stores various configuration variables that are required for the bemix server to function properly. Currently, the only defined variable is "rootDir" which has the value of the absolute path to the root music directory.

+---------------+-------------+------+-----+---------+-------+
| Field         | Type        | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| variable      | varchar(50) |      | PRI |         |       |
| variableValue | varchar(50) | YES  |     | NULL    |       |
+---------------+-------------+------+-----+---------+-------+

The playlist table scheme:

This table stores the name of each playlist that is created and automatically assigns each new playlist a unique id that is used as a foreign key the the track table, described below.

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| playlistId | int(11)      |      | PRI | NULL    | auto_increment |
| name       | varchar(100) |      |     |         |                |
+------------+--------------+------+-----+---------+----------------+

The track table scheme:

This table stores the individual tracks that make up a playlist. While each track is individually assigned a unique trackId, this value is currently not utilized by the bemix server. The field playlistId refers to the playlist with the same id in the playlist table, described above. The index of the song in the playlist is stored in the field trackNumber, while the file path to the actual song is stored in the file field. (This file path is relative to the rootDir defined in the data table.)

+-------------+---------+------+-----+---------+----------------+
| Field       | Type    | Null | Key | Default | Extra          |
+-------------+---------+------+-----+---------+----------------+
| trackId     | int(11) |      | PRI | NULL    | auto_increment |
| playlistId  | int(11) |      |     | 0       |                |
| trackNumber | int(11) |      |     | 0       |                |
| file        | text    | YES  |     | NULL    |                |
+-------------+---------+------+-----+---------+----------------+

The slave table scheme:

This table stores information about the slaves that are allowed to join the bemix server. It does not, however, contain any information about which slaves happen to be connected to the server at any particular time. TODO: the ip field currently stores a string of the form 'http://1.2.3.4:9088/' rather than a pure IP address.

+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| slaveId | int(11)      |      | PRI | NULL    | auto_increment |
| ip      | varchar(100) | YES  |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+

Bemix Client Protocol

The bemix client protocol is a one-way protocol that defines the form of messages sent to the server and the form of response sent to the client. The server is never allowed to communicate with the client unless it is in response to a client request. The client sends a command to the server by sending a GET request for a specified resource, and the server sends its reply in the body of the page that is sent in response to the client.

All commands are given to the server by a bemix client are sent as URIs with additional information passed in the query string. In the following documentation, all URIs are relative to the base server address (in our case http://bemix.mit.edu:9087/), and all URIs have an output=[html, client] element in their query string. An example URI that will instruct player 2 on slave 3 to begin playing the current playlist is http://bemix.mit.edu:9087/play?output=client&slaveId=3&playerId=2. The commands that may be sent to the server include getting the slaves that are currently connected, creating a new playlist, and instructing a specific player to mute itself.

In the following examples, a relative URI is listed indicating the form of the URI that is sent to the bemix server in order to accomplish a specific function. Listed after the URI are zero or more error codes along with a brief description of what can cause them. Following the error codes is sample bemix server output for a successful action in the plaintext 'client' output format. The query string indicates the type of the value of the variable as either int representing an integer or string representing an escaped sequence of utf-8 characters.

Player

A player represents a single soundcard connected to an individual slave. There is one player for every audio output device that the bemix server knows about. Before anything can be played on a player, a playlist must be created, filled with songs, and loaded on the player. When a player has finished playing a song, it should automatically play the next song sequentially in the current playlist. If the song that just finished was the last song in the playlist, the player should begin playing the first song in the current playlist. A client never communicates directly with a slave or player; all commands for a specific player are sent via the bemix server which behaves much like a proxy, forwarding the request to the specified slave.

Play

play?slaveId=int&playerId=int

  • no-playlist-loaded - there is no playlist loaded for the current player
  • invalid-playlistId - the playlist that is currently loaded is no longer valid
  • invalid-slaveId - the slaveId that was specified is not valid
  • invalid-playerId - the playerId that was specified is not valid
success=true

Instructs a player (referred to by its playerId) that is associated with a specific slave (referred to by its slaveId) to begin playing. If there is a playlist already loaded on the player, and the player is currently stopped, the player should begin playing from the beginning of the song that is stopped. If the player is paused, playback should be resumed. If the player is playing, the command should be ignored. The invalid-playlistId error arises if the playlist that is currently loaded on the player has been deleted and therefore cannot be played.

Pause

pause?slaveId=int&playerId=int

  • no-playlist-loaded - there is no playlist loaded for the current player
  • invalid-playlistId - the playlist that is currently loaded is no longer valid
  • invalid-slaveId - the slaveId that was specified is not valid
  • invalid-playerId - the playerId that was specified is not valid
success=true

Instructs a player to pause or resume the current song. If the player is currently playing then the song is paused. If the player is currently paused then the song is resumed ans begins playing from the position at which it was previously paused. If the player is stopped the command is ignored.

Stop

stop?slaveId=int&playerId=x

  • no-playlist-loaded - there is no playlist loaded for the current player
  • invalid-playlistId - the playlist that is currently loaded is no longer valid
  • invalid-slaveId - the slaveId that was specified is not valid
  • invalid-playerId - the playerId that was specified is not valid
success=true

Instructs a player to stop playing. If the player is currently playing or paused then playback stops and the position in the current song is reset to 0 so that if it is later played, playback begins from the beginning of the song. If the player is currently stopped then the command is ignored.


  • stop?playerId=x (stops playback on a player)
  • loadList?playerId=x&playlistId=y (loads and begins to play a playlist on a player)
  • seek?playerId=x&position=y (seeks the player to a certain position in a song, in seconds)
  • volume?playerId=x&level=y (sets the volume for a player to a level between 0-100)
  • next?playerId=x (moves to the next song in a playlist)
  • previous?playerId=x (moves to the previous song in a playlist)
  • player?playerId=x (gets the name, index of the loaded playlist, current index of the loaded playlist, current time of the current song, total time of the current song, and status of the player where 0=playing, 1=paused, 2=stopped)

Playlist

A playlist consists of a name and music files that are associated with it. The songs in a playlist are ordered with indices starting at 0. Before anything can be played, a playlist must be created and loaded onto a specific player.

  • create?name=x (creates a new playlist which is assigned a unique playlistId)
  • delete?playlistId=x (deletes a playlist from bemix)
  • add?playlistId=x&song=y (adds a song to a playlist)
  • remove?playlistId=x&index=y (removes a song at a certain index in a playlist from a playlist)
  • playlist?playlistId=x (gets the name and tracks from a playlist)

Browsing

These commands are used to navigate the filesystem on the server to determine which files are available for playing. All operations on files and directories are relative to the server's root music directory, so /A/Air may actually refer to /mnt/tunes/A/Air on the server.

  • browse?dir=x (returns directories and files that exist at relative path x)

General

These commands query the bemix server for generalized information about the state of the entire system.

  • slaves (lists all slaveIds that are currently available through the server)
  • playlists (lists all playlistIds available)

Results

Each command returns a data structure through HTTP that defines variable names and values. A (variable name, value) combination is referred to as a field. Every command contains at least one success field, whose value may be either true or false. If any tag is false, it should be assumed that the entire command failed. Each command may optionally return a comment field as well, whose value gives some indication of why an operation may have failed.

In addition, most commands return additional information about the state of a particular aspect of the bemix server.

Slave Commands

Slave commands are, in general, identical in form to their "server" counterparts defined above. However, there are additional commands which are defined for the bemix server to communicate with its slaves. The slave server understands all commands understood by the server that are listed under "Player", as well as the "players" command.

Additional Server Commands

The bemix server understands additional commands that are meant to be used only by bemix slaves. Clients should not use these commands themselves.

  • playlistSong?playlistId=x&index=y (gets the name of the song at a specified index in a given playlist)

Client Types

Bemix is capable of dumping data in multiple of formats. Currently only the "client" and "html" formats are planned, but XML/RDF formats are possibility in the future. The format of the export is indicated in the URL by the mandatory output parameter.

Client

The "client" output format returns data in a simple newline-delimited list. String values are not delimited by quote characters, and list items are delimited by commas (including a comma after the final element). The format of each line is name=value, where name is the name of the variable being returned, and value is the value of the variable. No whitespace characters are inserted between name, =, and value.

The order in which the fields are returned is not guaranteed, and it should not be assumed that all tags will be present or in any particular order.

The MIME type of the client output is 'text/plain'.

The result of the browse command http://slugwiki.mit.edu:8080/browse?output=client&dir=/P/Porcupine%20Tree

dir0=/P/Porcupine Tree/Coma Divine
dir1=/P/Porcupine Tree/Spiral Circus
dir2=/P/Porcupine Tree/In Absentia
dir3=/P/Porcupine Tree/Insignificance
dir4=/P/Porcupine Tree/Lightbulb Sun
dir5=/P/Porcupine Tree/Metanoia
dir6=/P/Porcupine Tree/On the Sunday of Life
dir7=/P/Porcupine Tree/Recordings
file0=/P/Porcupine Tree/Chloroform (Instrumental Rough Mix).mp3
file1=/P/Porcupine Tree/Disappear (Demo Version 2.0).mp3
file2=/P/Porcupine Tree/Is Not.mp3
file3=/P/Porcupine Tree/2003 Intro Music.mp3
file4=/P/Porcupine Tree/Even Less, Part 2.mp3
file5=/P/Porcupine Tree/London.mp3
success=true

The result of the playlists command http://slugwiki.mit.edu:8080/playlists?output=client

ids=32,30,31,
success=true

The result of the players command with an invalid playerId http://slugwiki.mit.edu:8080/player?output=client&playerId=2

success=false
comment=playerId not found
success=true

HTML

When dumping data as HTML, bemix creates pages that can also be used to issue commands to the server. For instance, a browse page generates a link for each directory that is being displayed that points to the browse page for that subdirectory.

HTML output is meant to be used through a webbrowser. If you're interested in manipulating the bemix server through some other means, use the "client" output format described above.

Todo

The code base needs to be cleaned up substantially before the 1.0 release of the software. Currently an alpha version, the code works, but is messy.

Beta Version

The beta version will contain various improvements over the current version. This version will add support for slave clients, and will permit slaves to drop in and out at will.

bemix-server

  • The handler for the bemix server parses the incoming url into an action, an output type, and a dictionary of variables. These are then given to manager.ServerManager.
  • Supports bemix-slaves from dropping in and out dynamically, so computers can be turned off and on without restarting the bemix system
  • Deals with browsing and playlist administration as well

manager.ServerManager

  • Maintains a list of available bemix-slave instances, their available players, and the available playlists
  • Contains a single method performAction(action, output="client", vars={}) that calls the appropriate method in the many command classes

bemixlib

  • Contains methods that are used by manager.ServerManager to perform actions that bemix-server responds to (methods accept an output type and the vars dictionary)

bemix-slave

  • All slaves must have access to the identical music shares (achieved externally via samba, afs, etc)
  • A running http server that responds to messages from the bemix-server it is registered with only (done via IP address checking)
  • Maintains internal state of volume, secondsElapsed, secondsTotal, status, index, and playlistId
  • Must query bemix-server to get the filename and index of the next song it is to play (performed through queries such as playlist-next?playerId=0&slaveManagerId=2&output=slave which returns something like "song=/P/Porcupine Tree/London.mp3\nindex=5\n")

Overall Improvements

  • create a SlaveManager to drop-in for the normal manager to be used in the slaves
  • replace the current dispatching in bemix-server with a model that passes in all of the variables
  • find a python module to parse the query string in a URL (rather than the current homebrew method)
  • replace the current server+players & slaveServer+players with server+slavePlayers, where slavePlayers are created on both the local and remote machines -- might create too many open ports; could create a slave manager that has the open port and works like current server (controls the various local players itself), doesn't have to be done over http?

See Also

  • Bemix - Overview information about the Bemix music system