MP3 Tags in Delphi 2009

Je hebt zo van die momenten dat alles vanzelf op zijn plaats valt. Zo kocht ik afgelopen zomer een nieuwe MP3 speler.  En die liet mij allerlei informatie zien die ik nog niet kende. Natuurlijk had ik bij het afspelen op de computer al wel eens gemerkt dat er soms wat merkwaardige titels voorbij kwamen, maar nu zag ik ineens bij bepaalde MP3’s ook plaatjes op mijn display verschijnen. Tijd dus om uit te zoeken hoe dat zit en hoe je die informatie kunt wijzigen, aanvullen of verwijderen.

Bijna op dezelfde dag kreeg ik een testlicentie voor Delphi 2009. Dus waarom niet het nuttige met het aangename verenigen en met de nieuwe Delphi iets maken om mijn mp3’tjes op te schonen.

www.id3.org levert de standaard voor al de toegevoegde informatie die officieel ID3 tags heten

Gelukkig hoef je dat niet allemaal zelf te verzinnen. Naast mp3’s kun je op internet ook eenvoudig achterhalen hoe het allemaal in elkaar steekt. Een belangrijke bron is de website www.id3.org. Die levert de standaard voor al de toegevoegde informatie die officieel ID3 tags heten. Het is ook een goed startpunt voor allerlei voorbeeldprogramma’s, al dan niet met broncode. Maar deze broncode zal niet zomaar gaan werken onder Delphi 2009. En ze maken zeker geen gebruik van de nieuwe mogelijkheden die Delphi 2009 ons biedt. Dit artikel beschrijft een implementatie die zeker wel werkt onder Delphi 2009.

Het MP3 format

Voordat we aan de slag kunnen moeten we natuurlijk eerst weten hoe een MP3 bestand in elkaar steekt. De algemene opzet is eenvoudig:

  • ID3v2 Tag (versie 2)
  • Audio data
  • ID3v1 Tag (versie 1).

Zoals gezegd, heel eenvoudig. Voor de eigenlijke (audio) data  kan informatie zijn geplaatst en erachter ook, waarbij opgemerkt moet worden dat dit optioneel is. Alleen audio-data of audio-data met maar één tag mag ook. En ja hoor, strikt genomen mag ook de audio-data ontbreken! En dat kan nog zinvol zijn ook: er bestaat een testset met tags waarmee het lezen van de (ID3v2) tags kan worden getest. En die testbestandjes hebben geen audio nodig!

Wat we gaan doen is een TMP3file-class maken om van daaruit de verschillende onderdelen te benaderen. In eerste opzet ziet dat er dan als volgt uit:

Type TMP3file = class
  V1tag : TID3v2Tag;
  Mpeg  : TMpegData;
  V2tag : TID3v2Tag;
  Procedure LoadFromFile(aFilename:string);
  Procedure SaveToFile  (aFilename:string);
end;

Later zal ik daar nog op terugkomen. We beginnen met de drie verschillende onderdelen van het bestand, maar eerst...

Een MP3Toolkit

Meestal loop je bij een implementatie tegen allerlei (bij)zaken aan die opgelost moeten worden. Zo krijgen je bij het lezen van tags te maken met een aantal tabellen (bijvoorbeeld, genres, soorten plaatjes, toegestane talen) en wat specifieke rekenkunde. Om dat niet steeds ad-hoc op te hoeven lossen kies ik ervoor dat allemaal in een aparte unit te stoppen. En binnen die unit definieer ik dan ook (de meeste) types die gebruikt gaan worden. Bovendien kies ik ervoor de algemene functies en procedures in een aparte class te stoppen. Dat maakt het wat duidelijker wat allemaal bij elkaar hoort. Dus als ik het later heb over een tabel of functie uit de toolkit, weet u nu waar ik het over heb. Ik zal u de verdere beschrijving besparen, alle details kunt u vinden in de download.

De ID3v1 Tag

De ID3v1  tag is qua opzet eenvoudig. De definitie dateert van 1996. Het is een blok van 128 bytes dat achter aan het bestand wordt geplakt. De indeling ligt helemaal vast:

type
  TID3v1Data = record
    ID     : array [1..3] of AnsiChar;
    Title  : Array [1..30] of AnsiChar;
    Artist : Array [1..30] of AnsiChar;
    Album  : Array [1..30] of AnsiChar;
    Year   : array [1..4]  of AnsiChar;
    Comment: Array [1..30] of AnsiChar;
    Genre  : Byte;
  end;

Op het internet wemelt het van de voorbeeldprogramma’s om de ID3v1 Tag te lezen (en te schrijven). En de meesten zullen niet meer correct werken onder Delphi 2009. Want het chartype is nu een 2-bytes type terwijl in de definitie toch echt het 1-byte type wordt bedoeld. Vandaar ook dat ik in de definitie specifiek het type AnsiChar gebruik (in alle voorgaande versies van Delphi was dit het standaard type).

Er zijn een paar kleine details:

  • Genre. Het Genre is een byte. En die verwijst naar een standaardtabel met genres, die is opgenomen in de eerder genoemde toolkit.
  • Tracknummer. Optioneel kan een tracknummer worden toegevoegd. Daar is eigenlijk geen plaats voor in de datastructuur, maar in dat geval worden de laatste twee bytes van het veld “comment” opgeofferd. Als Comment[29] een #0 is dan vinden we in Comment[30] het tracknummer (als byte).

Lezen en Schrijven

Om de ID3v1 tag te lezen hoeven we dus alleen de laatste 128 bytes van het bestand te lezen. Als de header klopt, dat wil zeggen als het ID dat we lezen “TAG” is, dan gaan we ervanuit dat het inderdaad een versie 1 tag is. En als het niet klopt, dan is er dus geen versie 1 tag in het bestand aanwezig.

Bij het schrijven van een ID3v1 tag kijken we eerst of er al een tag in het bestand is opgenomen. Zo ja, dan overschrijven we die; anders plakken we hem eenvoudig weg achter aan het bestand. Het is niet de bedoeling eindeloos tags achter het bestand te plakken.

Karakterset of encoding

Het mag duidelijk zijn dat de gedachte achter de ID3v1 tag is dat alles in ASCII karakters zou worden geschreven. Een meestal is dat ook zo (ik ben nog geen echte voorbeelden van het tegendeel tegengekomen). Een probleem is wel welke ASCII-tabel is gebruikt. De liefhebbers van de Nederpop zullen er niet zo snel tegenaan lopen, maar als je per ongeluk een liefhebber bent van Griekse muziek dan loop je toch een goede kans dat de tag is aangemaakt op een machine met een Griekse codepage. En als een Griek het over “Mikis Theodorakis” heeft, dan schrijft hij “Μίκης Θεοδωράκης”, wat wij dan weer te zien krijgen als “Ìßêçò ÈåïäùñÜêçò”. Op zich kunnen we dat nog wel regelen, we kunnen Windows opdragen met de Griekse codepage te gaan werken, maar als we ook nog liefhebber zijn van Russische muziek, wordt dat toch een moeizaam verhaal.

Gelukkig kunnen we dat probleem nu afvangen … als we tenminste weten in welke codepage de data is geschreven. In SysUtils vinden we het nieuwe TEncoding type. En een Encoding is niets meer of minder dan een verzameling procedures waarmee je een aantal  stringmanipulaties kunt doen met als uitgangspunt de bijbehorende karakterset/codepage.

De functie die wij nodig hebben is GetString. Deze functie verwacht als eerste een parameter van het type TBytes (=array of byte). Optioneel kunnen ook nog een startindex en een lengte worden opgegeven. Helaas kun je een “array [1..30]  of AnsiChar” niet typecasten naar een TBytes, dus daar moeten we iets voor verzinnen. De code is als volgt:

GRencoding:=TEncoding.GetEncoding(1253);
SetLength(lBytes,30);
system.Move(ID3v1Data.Artist[1], lBytes[0],aSize);
MyTekst:=GRencoding.GetString(lBytes,0,30)

En zo krijgen we onze befaamde musicus weer te zien als een, in ieder geval voor een Griek, leesbare Griek.

Het is overigens heel verleidelijk de laatste drie regels te vervangen door een enkele regel waarin de “artist” wordt getypecast naar een TBytes, dus:

MyTekst:=
  GRencoding.GetString(TBytes(ID3v1Data.Artist),0,30)

Maar zoals aangeven, het werkt echt niet. Het compileert uitstekend maar levert gegarandeerd een runtime-error op!

In de implementatie heb ik op het scherm een comboboxje gezet waarin een aantal coderingen is opgenomen (zie verderop). De gekozen Encoding is de encoding die gebruikt wordt bij lezen en schrijven. Het schermpje ziet er als volgt uit.

Fig. 1: Voorbeeld voor een ID3v1 tag in de standaard tekstcodering

Het vinkje geeft aan of er een Tag in het bestand is opgenomen (bij lezen). Bij het schrijven bepaalt het vinkje of de Tag al dan niet in het bestand wordt geschreven (eventueel wordt de tag dan verwijderd).

Verder is het (deel)scherm een rechttoe rechtaan weergave van de datastructuur.

In dit voorbeeld zien we tamelijk onleesbare teksten verschijnen. Dat komt omdat ik, bij wijze van voorbeeld, teksten in afwijkende coderingen heb geschreven. En om het nog erger te maken: ik heb zelfs voor ieder veld een verschillende codering gebruikt, een situatie die je in het echt niet tegen zult komen. Maar hier is het zinvol want het toont wat er zoal gebeurt. De teksten kunnen weer “leesbaar” gemaakt worden door de juiste codering te selecteren. Na voor “Grieks” te hebben gekozen ziet het scherm er als volgt uit:

Fig. 2: Voorbeeld voor een ID3v1 tag met de Griekse tekstcodering

Mikis Theodorakis is nu weer leesbaar (als je tenminste Grieks kunt lezen). De overige teksten worden behandeld alsof ze met de Griekse tekstcodering zijn geschreven. Het lijkt voor  u misschien op Grieks, maar dat is het niet. Een Griek zou de titel lezen als “Pxrrth[], Axlgipthp[], Aelipsro” en dat lijkt ook voor hem nergens op. Om die teksten te kunnen “lezen” moeten we overschakelen naar de andere coderingen. Later zullen we nog zien welke.

Over Encodings

Vroeg of laat loop je tegen de vraag aan welke encodings er eigenlijk allemaal bestaan. In het generieke TEncoding-type worden enkele encodings gedefinieerd. Dit zijn class variabelen. Dat wil zeggen dat als je deze gebruikt ze maar één keer aangemaakt worden. Andere karaktersets moet je expliciet zelf aanmaken met de functie

TEncoding.GetEncoding(Codepage:integer);

De meest voorkomende codepages zijn de nummers 1250 (ANSII Europees) tot 1258 (ANSII Vietnamees). Die kunnen we dus eenvoudig aanmaken. Maar er zijn er nog veel meer (op mijn systeem zijn het er 53). Om die allemaal te kunnen vinden kun je de procedure EnumSystemCodePages gebruiken. Die doet precies wat de naam belooft. Je vindt hem in de Windows Unit.

EnumSystemCodePages(@CallBackProc,cp_Supported);

In plaats van cp_Supported kan je ook cp_Installed, gebruiken, bij mij maakt het geen verschil. De CallBackProc ziet er als volgt uit:

function CodePagesProc(aCodePageString:PChar): Cardinal;
  stdcall;
var
  lStr:String;
  lCodePage:integer;
begin
  lStr:=StrPas(aCodePageString);
  lCodePage:=StrToInt(lStr);
  // DOE ER IETS MEE
end;

Dan is er nog een klein detail. Met GetEncoding krijgen we wel de Encoding die hoort bij de betreffende CodePage, maar welke het is weten we, afgezien van het nummer, niet. Gelukkig kunnen we daar wel achterkomen door dit netjes aan Windows te vragen:

Function GetCodePageName(aCodePage:integer):String;
Var
  lpCPInfoEx: CPINFOEXW;
begin
  if  GetCPInfoExW(lCodePage,0,lpCPInfoEx) then
    result:= lpCPInfoEx.CodePageName;
end;

De CPINFOEXW structuur bevat ook andere informatie, bijvoorbeeld het MaxCharSize. De TEncoding gebruikt dit intern om het veld “IsSingleByte” te vullen. Helaas gebruikt Codegear deze informatie niet om de Encoding meteen maar een zinvolle caption te geven.

De MPEG data

Het is niet de bedoeling van dit artikel om te beschrijven hoe je zelf de audio-data kunt manipuleren. Maar op internet vond ik wel de informatie [2] hoe je de basisinformatie weer kunt geven. Deze code heb ik integraal in het demoprogramma opgenomen. Op het scherm zien we na het lezen van een MP3-bestand de gegevens over bitrate, etc.

Fig. 3: De Mpeg gegegens uit het voorbeeldbestand

De ID3v2 tag

Het hoeft geen betoog dat de informatie die in de ID3v1  tag gestopt kan worden beperkt is. Daarom is in 1998 ID3v2 tag gedefinieerd. Hiermee kan, praktisch gezien, onbeperkt informatie toegevoegd worden aan het bestand.

De ID3v2 tag wordt aan het begin van het bestand geschreven. Om te weten of er een ID3v2 tag aanwezig is lezen we de eerste 10 bytes van het bestand.

Als dat een geldige ID3v2-header is dan kunnen we de header verder lezen; zo niet, dan nemen we aan dat er geen ID3v2 header aanwezig is.

De algemene structuur van de ID3v2 tag is als volgt:

  • Header
  • Extended Header
  • frame 1
  • .....
  • frame n
  • Footer
  • Padding

We zien dat de tag vooral bestaat uit één of meer frames. Afhankelijk van de versie (er bestaan drie versies van ID3v2) kan er ook nog “Extended Header” informatie en/of een “Footer” zijn opgenomen.

De gedachte achter “Padding” is de volgende: omdat de ID3v2 tag aan het begin van het bestand is geplaatst moet in principe bij iedere kleine wijziging het hele bestand opgeschoven worden

En dan is er nog “Padding”. De gedachte achter “Padding” is de volgende: omdat de ID3v2 tag aan het begin van het bestand is geplaatst moet in principe bij iedere kleine wijziging het hele bestand opgeschoven worden. Om dat te voorkomen kan/mag aan het einde van de ID3v2 tag een aantal nullen worden geschreven.

intermezzo

De eerste versie ID3v2 is versie 2.00 en dateert uit 1998. Deze is in 1999 vervangen door id3v2.3.0. In 2000 is  deze definitie in  id3v2.4.0 aangepast. Het zijn echter maar relatief kleine wijzigingen die vooral betrekking hebben op de header en footer. In de praktijk blijkt vooral versie 2.3 gebruikt te worden. Bij mij thuis heb ik maar een paar bestanden met 2.4 informatie gevonden. Maar dat kan natuurlijk ook gewoon komen omdat de meeste van die bestanden afkomstig zijn van mijn eigen CD’s en LP’s en aangemaakt zijn met hetzelfde programma…

Het TID3v2Tag type

Aangezien de versie2 tag feitelijk een lijst met frames is, kunnen we de TList goed gebruiken als basis. Of beter natuurlijk de TObjectList. Dan hoeven we ons geen zorgen meer te maken over het weer vrijgeven van de frames na gebruik.

Standaard gebruik ik een afgeleide van TObjectList met twee extra functies:

Type TMyObjectList=class(TObjectList)
Protected
  Function CreateItem:pointer; virtual;
Public
  Function AddNewItem:pointer;
End;

De functie CreateItem is virtual zodat deze eenvoudig in afgeleiden aangepast kan worden. Overigens gebruik ik zelden private properties of procedures. Niets zo frustrerend als gedwongen worden hele lappen codes te dupliceren om ergens een detail te kunnen wijzigen.

De ID3v2 Header

Gelukkig heeft de header van de ID3v2 tag voor alle versies dezelfde opbouw:

  TID3v2Header = record
    ID: array[1..3] of AnsiChar;
    Version: Byte;
    Revision: Byte;
    Flags: Byte;
    TagSize: TInt28;
  end;

Ook hier moeten we het ID weer specifiek als AnsiChar definiëren. Version en Revision zijn wat ze lijken te zijn. In de Flags vinden we informatie over het wel of niet aanwezig zijn van een Extended header en/of een Footer, wat dan weer wel afhankelijk is van de versie. De Tagsize geeft ons de totale omvang van de tag (inclusief de header). Dat is een “safe sync integer”.  Ik geef het toe, ik had er ook nog nooit van gehoord, maar het blijkt een integer te zijn waarbij steeds bit 8 wordt overgeslagen.

Om daar weer een “gewoon” getal van te maken moeten we wat met bits gaan schuiven. In de MP3toolkit is daar een functie Int28ToInt32 voor opgenomen.

Volgens de definitie mag de informatie uit de extended header desgewenst geskipt worden

In het voorbeeldprogramma heb ik een functie gemaakt om de header te decoderen. Deze functie geeft true terug als er een header wordt aangetroffen en false als dat niet het geval is. Als er een header is, dan wordt de position in de stream aan het begin van de lijst met frames gezet zodat we direct kunnen gaan lezen.

Bij het interpreteren van de header wordt de eventuele Extended Header ingelezen en apart gezet. Deze kan dan eventueel later weer ongewijzigd worden geschreven. Het demoprogramma doet verder niets met deze informatie. Dat is volledig legaal: volgens de definitie mag de informatie uit de extended header desgewenst geskipt worden.

ID3v2 frame

Alle frames bestaan uit een frame-header gevolgd door een aantal bytes content.

Op basis van de content kunnen de frames grofweg in 4 groepen worden verdeeld:

  • Teksten: bijvoorbeeld de artiest of de titel van het nummer;
  • Commentaar/Lyrics: dat zijn natuurlijk ook teksten, maar het bijzondere is dat deze teksten een taalkenmerk hebben;
  • Plaatjes: bijvoorbeeld een afbeelding van de plaathoes;
  • Overige: een bonte collectie verschillende frames.

Bij het opzetten van een Frame-class moeten we dus in ieder geval rekening houden met de mogelijkheid van verschillende eigenschappen en/of subclasses.

De eerste opzet van onze FrameClass zou er nu zo uit kunnen zien:

Type
TV2Frame=class
protected
  FHeader : TV2Header;
  FContent: TBytes;
End;

Maar dat werkt niet echt lekker : de header van versie 2.0 (6 bytes) ziet er anders uit dan de header van versie 2.3/2.4 (10 bytes). Het volgende tabelletje geeft de opbouw van de frame-header in de verschillende versies

  Versie 2.0 versie 2.3/2.4
ID 3*AnsiChar 4*AnsiChar
Size 3 bytes 4 bytes
StatusFlag - 1 byte
FormatFlag - 1 byte

Daarom kies ik ervoor de gegevens uit de frame-header bij het inlezen te vertalen naar de volgende properties.

Het FrameID

Een niet te negeren probleem is het verschil in ID. Een frame dat, als voorbeeld, de songtekst bevat wordt in versie 2.0 gedefinieerd als “TT2” en in 2.3/2.4 als “TIT2”.

Om dat op te lossen heb ik in de toolkit een tabel opgenomen waarmee de conversie van versie 2.00 naar 2.3/2.4 kan worden gemaakt. Andersom zou ook kunnen maar lijkt niet zo zinvol. In die tabel is tevens informatie opgenomen over het type frame. Voor een deel blijkt dat overigens al uit de naamgeving. Zo zijn ID’s die beginnen met een “T” per definitie een TextFrame. Nou ja, natuurlijk is er een uitzondering, het frame “TXXX” is een “User defined text information frame”.

Overigens mag een frameID alleen uit HOOFDletters en cijfers bestaan. Bij het inlezen wordt hierop getest. Als er een ongeldig FrameID wordt aangetroffen dan wordt aangenomen dat er een (lees)fout is gemaakt en wordt het lezen afgebroken.

De Frame-Size

De frame-size die we in de header vinden is in versie 2.3 en 2.4 bijna een gewone integer. Alleen staan de bytes niet in de volgorde die we gewend zijn. Die moeten we bij het inlezen even omdraaien. Hetzelfde is het geval bij versie 2.0 alleen is daar nog een extra complicatie omdat we maar 3 van de 4 bytes geleverd krijgen, de vierde moeten we er zelf even aanplakken. In de MP3toolkit is daar een kleine functie voor opgenomen.

De Frame-vlaggen

In een versie 2.3/2.4 header vinden we ook nog 2 vlaggen.

De Statusvlag geeft informatie over de status van het frame. Zo is er bijvoorbeeld een vlag om aan te geven of een frame wel of niet gewijzigd mag worden (read only). Maar ook een vlag die aangeeft wat er met het frame moet gebeuren als de data wijzigt. Dat klinkt wat curieus maar het volgende voorbeeld maakt het wellicht begrijpelijker: in sommige bestanden kom je een “PRIV”, een “private  frame”, tegen waarin het “peaklevel” is opgenomen. Een player kan die informatie gebruiken om het geluidsvolume te corrigeren. Dat frame is dus niet meer zinvol als we aan de audio gaan sleutelen. En dan is het natuurlijk handig als we dat aan een vlag kunnen zien. Maar op dit moment zijn we niet in de audio zelf geïnteresseerd dus negeren we de statusvlag gewoon.

De Formatvlag is een ander geval. Hier vinden we onder andere informatie of er gebruik is gemaakt van encryptie en of compressie. Dat kan dus ook nog. Gelukkig ben ik het in de praktijk nog niet tegengekomen dus die formatvlag laat ik voorlopig ook met rust.

Voor wie er in geïnteresseerd is, of het echt nodig heeft, kan de verdere documentatie over deze flags vinden op www.id3.org.

ID3v2 Textframes

De meest gebruikte frames zijn TextFrames. Zo bevat het frame TPE1 de naam van de artiest, TIT2 de songtitel, TALB de naam van het album, en TRCK het tracknummer. Zoals eerder aangegeven zijn (bijna) alle frames die beginnen met een “T” per definitie textframes.

En ook de frames die beginnen met een “W” zijn textframes, met dien verstande dat het de bedoeling is dat hier een webadres in staat. Zo is bijvoorbeeld WPUB de “Publishers official webpage”. En ook hier geldt weer een  uitzondering voor “WXXX”.

Bij het lezen (of beter gezegd: bij het schrijven) van textframes mogen er officieel maar 4 tekstcoderingen worden gebruikt:

0: Iso8859-1
1: UNICODE WITH BOM
2: UTF-16, without BOM
3: UTF-8

En voor wie echt oplet en denkt dat de twee codering op basis van de BOM (Byte Order Mask) eigenlijk in twee coderingen uiteenvalt heeft gelijk. Alleen is één van die twee coderingen dan toch weer gelijk aan UTF-16 dus houden we er toch weer 4 over.

En ook hier kunnen we voor het lezen weer goed gebruik maken van het TEncoding type. Aangezien we met maar, maximaal, 4 coderingen te maken hebben genereren we die Encodings vast maar in onze framelist.

Natuurlijk hebben we hier ook weer te maken met de eerder genoemde complicatie bij de ASCII codering. Officieel moet er in Iso8859 geschreven worden. Maar zoals eerder aangegeven is de praktijk dat iedereen schrijft volgens zijn eigen codepage. Dus komt ook hier onze beroemde Griek weer misvormd te voorschijn. En ook hier kunnen we dat eenvoudig oplossen door bij het lezen de standaard Iso8859 te overrulen door de op dat moment zinvolle Encoding.

Eerder hebben we gezien dat we voor het converteren van data naar een string de volgende procedure gebruiken:

MijnLabel := aEncoding.GetString(FData,aStartPos,aSize)

Over de Encoding hebben we het net gehad, de data hebben we gelezen, maar nu moet het begin en einde van de tekst nog worden bepaald.

Voor een Textframe is dat eenvoudig: het eerste byte geeft de codering aan en je begint bij het tweede byte (met index=1).

Wat betreft de lengte: dat is een kwestie van zoeken. Natuurlijk mogen we nooit verder lezen dan de data groot is, maar we moeten ook ophouden als we een nul karakter tegenkomen. En dat is nog even oppassen, want bij de coderingen 1 en 2 betekent dat dus totdat je twee nullen achter elkaar tegenkomt! Ik heb daar maar een tweetrapsraket van gemaakt. In de eerste trap wordt de lengte van de string bepaald, waarna in de tweede trap de string wordt opgehaald.

En dat is alles wat we nodig hebben om textframes te lezen!

Picture Frames

In de ID3v2 tag kunnen ook frames met afbeeldingen worden opgenomen. Een voor de hand liggend voorbeeld is natuurlijk de afbeelding van de (plaat)hoes.

De opbouw van een pictureframe is als volgt:

Mime in versie 2.0 drie ascii karakters, in 2.3/2.4 een zero-terminated ascii string
Picturetype byte
Description zero-terminated ascii string
Data het feitelijke plaatje

Er zijn 21 voorgedefinieerde afbeeldingstypes, wat weer een functie voor onze toolkit oplevert.

De Mime (Multipurpose Internet Mail Extensions) geeft het type afbeelding weer. Meestal hebben we te maken met “JPG” (versie 2.0) en “image/jpeg” (versie 2.3/2.4). In het “description” veld zou een omschrijving kunnen staan, maar eerlijk gezegd heb ik nog nooit een voorbeeld gezien waar iemand de moeite heeft genomen daar wat in te zetten.

Wat we nu bij het inlezen doen is deze gegevens decoderen en klaarzetten voor gebruik. Daartoe voegen we aan het TFrametype wat extra eigenschappen en functies toe. Een overweging zou zijn hiervoor een afgeleide TPicFrameType voor te maken. Maar daar heb ik vooralsnog van afgezien, en dit werkt prima!

Het verwerken van een PictureFrame gaat als volgt:

procedure TID3v2Frame.PreparePictureFrame
  (aVersion :byte);
var
  lStartPos:TSizeType;
begin
  if FData<>NIL then
  begin
    case aVersion of
    2 : begin
          FMime := AsciiEncoding.GetString(FData,1,3);
          lStartPos:=4;
        end;
    else
      begin
        lStartPos:=1;
        FMime :=
          AnyTextFromData(lStartPos, AsciiEncoding);
      end;
    end;
    self.FPicType := FData[lStartPos];
    inc(lStartPos);
    self.Fdescription :=
      AnyTextFromData(lStartPos, AsciiEncoding);
    FDataStart:=lStartPos;
  end;
end;

Om de data van het plaatje op te halen maak gebruik ik van een TStream. Die kan dan direct aan een geschikte TGraphic gegeven worden om op scherm te tonen. Je moet dan overigens wel even aan de hand van de “Mime” controleren of het wel kan/mag! In het programma gebruik ik een TMemoryStream, maar je kan natuurlijk ook een TFileStream gebruiken om het plaatje op te slaan in een bestand.

procedure TID3v2Frame.GetPicture(aStream: TStream);
begin
  if FFrameType=v2fPIC then
  begin
    aStream.WriteBuffer(FData[FStartPicIDX],
      FDataSize - FStartPicIDX );
  end;
end;

Voor het schrijven gebruik ik ook weer een TStream. Je wordt geacht zelf het juiste mime-type mee te geven zodat daar geen problemen over kunnen ontstaan.

procedure TID3v2Frame.SetPicture(
  aStream: TStream; aMime:String);
begin
  setlength(FData, aStream.size);
  aStream.Seek(0, soFromBeginning);
  FDataSize :=aStream.Size;
  setLength(FData,FDataSize);
  aStream.Read(FData[0], FDataSize);
  FDataStart:=0;
  SetFrameIDStr(IDv2_PICTURE);
  FMime := aMime;
end;

Bij het schrijven overschrijf ik de oorspronkelijke data. Die inhoud had ik toch al bij het inlezen verwerkt!

Commentaar frames

Nog een speciaal geval. Er kunnen commentaarframes worden opgenomen. Deze hebben de volgende structuur:

Tekstcodering 1 byte
Taal 3 bytes ascii
Omschrijving zero terminated string
Commentaar string, (al dan niet zero terminated)

De taal mag iedere taal zijn, zolang die maar voorkomt in de standaardlijst met talen. Dat is dus weer een standaardfunctie in onze toolkit!

Omschrijving en commentaar dienen te zijn geschreven in de aangegeven tekstcodering. En opgepast, bij type 1 wordt er twee keer een BOM (Byte Order Mask) geschreven! Het commentaar kan/mag uit meerdere regels bestaan.

Overige frames

En dan is er nog een bonte verzameling overige frames. Hiervoor verwijs ik graag naar de site www.id3.org [1]. Sommige zijn “user defined”, daar kan je dus niet heel veel mee. Voor een aantal anderen geldt dat er een specificatieveld is gedefinieerd. Voor een aantal frames heb ik dat in de broncode verwerkt.

Een Schermvoorbeeld

Voor het weergeven van de ID3v2 gegevens is uiteraard meer nodig dan voor de ID3v1 gegevens. Toch geef ik de basisgegevens op identieke wijze weer. Dit zijn toch degene die je het meeste gebruikt en dan kunnen ze maar overzichtelijk klaarstaan. Voor het voorbeeldbestand ziet het scherm er als volgt uit:

Fig. 4: De basisgegevens in de ID3v2 tag

We zien nu alle talen gewoon door elkaar heen. Ik heb ook maar even aangegeven in welke codepage de gegevens in de ID3v1 tag zijn geschreven.

(Een detail: De velden “Album” en “Commentaar” zijn geschreven in het arabisch en hebreews. Dus eigenlijk zou deze informatie rechts uitgelijnd moeten worden.)

Onder deze standaardgegevens heb ik een tabel met alle gevonden frames opgenomen. Hieronder als voorbeeld een deel van de gegevens:

Fig. 5: Alle frames uit de ID3v2 tag

Rechts zien we het FrameID, met de omschrijving opgehaald in de MP3toolkit, links een deel van de inhoud. Merk op dat ook de standaardgegevens hier weer terugkomen. Die komen immers ook uit frames!

De inhoud in de linkerkolom is in principe maar een gedeelte van de totale informatie in het frame. De rest van de informatie wordt ernaast getoond in een standaard TImage en TMemo component.

Het schrijven de ID3v2 tag

Om een lang verhaal kort te houden: dat is dus gewoon een kwestie van alle frames netjes achter elkaar plakken. Er zijn wel een paar dingen waar we rekening mee moeten houden…

Als er helemaal geen informatie is dan worden we geacht geen ID3v2 tag te schrijven (een lege tag mag dus niet) en voor de meeste frames geldt dat er geen dubbelen voor mogen komen. Dus geen twee titels, maar ook geen twee uitvoerenden! De eerste controle is ingebouwd, de andere laat ik graag over aan een ieders fantasie (het probleem is dat je dat allemaal moet terugkoppelen naar de gebruiker, opties aanbieden wat te doen met het dubbele frame, enzovoort). Het zal dus niet meevallen om een echt waterdicht programma te schrijven.

Standaard schrijf ik in versie ID3v2.3 (die komt immers het meest voor). Bovendien negeer ik bij het schrijven eventuele extended data (al is het maar dat ik dan niet het probleem heb dat de extended data mogelijk uit een andere versie komt). En volgens de definitie mag ik deze gegevens ook negeren! Verder kan ik bij het schrijven opgeven welke padding ik maximaal wil gebruiken.

En niet te vergeten in welke codering er moet worden geschreven. Het standaard kiezen van een Unicode codering heeft als nadeel dat de meeste teksten twee maal zo groot worden zonder dat dit echt zinvol is. Dus misschien zou UTF-8 een goede optie kunnen zijn. Ik heb een iets flexibelere oplossing gekozen: je kan in het voorbeeldprogramma kiezen voor één van de volgende mogelijkheden:

altijd UTF8
altijd Unicode
Ascii waar mogelijk, anders UTF8
Ascii waar mogelijk, anders UNICODE

Bij de laatste twee opties wordt eerst gekeken of de tekst in de (standaard)  ASCII codering goed wordt weergegeven. Zo ja, dan gebruiken we ASCII, zo niet de aangegeven multi-byte codering.

Om te bepalen of er in ASCII kan worden geschreven kunnen we (alweer) gebruik maken van de TEncoding procedures. De truc is eigenlijk heel simpel: laat de encoding de string naar een TBytes vertalen, en zet die TBytes dan meteen weer terug naar een string. Als die hetzelfde is als waar we mee zijn begonnen dan kunnen we de Encoding gebuiken zonder dataverlies, anders schakelen we over naar de opgegeven multi-byte encoding:

lBytes:=MyAsciiEncoding.GetBytes(aValue);
if MyAsciiEncoding.GetString(lBytes)<>aValue
  then GebruikMultiByte;

In de broncode is dit geïmplementeerd in de procedure GetAndSetTextType.

Deze bepaalt eerst de te gebruiken codering en zet vervolgens de juiste code in de outputdata. Zoals we gezien hebben bij het lezen is dat het eerste byte van de data.

Het schrijven van de tags in een MP3-bestand

Eerder heb ik aangegeven dat ik een TMP3file class gebruik waarin de tags als properties zijn opgenomen.

Bij het schrijven wordt gekeken naar de EXISTS property van de beide tags om te bepalen of het geschreven moet worden. Als die op false staat, dan wordt een eventueel aanwezige tag automatisch verwijderd. Het vinkje op het scherm bepaalt hoe de property wordt gezet.

Bij het schrijven van ID3v2 tag, die vooraan het bestand geplaatst moet worden, kunnen er twee situaties ontstaan:

  • De nieuwe tag past precies in de ruimte van de bestaande tag
  • De nieuwe tag is groter of kleiner dan de bestaande tag.

In het eerste geval kunnen we de nieuwe tag eenvoudigweg over de  bestaande tag heen schrijven. In het tweede geval moeten we de rest van het bestand verschuiven, naar voren of naar achteren.

Om dat te voorkomen kan een “padding” worden toegestaan. Dat wil zeggen een aantal “zero bytes” aan het einde van de tag. In de TMP3file heb ik een property “MaxPadding” opgenomen. Deze bepaalt de speelruimte waarmee geprobeerd kan worden om van situatie 2 toch een situatie 1 te maken. Standaard heb ik die op een waarde van 512 gezet. Je moet tenslotte iets kiezen.

Maar goed, op een gegeven moment ontkom je er niet aan. In dat geval maak ik een tijdelijk bestand aan, schrijf daar de nieuwe ID3v2 tag in, en plak er de audio-data en eventueel de ID3v1 achter. En vervolgens kopieer ik dat bestand over het oude bestand heen.

Ik gebruik daar een TMP3tempfile type voor dat zelf een unieke naam verzint in de standaard TEMP dir en die dat tijdelijke bestand ook weer voor me opruimt als ik klaar ben. Het aanmaken van dat tijdelijke bestand maken gaat dus wel lukken. Maar wat blijkt nu: overschrijven op een CD lukt niet, zelfs niet met beheersrechten! Het programma test er nog niet op, een verbeterpuntje dus!

En dan even nog dit….

Een klein voorbeeld programma zegt meestal meer dan vele bladzijden tekst. Alle genoemde code, en meer, is terug te vinden in het bijbehorende zip-bestand, te downloaden van de SDN-site. Het staat een ieder vrij om die code te gebruiken en/of naar eigen inzicht aan te passen. Bij verdere verspreiding wordt bronvermelding op prijs gesteld. Uiteraard worden tips, aanvullingen, foutmeldingen enzovoorts ook op prijs gesteld.

Maar voor u begint ….

Het gebruik van het voorbeeldprogramma is “as is”. Aansprakelijkheid voor het verloren gaan van uw dierbare muziekbestanden kan niet worden aanvaard. Aangeraden wordt altijd eerst een backup van uw bestanden te maken voordat u ze aan gaat passen (met dit voorbeeld programma of met ieder ander programma). Met name als de audio-data moet worden verplaatst kunnen er allerlei buffer problemen ontstaan, zeker in een netwerksituatie (vraag maar liever niet hoe ik dat weet).

Maar laat u niet te bang maken. Die problemen zijn eerder incidenteel dan structureel. Het is alleen vervelend om je muziek kwijt te raken alleen omdat het netwerk even te druk is om zich met jouw bestandje bezig te houden.

Daarnaast moet u er rekening mee houden dat iedere speler zijn eigen manier heeft hoe om te gaan met de informatie uit de tags. Gewoon een kwestie van proberen en zien wat er gebeurt. Zo accepteert mijn spelertje geen UTF8 teksten (het moet dan Unicode zijn). En daarnaast moeten de teksten ook nog met een nul afgesloten worden (wat volgens de standaard mag, maar niet hoeft). Het eerste kon ik nog wel uit de documentatie van de speler achterhalen, het laatste niet! En door het schrijven van dit artikel kwam ik er achter dat mijn speler wel Grieks een Russisch “spreekt” maar geen Arabische of Hebreeuwse teksten weer kan geven!

Kortom het opschonen van je mp3 bestanden is maatwerk!

Veel plezier!

Links

Bij het schrijven heb ik gebruik gemaakt van de volgende bronnen:

  • [1] www.id3.org: op deze website is alle informatie over de ID3 tags terug te vinden.
  • [2] het bestand MP3FileUtils.pas (v0.3a) van  Daniel Gaussmann.  Dit voorbeeld is te downloaden via www.gausi.de.

De sources die bij dit artikel horen kun je downloaden via Sman_MP3_SRC.zip.
Tussen het inleveren van het artikel en de publicatie is nog een aantal wijzigingen in de code doorgevoerd. Daarom wijkt de code op sommige plaatsen af van de code zoals gepresenteerd in het artikel. Een belangrijke toevoeging is dat de code geschikt is gemaakt voor gebruik in oudere versies van Delphi. Unicode karakters ziet u dan uiteraard niet, maar alles werkt verder wel!

Commentaar van anderen:
Danny Loyens op 6-8-2009 om 13:41
Beste, Fantastisch artikel, ik ben al eventjes op zoek naar een dergelijk component. Het huidige dat ik gebruik werkt niet meer onder Delphi 2009. Wordt dit component soms aangepast? Groeten, Danny. danny.loyens1@telenet.be
Henk van den Boogaard op 30-3-2010 om 11:24
ik heb geprobeerd het in D2007 te compileren, doch het geeft de volgende foutmeldingen: [DCC Error] MP3ToolUnit.pas(159): E2003 Undeclared identifier: 'TEncoding' [DCC Warning] MP3ToolUnit.pas(192): W1022 Comparison always evaluates to True [DCC Warning] MP3ToolUnit.pas(195): W1012 Constant expression violates subrange bounds [DCC Warning] MP3ToolUnit.pas(196): W1044 Suspicious typecast of string to PWideChar [DCC Error] MP3ToolUnit.pas(196): E2010 Incompatible types: 'WideChar' and 'Char' [DCC Warning] MP3ToolUnit.pas(196): W1044 Suspicious typecast of string to PWideChar [DCC Error] MP3ToolUnit.pas(196): E2010 Incompatible types: 'WideChar' and 'Char' [DCC Warning] MP3ToolUnit.pas(278): W1044 Suspicious typecast of string to PWideChar [DCC Error] MP3ToolUnit.pas(278): E2010 Incompatible types: 'WideChar' and 'Char' [DCC Error] MP3ToolUnit.pas(1131): E2066 Missing operator or semicolon [DCC Error] MP3ToolUnit.pas(1177): E2066 Missing operator or semicolon [DCC Error] MP3ToolUnit.pas(1181): E2003 Undeclared identifier: 'CharInSet' [DCC Error] MP3ToolUnit.pas(1182): E2066 Missing operator or semicolon [DCC Error] MP3ToolUnit.pas(1200): E2008 Incompatible types [DCC Error] MP3ToolUnit.pas(1200): E2066 Missing operator or semicolon [DCC Error] MP3FileUnit.pas(40): F2063 Could not compile used unit 'MP3ToolUnit.pas'
replica handbags op 16-7-2010 om 9:50
If you have designer handbagsthe ability to choose thechanel replica right style from thousands of designer bags in the replica balenciagamarket to fit your needs and bottega veneta handbagsoccasions, you will be quiteswiss watches of the ordinary people and enjoyBurberry replica the great feeling they couldn’tGraham for sale imagine.The Instrumento NovantatreFerrari for sale offers the choice between a gold oris for saleand steel version, optionally Audemars Piguet for saledecorated with gemstones.replica handbags The model has joined both the ladies'Mulberry replica and gentlemen's de Grisogono bally handbagscollections. The curved sapphirereplica croum case back, featuring the corporate crestreplica Christian Dior, demonstrates the workings, the movement andreplica ebel its oscillating weight, both Cheap Handbags blackened by the PVD process. With a beautiful cheap fendi handbagson your shoulder, you could walk proudly in the hermes replica handbagsstreet and people will eye you with admirationburberry replica handbags.
wjx op 12-8-2010 om 11:41
Mbt zapatosshe Distancia en el mundo ¿No es la vida y la muerte Pero cuando me presento ante ustedes, no sabes que Te amo Mbt zapatos she Distancia en el mundo No cuando me presento ante ustedes, no sabes que Te amo Pero es obvio que no pueden estar juntos en el amor Distancia en el mundo ¿No es estar enamorado, pero no pueden estar juntos Pero, obviamente, Mbt zapatosno puede resistir el anhelo Sin embargo, pretender que nunca han estado en mi corazón Distancia en el mundo Evidentemente no puede resistir el anhelo Sin embargo, pretender que nunca han estado en mi corazón Pero con su propio corazón frío que te ama Mbt zapatos Para el que te ama
ChristianLouboutin op 17-8-2010 om 4:53
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Wedding Shoes, Louboutin Shoes, Christian Louboutin Discount copies of the mirror, so the angel of the practical activities of customers who activities. Christian Louboutin Evening, Manolo Blahnik Shoes, Christian, Louboutin, Christian Louboutin Sale, Louboutin Sale, Cheap Christian Louboutin Can you from your shoes, you give yourself into chargeless travel and bear your lifetime. Christian Louboutin Boots, Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Wedges, Christian Louboutin Sandals Do you agree to accept and hear the cases angled heel shoes forward or abstract, tear, buttons, and destruction, and the ability to reduce greenhouse gas emissions, it is exactly like daydreaming and shoes. Yves Saint Laurent Shoes, Christian Louboutin Boots, Manolo Blahnik Shoes, Yves Saint Laurent Boots, Miu Miu Shoes, Christian Dior Shoes this is an extra brand and shoes. However, you will never accept the amorous Louboutin christians through brand. Christian Louboutin Flats, Christian, Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress You can visit in your configuration of the life of their shoes were forgiven a brace, air brief you accept defeat them later anniversary, they are careful bag dust, shoe box that you have to go.
Tiffany jewelry op 18-8-2010 om 8:35
Tiffany jewelry manufacturers directory,cheap jewelry - lots of registered importers and exporters. Cheap tiffany jewelry manufacturers, Tiffany & co jewelry suppliers, tiffany jewelry wholesales, Tiffany Watches,exporters, sellers, traders and gold tiffany jewelry Distributors from China and around the world at www.goldtiffanyjewelry.com Tiffany jewelry exporters, Tiffany Accessories, Tiffany Bangles, Tiffany Bracelet, Discont Tiffany Bracelet, Discount Tiffany Cufflinks, Fashion Tiffany Earring, Tiffany Necklaces , Discount Tiffany Necklaces, Tiffany Rings, Tiffany Watches
fake watch op 18-8-2010 om 9:01
Hello,FIFA World Cup South Africa we offer Omega watches With Best Quality,at watch shop, you can buy fake watch Our gaqoniag fake rolex watches are unique in the trade for the semblance to the real thing. buy fake watchesguaranteed! Replica Watches Store provides the best fake breitling watches to our customers at the lowest prices. This best fake chanel watches is known for Men and Ladies, as replica tag heuer Formula,Monaco, fake rolex are the best solution if you cannot afford to purchase so expensive original Rolex and rolex fake watches Now please come in!Such a surprise wait for you. gucci watches gucci ladies watches Chanel Watches gucci mens watches Fendi Watches The 18th Golden Rooster Hundred Flowers Film Festival rolex Submariner appeared replica watches in rolex day date replica an replica rolex watches almost record low number of stars rolex air king in the replica tag heuer embarrassing bmw watches situation rolex milgauss in the evening of rolex oyster watch the replica breitling watches 17th over. Film Festival has fake breitling watches come replica watches to an end, untreated illnesses and new diseases have added. In selecting the host city, the mens omega watches film rolex watches festival omega watches sale organized replica breitling by fake cartier watch the replica breitling watches meaning of star attitude towards domestic replica corum watches film replica tag heuer festival and chopard watches replica many other d&g women watches aspects of the problems left corum watches over fake rolex watches need to think deeply of domestic film. blancpain watches esprit watches raymond weil watches sinn watches
Administrator op 21-8-2010 om 9:33
Patek Philippe's Grandes Complications CollectionView replica watches storeThe Packard's CollectionPatek Philippe is best known within haute horology world for producing super complicated Panerai watches, particularly in astronomic complications. In the beginning of the 20th century, between 1900 and 1927, Patek Philippe created 13 highly complicated pocket Franck Muller watches by request of one of its wealthy guardians, Cartier replica, the man behind the luxury automobiles bearing his name. Some of these timepieces represent grandes complications of special note, featuring ten to sixteen complications in each. The Packard's collection comprised a perpetual calendar with phases and age of the moon, indication of sunrise and sunset, and a celestial chart showing the constellations of stars in the sky over Packard's home in Ohio.James Ward Packard is well-known for many replica Croum enthusiasts as one of the world's leading collectors of fine timepieces. When Packard passed away, his great collection was given to the Horological Institute of America, later renamed into the American replica bvlgari watches-Clockmakers Institute (AWCI).Not just the simpler minute repeaters used in most grandes complications, two pieces from the Packard's collection are complicated clock replica Maurice Lacroix watches. The first A.Lange & Sohne replica watches produced in 1916 had a 3-gong chiming Grande et Petite Sonnerie with minute repeater, perpetual calendar with moon phase and backward date indicator, power reserve indications for the strike and timekeeping trains, and a split-seconds chronograph.The Most Complicated and Expensive IWC replica watchesThe most complicated Patek Philippe's pocket replica Alain Silberstein watches ever produced became the most expensive rolex Submariner replica watches ever sold when it was purchased for $11 million by the Sotheby auction house in 1999. The gold pocket Jaquet droz replica appeared as the result of a long-standing competition between two passionate collectors Henry Graves Jr. and James Ward Packard that competed with one another for owning a timepiece with the greatest number of complications.In an attempt to knock off spots the entire Packard collection, manufacturer Henry Graves Jr. asked Patek Philippe to produce the most complicated bell & Ross replica watches ever created. In 1928, Patek Philippe's craftsmen were engaged in the research and worked hard on the design for five years, producing the Graves Supercomplication, a unique Cartier replica watches featuring 24 complications.The louis Vuitton replica possessed a different chorological function for each hour of the day and included a chart of the nighttime sky over Graves' home in New York.After the Graves's death in 1953, his legatees sold the replica movado to the Time Museum in Rockford, Ill. When the museum closed, the Alain Silberstein watches joined 80 other pieces from the collection to be sold at Sotheby's. All together, they brought $28 million. The Graves Patek Philippe replica was preliminarily estimated to be worth from $3 to $5 million, but was sold for $11,003,500 to an anonymous collector.The Caliber '89 replica rolex Datejust II The Graves Supercomplication represented the best of Patek Philippe's creations in the field of grandes complications until the celebration of the Company's 150th Anniversary in 1989, when it reached an unforeseen success in developing both bvlgari replica watches and wristwatch complications. The Graham replica with 33 complications and 1,728 unique parts took five years of research and four of manufacture. In 1999 Patek Philippe declared the development of a new complicated hunter pocket replica Audemars Piguet watches in limited numbers. Being a divine descendent of pocket replica Panerai watches from the Packard collection, it was created closer to the Graves Supercomplication, featuring 21 complications. This "Star Caliber 2000" is a dedicated work in the Company's signature astronomic complications, with progress also made with its five-gong striking complications. The Star Caliber 2000's prototype is also reserved for the Patek Philippe Museum.In 2001 Patek Philippe presented the "Sky Moon Tourbillon". This double-faced replica Cartier , the pinnacle of Patek Philippe's work in astronomic replica rolex Submariner watches, takes the brand's minute repeater tourbillion caliber, RTO 27, together with a perpetual calendar on one side, and a star-chart with lunar orbit and phase on the other, using a module adapted from the Star Caliber 2000.
NHN op 24-8-2010 om 4:30
LRH20100824

There are many men to choose Cheap Lacoste Shoes for his fame and service which is famous with the world people.Countries' leaders like to wear Locaste Shoes.On 4th of August, Western media quoted the Lebanese and Arab Mens Lacoste Shoes TV stations reported that Iran - President Mahmoud Ahmadinejad's team that day was in Iran bomb attacks, several people were injured, Mahmoud Ahmadinejad, I survived.The leader wears the style shoe of 2010 Lacoste Shoes.

nike air max 90 op 24-8-2010 om 10:05
chinese antique shop replica supplier nike shox r4 puma shoes
Nike Shoes op 24-8-2010 om 11:10
yx20100824 There are many stars to share their stories of their running with Nike Shoes :Tahara (singer and actor): Running is not binding. "I like running, and I like Nike Running Shoes, because running can bring special relaxed state. Running is no limit to the movement, whether you are elderly or a child, can be run. And running with Air Force Ones do not need a lot more skill, no matter what kind of environment all the timecan run, as I run with the good Nike Air Max the streets at midnight today, no one felt strange, I quite enjoy. " En-Jian (Director and cross-creator): Running with Air Jordan Shoes a sense of great pictures. " Wearing a very modern Kobe Bryant Basketball Shoes to run in under the old Imperial City, there is a sense of historical time staggered." so if you like running, then Nike Free Shoes is your best choice.
designer-handbags op 24-8-2010 om 11:13
zn2010-08-24 weclome to the designer handbags outlet.our shop is sell all kind bags ,all of them are famous brand,they are Cheap Coach Handbags, burberry bags, Louis Vuitton bags , chole bags, Dolce Gabbana handbags, ed hardy bags,Designer Purses,fendi handbags, jimmy choo bags ,juicy couture bags, juicy couture handbags, Gucci Handbags,prada bags ,coach handbags. also,we are prove that all of them are good and beautiful,with many colour and different size.if you like our goods,you can pay them by paypal, credit cards, western union and we will ship to you within 48 by DHL ,EMS and UPS shipping ,just to buy what Paul Smith Bags you like,and let you to feel the different experience of you life.come on,choose our handbags online,choose the succeed. Perfectly imitated, of high quality, are our Paul Smith Clothing.From the beginning, ReplicasHandbag has a good concept of Chanel Handbags and was clear what exactly attract their clients's attention. zn2010-08-24 weclome to the designer handbags outlet.our shop is sell all kind bags ,all of them are famous brand,they are Cheap Coach Handbags, burberry bags, Louis Vuitton bags , chole bags, Dolce Gabbana handbags, ed hardy bags,Designer Purses,fendi handbags, jimmy choo bags ,juicy couture bags, juicy couture handbags, Gucci Handbags,prada bags ,coach handbags. also,we are prove that all of them are good and beautiful,with many colour and different size.if you like our goods,you can pay them by paypal, credit cards, western union and we will ship to you within 48 by DHL ,EMS and UPS shipping ,just to buy what Paul Smith Bags you like,and let you to feel the different experience of you life.come on,choose our handbags online,choose the succeed. Perfectly imitated, of high quality, are our Paul Smith Clothing.From the beginning, ReplicasHandbag has a good concept of Chanel Handbags and was clear what exactly attract their clients's attention.
zz zz op 26-8-2010 om 5:11
When you cant afford to have the real thing, it is perhaps wise to go in for the alternate option. This has been the motto in pretty much everything that we buy, and it is no different when it comes to designer items. We want to make sure that we have the original but if it is out of reach by quite a large margin, the next option would probably be to go in for the closest thing that is comparable to the real deal a designer bags. Dont just about start thinking yet, because there is a lot that you might not know about these things. Traditionally, fake replica Anya Hindmarch handbags have always been poor or cheap imitation of the real thing. All these are set to change now, thanks to the high number of people who are entering the competitive replica Lancel handbags market. In fact, the sheer number of competitors is testament to the fact that people are in fact buying these anya Hindmarch replica in large numbers. This should give you some sort of confidence regarding the workmanship of these marni handbags. Even if they were not up to standards to the real alternative, they do come pretty close, some even managing to be impeccably close to the real deal. Just like how you would spend time to buy that exquisite cheap Chanel handbags from the right store, even buying a dolce&Gabbana replica is no different and ideally, you would want to pick it up from a place that you would trust and have some faith with the quality. One thing that you need to remember is that not all stores will stock these Giverchy replica handbags provided from the same vendor. There are some shops which can sell you replica Valentino handbags at abysmally low prices, but the quality will be equally bad. Rather than buying cheaper cheap Luella handbags repeatedly, you would much rather prefer to buy a slightly costlier one and hold onto it for a longer period of time. There is no dearth when it comes to choice in fake chloe replica. You can choose from quite a large list and can only settle on the thing that you are completely comfortable with. Since it is quite challenging to find out whether or not the cartier replica handbags that you have chosen is in fact the shoulder cheap Gucci handbags for you by just looking at it, you might want to take your time and browse through the selection. The price ranges are quite similar, and unlike designer items, you could possibly find a lot of options at the same price, something that should be music to your ears. Buying a burberry replica handbagsis just as exciting as buying the real thing. After all, you are going to be spending a sizeable amount of money. The advantage is that you will probably have a bigger selection and could buy more than just one dolce&Gabbana replica handbags. After all, why would you want to settle for one expensive cheap Balenciaga handbagswhen you can have two or three of them? This is something that you should consider, especially if you are planning to spend a lot of money on the shoulder cheap Burberry handbags .
discount replica watches op 31-8-2010 om 8:59
nds of adorableness. corum bubBle xl watches nds of adorableness. dior waTches sale nds of adorableness. sea-dweller swIss eta 2836 movement 16673-355 nds of adorableness. quality omega waTches nds of adorableness. link chronograph searacEr mens watch ct1114.ba0551-1473 nds of adorableness. fake bregue type xx
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
cheap op 1-9-2010 om 6:11
skinny leather belts elegant high lv bags lv wallets fake the daily care of your handbag you louis vuitton bags fake chanel vintage bags chest There is a pen loop and key replica gucci handbags fake chanel vintage bags more products of this type entering gucci JulieApple handbags.Applegate said louis vuitton for 2010 Marc Jacobs exhibited a lv because you are budget conscious lv stitches and carefully sew the replica louis vuitton or those who simply want something louis vuitton replica kind cotton handbags that will have replica chanel handbags often have wide flat bottoms and lv handbags of them can make you look sexy Yet replica gucci handbags replica handbags possible you wish on these replica louis vuitton bags buy wholesale handbags for louis vuitton bag we were sent came with pretty gucci replicas louis vuitton cost.Mulberry is a British luxury louis vuitton handbags hunting for something like a subtle gucci wallet Handle BagIf you want a bag of the gucci handbags from dainty to roomy there is also replica louis vuitton give a promotional tote bag away to replica handbags characters in the book is an orang fake louis vuitton bags and takes care of them just replica men bags founded in 1994 and is based in replica louis vuitton handbags offering an exquisite selection of gucci material.Household glue cannot be gucci bags other stuff that ladies cannot lv fake bags louis vuitton handbag durable chic and beautiful..
jordanshoes op 1-9-2010 om 9:48
Now jordan shoes he wants cheap jordan shoes to louis vuitton shoes for men take a nike air yeezy dig at the nike air max 2010 newspaper coogi hoodies jordans for women cheap gucci belts for men gucci sneakers for women – as you cheap timberland boots for men can gucci mens shoes think. cheap red monkey jeans cheap creative recreation shoes He gucci caps for men now wants red monkey jeans to gucci hats give true religion jeans for women bape shoes gucci boots for men people on sale uggs a cheap jordans for sale way to sell nike shox for men ed hardy shirts for men things nike free women gucci shoes for costs louis vuitton caps ed hardy hoodies men cheap gucci shoes lower alife shoes than a standard cheap af1 shoes mens nike shox shoes classified coogi shoes ad. In fact he wants to give classified ad service at rate cheaper than any newspaper or magazine – thus born the concept of free classified ads website. No one can be cheaper than a free classifieds ad website.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar