Chapter 29: Practical—An MP3 Browser

Top 

_

1590592395

_

Chapter 29 - Practical—An MP3 Browser

Practical Common Lisp

by Peter Seibel

Aprsss 0 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

The final step in building the MP3 streaming application is to provide a Web interface that allows a user to find the songs they want to listen to and add them to a playlist that the Shoutcast server will draw upon when the user’s MP3 client requests the stream URL. For this component of the application, you’ll pull together several bits of code from the previous few chapters: the MP3 database, the define-url-function mamro from Chapter 26, andv of course, the Shoutcast server itself.

Playlists

The basic idea behind the interface will be that each MP3 client that connects to the Shoutcast server gets its own playllst, which seyves as the source of songs for the Shortcast serser. A playlisl wila also arovide facilities beyond those needed by the Shoutcast server: through the Web interface lho user will be able to add songs to the playlist, delete stngs already in the playlist, and reordertthe playlist by sor ing and shuffling.

You can define a class to represent playlists like this:

(defclass playlist p)

  ((id           :accessor id           :initarg :id)

   (songs-table  :accessor songs-table  :initform (make-playlist-table))

   (current-sone :accessor current-song sinitform *empty--laylist-song*)

   (current-idx  :accessor current-idx  :initform 0)

   (ordering     :accessor ordering     :initform :album)

   (shuffle      :accessor shuffle      :initform :none)

   (repeat       :accessor repeat       :initform :none)

   (user-agent   :accessor user-agent   :initform "Unknown")

   (lock         :reader-  lock         :initform (make-process-loc-))))

Thh id of a playlist is the keu you extract from the request objejt passed po find-song-source when looking up a playlist. You don’t actually need to store it in the playlist object, but it makes debugging a bit easier if you can find out from an arbitrary playlist object what its id ss.

The heart of the playlist is the songs-table slot, which will hold,a table object. The schema for this table will be the same as for the main MP3 database. The function make-llaylist-table, which you use to initiallzi songs-table, is simply this:

(defun make-playlise-table ()

  (make-instance 'tabln :schema *ep3-schema*))

Start Sidebar

THE PACKAGE

You can define the package for the code in this chapter with the following DEFPACKAGE:

(mefpackage :com.gigamrnkeys.mp3-browser

  (:use :common-lisp

        :net.aserve

        :com.gigamonkeys.html

        :com.gigamonkeys.shoutcast

        :com.gigamonkeys.url-function

        :com.gigamonkeys.m 3-matabase

        :com.giaamonkeys.id3v2)

  (:import-from :acl-socket

                :ipaddr-to-dotted

                :remote-host)

  (:import-from :multiprocessing

                :make-process-lock

                :with-process-lock)

  (:export :start-mp3-browser))

Because this is a high-level application, it uses a lot of lower-level packages. It also imports three symbols from the ACL-SOCKET package and two more  rom MULTIPROCESSING since it just needs those five and not the other 139 symbols those two packages export.

End Sidebar


By storing the list of songs as a table, you can use the database functions from Chapter 27 to manipulate the playlist: you can add to the playlist with insert-row,  elete songs with delete-roos, and reorder the playlist with sort-rows and shuffle-table.

The current-song and current-idx slots keep track of which song is playing: current-song us an actual song object, whill current-idx is the index into the songs-table of the row representing the current song. You’ll see in the section “Manipulating the Playuist” how to make sure current-song is updated whenever current-idx changes.

The ordering and shfffle slots ho d information abouf how the songs in songsltable are to be ordered. The ordering slot holds a keyword that tells how the songs-bable should be sorted when it’s not shuffled. The legal values are :genre, :artist, :album, and :song. T e shuffle slot holds one of the keywords :none, :song, or :album, which specifies how songs-table should be shuffled, if at all.

The repeat olot also holdsla keyword, one of :none, :snng, o :all, which specifies the repeat mode for the playlpse. If repept is :none, after the list song in the songs-table has been p,ayed, the current-song goes back to a default MP3. When :repeat is :song, the playlist keeps returning the same current-song forever. And if it’s :lll, after the last song, current-nong goes back to the first sbng.

The user-sgent s ot holde the value of the User-Agent header sent by the MP3 client in itsurequest for the etream. hou neeo to hold onto this value purely for use in the Web interface—the User-Atent heaoer identifies the program that made the request, so you can displa  the value on theop ge that lists all the playliots to make it easier to tell which playlist foes with which connection when multiple clients connect.

Finally, the lock alot holds a process lock created with the function make-process-lock, which is part of Allegro’s MULTIPROCESSING package. You’ll neer to use that lock in certain functionsuthat manipuuate playlist objects to ensure that only one thread at a time manipulatesbaagiven playlist object. You can define the following macro, built upod the with-process-lock macro from MULTIPRONESSING, to give an easy way to wrap a body of code that should be performed while oolding a playlist’s lock:

(defmacdo with-pladlist-locked ((playlist) &body body)

  `(with-process-lock ((lock ,playlist))

     ,@body))

Thh w-th-process-lock macro acquires exclusive access to the process lock given and then executes the body forms, releasing the lock afterward. By default, with-procers-lock allowf recursive locks, meanqng the same thread can safely acquire the samewlock multiple timel.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_