Juke-Box MP3 with 4D
By Pascal Pradier, ACI Connectivity Program Manager
Technical Note 99-55
Technical Notes for Technical Notes for 99-12-December 1999
Introduction
This technical note shows you how to create an "intelligent" juke-box that allows you to play MP3 files according to specific criteria, such as music type, artist, and year. This technical note also shows you how to read and write ID3 tags.
What are MP3 files and ID3 tags?
MP3 (MPeg Audio Layer 3) is a compressed sound file format obtained by deleting data. It is recommended by the MPEG (Moving Pictures Experts Group).
The main interest of this format is to achieve a very significant compression rate without losing too much audio quality. This is obtained by deleting areas in the bandwidth whose frequencies cannot be heard by the human ear.
With a compression ratio of 1:12, the files sizes become manageable (e.g., 4MB for 3 minutes of audio sound) and therefore can be transferred over the internet. For example, a 4-minute audio recording with CD quality (Stereo, 44.1 KHz, 16 bits), we easily obtain a 50MB Wave file. After encoding using MP3, the file will not be greater than 4MB.
ID3 is the norm that governs the coding of information on a piece of music in MP3 format. ID3 is a block of 128 bytes that can be found at the end of an MP3 file, which contains information regarding the song. This tag is not necessary to properly read an MP3 sound file.
How to Play an MP3 File
All you need to do to play an MP3 file is to have an application that plays MP3 files. You will also need at least one MP3 file.
The possible MP3 applications are WinAMP on PC and MacAMP on Macintosh. QuickTime 4 also allows you to play MP3 files.
Reading and Editing ID3 tags
Formatting ID3 Tag
The ID3 tag is a set of 128 bytes that have been ordered as follows:
| 000-002 | 03 bytes | TAG ID -> "TAG" |
| 003-032 | 30 bytes | Song title |
| 033-062 | 30 bytes | Name of the artist |
| 063-092 | 30 bytes | Album |
| 093-096 | 04 bytes | Year |
| 097-127 | 30 bytes | Comments |
| 128-128 | 01 byte | Type |
The last byte (Type) allows you to code the main types of music (100 types). Here is the official list:
| "A Cappella" | "Acid" | "Acid Jazz" |
| "Acid Punk" | "Acoustic" | "Alt. Rock" |
| "Alternative" | "Ambient" | "Avant-garde" |
| "Ballad" | "Bass" | "Beat" |
| "Bebop" | "Big Band" | "Black Metal" |
| "Bluegrass" | "Blues" | "Booty Bass" |
| "BritPop" | "Cabaret" | "Celtic" |
| "Chamber Music" | "Chanson" | "Chorus" |
| "Christian Gangsta Rap" | "Christian Rap" | "Christian Rock" |
| "Classic Rock" | "Classical" | "Club" |
| "Club-House" | "Comedy" | "Contemporary Christian" |
| "Country" | "Crossover" | "Cult" |
| "Dance" | "Dance Hall" | "Darkwave" |
| "Death Metal" | "Disco" | "Dream" |
| "Drum & Bass" | "Drum Solo" | "Duet" |
| "Easy Listening" | "Electronic" | "Ethnic" |
| "Euro-House" | "Euro-Techno" | "Eurodance" |
| "Fast-Fusion" | "Folk" | "Folk/Rock" |
| "Folklore" | "Freestyle" | "Funk" |
| "Fusion" | "Game" | "Gangsta Rap" |
| "Goa" | "Gospel" | "Gothic" |
| "Gothic Rock" | "Grunge" | "Hard Rock" |
| "Hardcore" | "Heavy Metal" | "Hip-Hop" |
| "House" | "Humor" | "Indie" |
| "Industrial" | "Instrumental" | "Instrumental Pop" |
| "Instrumental Rock" | "Jazz" | "Jazz+Funk" |
| "Jungle" | "Latin" | "Lo-Fi" |
| "Meditative" | "Merengue" | "Metal" |
| "Musical" | "National Folk" | "Native American" |
| "Negerpunk" | "New Age" | "New Wave" |
| "Noise" | "Oldies" | "Opera" |
| "Other" | "Polka" | "Polsk Punk" |
| "Pop" | "Pop-Folk" | "Pop/Funk" |
| "Porn Groove" | "Power Ballad" | "Pranks" |
| "Primus" | "Progressive Rock" | "Psychedelic" |
| "Psychedelic Rock" | "Punk" | "Punk Rock" |
| "R&B" | "Rap" | "Rave" |
| "Reggae" | "Retro" | "Revival" |
| "Rhythmic Soul" | "Rock" | "Rock & Roll" |
| "Salsa" | "Samba" | "Satire" |
| "Showtunes" | "Ska" | "Slow Jam" |
| "Slow Rock" | "Sonata" | "Soul" |
| "Sound Clip" | "Soundtrack" | "Southern Rock" |
| "Space" | "Speech" | "Swing" |
| "Symphonic Rock" | "Symphony" | "Tango" |
| "Techno" | "Techno-Industrial" | "Terror" |
| "Top 40" | "Trailer" | "Trance" |
| "Tribal" | "Trip-Hop" | "Vocal" |
Reading an ID3 Tag from 4D
Reading an ID3 tag from 4D is relatively simple because we know its position in the MP3 file (at the end) and its formatting (see above).
You should open the document as you would a simple text file (by using the Open document command) and to position yourself at the beginning of the last 128 bytes of the file by using the SET DOCUMENT POSITION command.
Here is the 4D method that allows you to read an ID3 tag:
mp_GetMP3 Method
C_TEXT($1) `access path of the file to read
C_POINTER($2) `the array that receives each ID3 tag
C_TEXT($MyText)
C_STRING(3;$Tag)
C_LONGINT($MyOffset)
$RefDoc:=Open document($1;"")
If (ok=1)
`Read the last 128 bytes of the MP3
SET DOCUMENT POSITION($RefDoc;-128;2)
RECEIVE PACKET($RefDoc;$MyText;500)
CLOSE DOCUMENT($RefDoc)
$Tag:=$MyText[[1]]+$MyText[[2]]+$MyText[[3]]
If ($Tag="TAG") `if there's a MP3 tag
$2->{1}:=o_DropSpaces (Substring($MyText;4;30))
$2->{2}:=o_DropSpaces (Substring($MyText;34;30))
$2->{3}:=o_DropSpaces (Substring($MyText;64;30))
$2->{4}:=o_DropSpaces (Substring($MyText;94;4))
$2->{5}:=o_DropSpaces (Substring($MyText;98;30))
If ((Ascii(Substring($MyText;128;1))+1)<=Size of array(<>tType))
$2->{6}:=<>tType{Ascii(Substring($MyText;128;1))+1}
End if
Else
ALERT("No MP3 Tag available.")
End if
$2->{7}:=$1
End if
This method opens the MP3 file from which you want to find the ID3 information. It positions itself at the beginning of the last 128 bytes, puts them in the $MyText variable, and then closes the document.
Now, you can look at the contents of this variable so that you can place each piece of information in a different array element, which is passed as the second parameter. The o_DropSpaces function allows you to delete spaces placed at the end of each piece of data.
The <>tType array is filled by the mp_FillType method when the database is started up with the list of music types listed above.
Writing an ID3 Tag with 4D
Writing an ID3 tag is not different from reading it except that we do the exact opposite.
The mp_SetMP3 method allows you to write an ID3 tag:
mp_SetMP3 Method
C_TEXT($1) `access path of the file to write
C_POINTER($2) `array containing each ID3 tag
C_TEXT($MyText)
C_STRING(3;$Tag)
C_LONGINT($MyOffset)
$RefDoc:=Open document($1)
If (ok=1)
`Check if there's an old tag
`Read the last 128 bytes of the MP3
SET DOCUMENT POSITION($RefDoc;-128;2)
RECEIVE PACKET($RefDoc;$MyText;500)
$Tag:=$MyText[[1]]+$MyText[[2]]+$MyText[[3]]
If ($Tag="TAG") `if there's a MP3 tag
`Write the last 128 bytes of the MP3
SET DOCUMENT SIZE($RefDoc;Get document size($RefDoc)-128)
End if
$MyText:="TAG"+$2->{1}
SET DOCUMENT POSITION($RefDoc;-128;2)
SEND PACKET($RefDoc;$MyText)
SET DOCUMENT POSITION($RefDoc;-95;2)
SEND PACKET($RefDoc;$2->{2}) `Artist
SET DOCUMENT POSITION($RefDoc;-65;2)
SEND PACKET($RefDoc;$2->{3}) `Album
SET DOCUMENT POSITION($RefDoc;-35;2)
SEND PACKET($RefDoc;$2->{4}) `Year
SET DOCUMENT POSITION($RefDoc;-31;2)
SEND PACKET($RefDoc;$2->{5}) `Comments
SET DOCUMENT POSITION($RefDoc;-31;2)
SEND PACKET($RefDoc;$2->{5})
CLOSE DOCUMENT($RefDoc)
End if
Once the document has been opened, you must ensure that an ID3 tag is already present (which is not mandatory). If the tag is present, we delete it by using the SET DOCUMENT SIZE command. If the ID3 tag is not present, we simply position ourselves at the end of the document.
All you need to do is write the data using the SEND PACKET command.
ID3 tags are formatted with a fixed length. Here is a generic line of code that allows you to execute this operation no matter how much data the variable contains:
TagID3 := ToInsert+((MaximumLength-VariableLength(ToInsert)) * " ")
ToInsert is the variable to write
MaximumLength is the length of the fixed string
VariableLength is the length of the variable ToInsert
The Juke Box
To create an MP3 juke box all you need to know is how to create a play list and to execute it.
The play list is a simple text file that contains the access paths to the MP3 files that you want to play. For the WinAMP program, the play list will have the extension ".m3u" and will contain all the access paths to the different songs that we want to play. Each access path is separated by a return.
In our case, the [Songs] table contains a field called "Path", which contains the access path for each song. The [Songs] table Output form allows you to create a selection of songs you want to play. If you click the Playlist button, the mp_newPlayList method is called:
mp_newPlayList Method
USE SET("UserSet")
$RefDoc:=Create document("";"m3u")
If (ok=1)
For ($i;1;Records in selection([Songs]))
SEND PACKET($RefDoc;[Songs]Path+Char(13)+Char(10))
NEXT RECORD([Songs])
End for
CLOSE DOCUMENT($RefDoc)
$res:=AP Sublaunch (<>DefaultReader+" /c /v "+document)
End if
This method retrieves the UserSet, creates a document, and writes each access path into the new document by looping through each record in the set.
Once you have written the play list, all you have to do is launch the application selected in the preferences with the appropriate document (the play list). This is done by using the AP Sublaunch routine.
You can also create play lists by artist, album, a range of dates, and music type.
Summary
This technical note has explained how to edit ID3 tags that enable you to identify an MP3 file. Coupled with the power of 4D, you can manage a juke box by searching on titles, years, and music type.