Autor Beitrag
ACID
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 53


D7 Pers
BeitragVerfasst: Do 28.07.05 08:25 
Habe den quellcode von www.delphi-forum.de/viewtopic.php?t=16228 ausprobiert aber der zeigt mir nichts an. Dann habe ich den Code vom Easy-Helper von www.delphi-total.de/eh/ ausprobiert und der zeigt mir auch nichts an.

Leider weiss ich nicht mehr weiter. Kann mir jemand weiterhelfen, oder hat jemand ein kleines Demoprogramm mit Source-Code wo dies beschrieben ist?

Bitte helft mir!!!
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 480

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 28.07.05 08:35 
Der beschriebene Code soll ja auch gar nichts anzeigen. Er soll nur was auslesen.
Anzeigen geht z.B. so:
ausblenden Delphi-Quelltext
1:
2:
mp3Info(myfilename,mpeginfo,ID3v2Tag,id3v1tag); 
showmessage(ID3v1Tag.Artist);

_________________
We are, we were and will not be.
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Do 28.07.05 09:23 
Beispielprogramme und ein paar Units dazu findest du hier (Ich glaube das war die, ich kann gerade nicht nachgucken, da die Seite gerade offline ist).
BennyM
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 45



BeitragVerfasst: Do 04.08.05 10:26 
es gibt auch bei den jedi komponenten im register "jv non-visual" eine komponente dafür
F34r0fTh3D4rk
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 5284
Erhaltene Danke: 27

Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
BeitragVerfasst: Do 04.08.05 11:15 
der code ausm easy helper funktioniert, nur haben die Strings immer die gleiche länge (30), deshalb ist es nicht so einfach Interpret und Titel zusammenzufügen:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
Type
  TID3Tag = record
    ID: string[3];
    Titel: string[30];
    Artist: string[30];
    Album: string[30];
    Year: string[4];
    Comment: string[30];
    Genre: byte;
    dauer: longint; //in sekunden;
    sample: integer;
    bits: byte;
    vers: byte;
    layers: byte;
end;

const
  MPEG_BIT_RATES : array[1..3of array[1..3of array[0..15of word =
  { Version 1, Layer I }
    (((0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),
  { Version 1, Layer II }
    (0,32,48,56648096,112,128,160,192,224,256,320,384,0),
  { Version 1, Layer III }
    (0,32,40,4856648096,112,128,160,192,224,256,320,0)),
  { Version 2, Layer I }
    ((0,32,4856648096,112,128,144,160,176,192,224,256,0),
  { Version 2, Layer II }
    (08,16,2432404856648096112,128,144,160,0),
  { Version 2, Layer III }
    (08,16,2432404856648096112,128,144,160,0)),
  { Version 2.5, Layer I }
    ((0,32,4856648096,112,128,144,160,176,192,224,256,0),
  { Version 2.5, Layer II }
    (08,16,2432404856648096112,128,144,160,0),
  { Version 2.5, Layer III }
    (08,16,2432404856648096112,128,144,160,0)));

  sample_rates: array[1..3of array [0..3of word=
    ((44100,48000,32000,0),
    (22050,24000,16000,0),
    (11025,12000,8000,0));

Es folgt die eigentliche "Auslese" Procedure:

var
  ID3Tag: TID3Tag;

procedure Lese_ID3Tag(Filename: string);
var
  Buffer: array[1..128of char;
  F: File;
  mp3hdrread: array[1..4of byte;
  mp3hdr: longint absolute mp3hdrread;
  tempbyte, bitrateindex, versionindex: byte;
  bitrate, version, layer, groese: longint;
begin
  AssignFile(F, Filename);

  Reset(F,1);
  groese:=filesize(f);
  blockread(f,mp3hdrread,4);
  tempbyte:=mp3hdrread[1];
  mp3hdrread[1]:=mp3hdrread[4];
  mp3hdrread[4]:=tempbyte;
  tempbyte:=mp3hdrread[2];
  mp3hdrread[2]:=mp3hdrread[3];
  mp3hdrread[3]:=tempbyte;

  reset(f,1);
  Seek(F,FileSize(F)-128);
  BlockRead(F, Buffer, SizeOf(Buffer));
  CloseFile(F);
  bitrateindex:=((mp3hdr shr 12and $F);
  versionindex:=((mp3hdr shr 19and $3);

  case versionindex of
    0: version:=3;
    1: version:=0//unbekannt
    2: version:=2;
    3: version:=1;
  end;

  layer:=4-((mp3hdr shr 17and $3);

  if version<>0 then
    bitrate:=MPEG_BIT_RATES[version][layer][bitrateindex]
  else
    bitrate:=128;

  with ID3Tag do
  begin
    ID:=copy(Buffer,1,3);
    Titel:=copy(Buffer,4,30);
    Artist:=copy(Buffer,34,30);
    Album:=copy(Buffer,64,30);
    Year:=copy(Buffer,94,4);
    Comment:=copy(Buffer,98,30);
    Genre:=ord(Buffer[128]);
    sample:=sample_rates[version][((mp3hdr shr 10and $3)];
    dauer:=(groese*8div ((bitrate)*1000);
    bits:=bitrate;
    vers:=version;
    layers:=layer;
  end;
end;

Die Procedure kann man dann folgendermaßen Anwenden:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    Lese_ID3Tag(OpenDialog1.FileName);

    Titel.Caption:=ID3Tag.Titel;
    Artist.Caption:=ID3Tag.Artist;
    Album.Caption:=ID3Tag.Album;
    Year.Caption:=ID3Tag.Year;
    Comment.Caption:=ID3Tag.Comment;
    Genre.Caption:=IntToStr(ID3Tag.Genre);

    Duration.Caption:=IntToStr(id3tag.dauer);
    Samplerate.Caption:=IntToStr(id3tag.sample);
    Bitrate.Caption:=IntToStr(id3tag.bits);
    Layer.caption:=IntToStr(id3tag.layers);
    Yersion.caption:=IntToStr(id3tag.vers);
  end;
end;

Ich kürze das so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function Cut(str: string): string;
var
  i: integer;
begin
  for i := length(str) downto 1 do
    if ord(str[i]) <> 0 then
      begin
        result := copy(str, 1, i);
        break;
      end;  
end;

ausblenden Delphi-Quelltext
1:
2:
3:
  Artist := Cut(ID3Tag.Artist);
  Title := Cut(ID3Tag.Title);
  Fullname := Artist + ' - ' + Title;


Zuletzt bearbeitet von F34r0fTh3D4rk am Do 04.08.05 16:04, insgesamt 1-mal bearbeitet
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Do 04.08.05 11:27 
Diese Variante würde ich dir auch noch aus einem anderem Grund nicht empfehlen. Es gibt bei mp3s auch eine freeformat-Komprimierung (freie wählbare Bitrate) und da kann ich dir nicht sagen wie der Code reagiert.
F34r0fTh3D4rk
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 5284
Erhaltene Danke: 27

Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
BeitragVerfasst: Do 04.08.05 11:39 
Hast du da ne bessere Lösung ? (heeer damit ;) )
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 480

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 04.08.05 13:36 
@F34r0fTh3D4rk: Der Codeteil zum Bestimmen der Länge funktioniert grob gesagt nur in einigen wenigen Spezialfällen.

Erstens: Es wird davon ausgegangen, dass der erste MPEG-HEader am Anfang der Datei steht (zumindest lese ich das aus dem Code raus.)
Das muss aber nicht so sein, und ist sehr oft auch nicht so. Sehr häufig steht dort der ID3v2-Tag, der fast beliebig lang sein kann (afaik bis zu 256MB) oder - wenn dieser nachträglich gelöscht wurde - unter Umständen nur entsprechend viele 0en.

Zweitens: Bei VBR-Files ist der erste MPEG-Fram kein echter Audio-Frame, sondern wird von vielen Encodern als Speicherort für diverse andere Dinge benutzt, z.B. durchschnittliche Bitrate, Anzahl der Frames (=> Längenberechnung möglich), und ein Sprungstellen-Array. es ist zwar ein gültiger MPEG-Frame mit richtigem Header, aber die Header-Informationen dieses Frames geben keine Info über die eigentliche Datei.

Die oben verlinkte Prozedur von mir ist zwar ein Monstrum, aber diese Dinge werden dort berücksichtigt.

_________________
We are, we were and will not be.
F34r0fTh3D4rk
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 5284
Erhaltene Danke: 27

Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
BeitragVerfasst: Do 04.08.05 15:22 
welche ? die aus dem ersten Beitrag ? Die unit ?

Kompiliert er nicht, TMPEGHeader kennt er zB nicht, trim, exists, dateigroesse und sample_rates ebenfalls :?

eine andere unit sieht so aus:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
unit ID3;

interface

{$A+} { Honor packed }
{$Z1} { Byte storage for small enumerated types }

type
  ID3Genre = (
    id3gBlues, id3gClassicRock, id3gCountry, id3gDance, id3gDisco,
    id3gFunk, id3gGrunge, id3gHipHop, id3gJazz, id3gMetal, id3gNewAge,
    id3gOldies, id3gOther, id3gPop, id3gRNB, id3gRap, id3gReggae,
    id3gRock, id3gTechno, id3gIndustrial, id3gAlternative, id3gSka,
    id3gDeathMetal, id3gPranks, id3gSoundtrack, id3gEuroTechno,
    id3gAmbient, id3gTripHop, id3gVocal, id3gJazzFunk, id3gFusion,
    id3gTrance, id3gClassical, id3gInstrumental, id3gAcid, id3gHouse,
    id3gGame, id3gSoundClip, id3gGospel, id3gNoise, id3gAlternRock,
    id3gBass, id3gSoul, id3gPunk, id3gSpace, id3gMeditative,
    id3gInstrumentalPop, id3gInstrumentalRock, id3gEthnic, id3gGothic,
    id3gDarkwave, id3gTechnoIndustrial, id3gElectronic, id3gPopFolk,
    id3gEurodance, id3gDream, id3gSouthernRock, id3gComedy, id3gCult,
    id3gGangsta, id3gTop40, id3gChristianRap, id3gPopFunk, id3gJungle,
    id3gNativeAmerican, id3gCabaret, id3gNewWave, id3gPsychadelic,
    id3gRave, id3gShowtunes, id3gTrailer, id3gLoFi, id3gTribal,
    id3gAcidPunk, id3gAcidJazz, id3gPolka, id3gRetro, id3gMusical,
    id3gRockNRoll, id3gHardRock, id3gFolk, id3gFolkRock,
    id3gNationalFolk, id3gSwing, id3gFastFusion, id3gBebob, id3gLatin,
    id3gRevival, id3gCeltic, id3gBluegrass, id3gAvantgarde,
    id3gGothicRock, id3gProgressiveRock, id3gPsychedelicRock,
    id3gSymphonicRock, id3gSlowRock, id3gBigBand, id3gChorus,
    id3gEasyListening, id3gAcoustic, id3gHumour, id3gSpeech,
    id3gChanson, id3gOpera, id3gChamberMusic, id3gSonata, id3gSymphony,
    id3gBootyBass, id3gPrimus, id3gPornGroove, id3gSatire, id3gSlowJam,
    id3gClub, id3gTango, id3gSamba, id3gFolklore, id3gBallad,
    id3gPowerBallad, id3gRhythmicSoul, id3gFreestyle, id3gDuet,
    id3gPunkRock, id3gDrumSolo, id3gAcapella, id3gEuroHouse,
    id3gDanceHall
  );
  ID3Struct = packed record
    Magic: array [0..2 ] of Char;
    Title: array [0..29of Char;
    Artist: array [0..29of Char;
    Album: array [0..29of Char;
    Year: array [0..3 ] of Char;
    Comment: array [0..29of Char;
    Genre: ID3Genre;
  end;

const
  ID3Magic = 'TAG';
  ID3OffsetFromEnd = 128;
  ID3UnknowGenre = ID3Genre(255);
  ID3GenreName: array [ID3Genre] of PChar = (
    'Blues''Classic Rock''Country''Dance''Disco''Funk''Grunge',
    'Hip-Hop''Jazz''Metal''New Age''Oldies''Other''Pop''R&B',
    'Rap''Reggae''Rock''Techno''Industrial''Alternative''Ska',
    'Death Metal''Pranks''Soundtrack''Euro-Techno''Ambient',
    'Trip-Hop''Vocal''Jazz+Funk''Fusion''Trance''Classical',
    'Instrumental''Acid''House''Game''Sound Clip''Gospel',
    'Noise''AlternRock''Bass''Soul''Punk''Space''Meditative',
    'Instrumental Pop''Instrumental Rock''Ethnic''Gothic',
    'Darkwave''Techno-Industrial''Electronic''Pop-Folk',
    'Eurodance''Dream''Southern Rock''Comedy''Cult''Gangsta',
    'Top 40''Christian Rap''Pop/Funk''Jungle''Native American',
    'Cabaret''New Wave''Psychadelic''Rave''Showtunes''Trailer',
    'Lo-Fi''Tribal''Acid Punk''Acid Jazz''Polka''Retro',
    'Musical''Rock & Roll''Hard Rock''Folk''Folk-Rock',
    'National Folk''Swing''Fast Fusion''Bebob''Latin''Revival',
    'Celtic''Bluegrass''Avantgarde''Gothic Rock''Progressive Rock',
    'Psychedelic Rock''Symphonic Rock''Slow Rock''Big Band',
    'Chorus''Easy Listening''Acoustic''Humour''Speech''Chanson',
    'Opera''Chamber Music''Sonata''Symphony''Booty Bass''Primus',
    'Porn Groove''Satire''Slow Jam''Club''Tango''Samba',
    'Folklore''Ballad''Power Ballad''Rhythmic Soul''Freestyle',
    'Duet''Punk Rock''Drum Solo''Acapella''Euro-House''Dance Hall'
    );

function GetMP3Infos(FN:String): id3struct;

implementation

function GetMP3Infos(FN:String): id3struct;
var
  F: File of Byte;
  Tag: ID3Struct;
  HasTag: Boolean;
  GenreName: String;
  NewFN: String;
const
  Tab = #9;
begin
  AssignFile(F, fn);
  try
    Reset(F);
    try
      Seek(F, FileSize(F) - ID3OffsetFromEnd);
      BlockRead(F, Tag, SizeOf(Tag));
      HasTag := Tag.Magic = ID3Magic;
      if not HasTag then
      begin
        FillChar(Tag, Sizeof(Tag), ' ');
        Tag.Magic := ID3Magic;
        Tag.Genre := ID3UnknowGenre;
      end;
      CloseFile(f);
      Result:=Tag;
    except
    end;
  except
  end;
end;

end.


ok jetzt sinds nur noch 2 compiler fehler:

Zitat:

[Fehler] Unit1.pas(131): Bei der vorherigen Deklaration von GetFramelength wurde die Direktive 'overload' nicht angegeben

[Fehler] Unit1.pas(312): Undefinierter Bezeichner: 'exists'

[Fehler] Unit1.pas(317): Undefinierter Bezeichner: 'exists'

[Fehler] Unit1.pas(342): Undefinierter Bezeichner: 'exists'

[Fehler] Unit1.pas(346): Undefinierter Bezeichner: 'exists'


Das mit dem exists konnte ich beheben, bleibt noch das:
Zitat:

[Fehler] Unit1.pas(131): Bei der vorherigen Deklaration von GetFramelength wurde die Direktive 'overload' nicht angegeben


hab beiden overload angefügt und er kompiliert ;)

so scheint es korrekt zu sein:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262:
263:
264:
265:
266:
267:
268:
269:
270:
271:
272:
273:
274:
275:
276:
277:
278:
279:
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
292:
293:
294:
295:
296:
297:
298:
299:
300:
301:
302:
303:
304:
305:
306:
307:
308:
309:
310:
311:
312:
313:
314:
315:
316:
317:
318:
319:
320:
321:
322:
323:
324:
325:
326:
327:
328:
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
343:
344:
345:
346:
347:
348:
349:
350:
351:
352:
353:
354:
355:
356:
357:
358:
359:
360:
361:
362:
363:
364:
365:
366:
367:
368:
369:
370:
371:
372:
373:
374:
375:
376:
377:
378:
379:
380:
381:
382:
383:
384:
385:
386:
387:
388:
389:
390:
391:
392:
393:
394:
395:
396:
397:
398:
399:
400:
401:
402:
403:
404:
405:
406:
407:
408:
409:
410:
411:
412:
413:
414:
415:
416:
417:
418:
419:
420:
421:
422:
423:
424:
425:
426:
427:
428:
429:
430:
431:
432:
433:
434:
435:
436:
437:
438:
439:
440:
441:
442:
443:
444:
445:
446:
447:
448:
449:
450:
451:
452:
453:
454:
455:
456:
457:
458:
459:
460:
461:
462:
463:
464:
465:
466:
467:
468:
469:
470:
471:
472:
473:
474:
475:
476:
477:
478:
479:
480:
481:
482:
483:
484:
485:
486:
487:
488:
489:
490:
491:
492:
493:
494:
495:
496:
497:
498:
499:
500:
501:
502:
503:
504:
505:
506:
507:
508:
509:
510:
511:
512:
513:
514:
515:
516:
517:
518:
519:
520:
521:
522:
523:
524:
525:
526:
527:
528:
529:
530:
531:
532:
533:
534:
535:
536:
537:
538:
539:
540:
541:
542:
543:
544:
545:
546:
547:
548:
549:
550:
551:
552:
553:
554:
555:
556:
557:
558:
559:
560:
561:
562:
563:
564:
565:
566:
567:
568:
569:
570:
571:
572:
573:
574:
575:
576:
577:
578:
579:
580:
581:
582:
583:
584:
585:
586:
587:
588:
589:
590:
591:
592:
593:
594:
595:
596:
597:
598:
599:
600:
601:
602:
603:
604:
605:
606:
607:
608:
609:
610:
611:
612:
613:
614:
615:
616:
617:
618:
619:
620:
621:
622:
623:
624:
625:
626:
627:
628:
629:
630:
631:
632:
633:
634:
635:
636:
637:
638:
639:
640:
641:
642:
643:
644:
645:
646:
647:
648:
649:
650:
651:
652:
653:
unit Unit1;

interface

uses sysutils;

type  
  TBuffer = Array of byte;  
  TMPEGHeader = array[1..4of byte;  
  TID3v1Tag = record
    exists: boolean;
    ID: string[3];  
    Titel: string[30];
    Artist: string[30];  
    Album: string[30];  
    Year: string[4];  
    Comment: string[30];  
    Track: byte;  
    Genre: byte;  
  end;  

 
  TID3v2Tag = record
    exists: boolean;
    artist: string;
    titel: string;  
    album: string;  
  end;  

 
  TMPEGInfo = record  
    dateigroesse:longword;  
    Position: integer;  
    Version: integer;  
    Layer: integer;  
    Protection: boolean;  
    Bitrate: integer;  
    Samplerate: integer;  
    ChannelMode: byte;  
    Extension: byte;  
    Copyright: boolean;  
    Original: boolean;  
    Emphasis: byte;  
    Frames: longint;  
    Dauer: longint;  
    VBR: boolean;  
  end

const  
  MPEG_BIT_RATES: array[1..3of array[1..3of array[0..15of word = ((  
      { Version 1, Layer I }  
      (0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),  
      { Version 1, Layer II }  
      (0,32,48,56648096,112,128,160,192,224,256,320,384,0),  
      { Version 1, Layer III }  
      (0,32,40,4856648096,112,128,160,192,224,256,320,0)),  
      { Version 2, Layer I }  
      ((0,32,4856648096,112,128,144,160,176,192,224,256,0),  
      { Version 2, Layer II }  
      (08,16,2432404856648096112,128,144,160,0),  
      { Version 2, Layer III }  
      (08,16,2432404856648096112,128,144,160,0)),  
      { Version 2.5, Layer I }  
      ((0,32,4856648096,112,128,144,160,176,192,224,256,0),  
      { Version 2.5, Layer II }  
      (08,16,2432404856648096112,128,144,160,0),  
      { Version 2.5, Layer III }  
      (08,16,2432404856648096112,128,144,160,0)  
    ));  

 
  SAMPLE_RATES: array[1..3of array [0..3of word = (  
      (4410048000320000),  
      (2205024000160000),  
      (1102512000,  80000)  
    );  

 
  CHANNEL_MODES: array[0..3of string = (  
      'Stereo''Joint stereo''Dual channel (Stereo)''Single channel (Mono)'  
    );  

 
  EXTENSIONS: array[1..3of array [0..3of string = (  
      ('bands 4 to 31''bands 8 to 32''bands 12 to 31''bands 16 to 31'),  
      ('bands 4 to 31''bands 8 to 32''bands 12 to 31''bands 16 to 31'),  
      ('IS:off, MS:off''IS:on, MS:off''IS:off, MS:on''IS:on, MS:on')  
    );  

 
  EMPHASIS_VALUES: array[0..3of string = (  
      'None''50/15ms','resrerved','CCIT J.17'  
    );  

 
  GENRES: array[0..125of string = (  
      {00-09} 'Blues','Classic Rock','Country','Dance','Disco','Funk','Grunge','Hip-Hop','Jazz','Metal',  
      {10-19} 'New Age','Oldies','Other','Pop','R&B','Rap','Reggae','Rock','Techno','Industrial',  
      {20-29} 'Alternative','Ska','Death Metal','Pranks','Soundtrack','Euro-Techno','Ambient','Trip-Hop','Vocal','Jazz+Funk',  
      {30-39} 'Fusion','Trance','Classical','Instrumental','Acid','House','Game','Sound Clip','Gospel','Noise',  
      {40-49} 'Altern Rock','Bass','Soul','Punk','Space','Meditative','Instrumental Pop','Instrumental Rock','Ethnic','Gothic',  
      {50-59} 'Darkwave','Techno-Industrial','Electric','Pop-Folk','Eurodance','Dream','Southern Rock','Comedy','Cult','Gangsta',  
      {60-69} 'Top 40','Christian Rap','Pop/Funk','Jungle','Native American','Cabaret','New Wave','Psychadelic','Rave','Showtunes',  
      {70-79} 'Trailer','Lo-Fi','Tribal','Acid Punk','Acid Jazz','Polka','Retro','Musical','Rock & Roll','Hard Rock',  
  // Die Folgenden Codes wurden von Nullsoft (Winamp) hinzugefügt, und gehören eigentlich nicht zum "id3 Standard"  
      {80-89} 'Folk','Folk-Rock','National Folk','Swing','Fast Fusion','Bebob','Latin','Revival','Celtic','Bluegrass',  
      {90-99} 'Avantgarde','Gothic Rock','Progressive Rock','Psychadelic Rock','Symphonic Rock','Slow Rock','Big Band','Chorus','Easy Listening','Acoustic',  
      {100-109} 'Humor','Speech','Chanson','Opera','Chamber Music','Sonata','Symphony','Booty Brass','Primus','Porn Groove',  
      {110-119} 'Satire','Slow Jam','Club','Tango','Samba','Folklore','Ballad','Power Ballad','Rhytmic Soul','Freestyle',  
      {120-125} 'Duet','Punk Rock','Drum Solo','A Capella','Euro-House','Dance Hall'  
    );  
  // Andere Werte sind 'unbekannt'

procedure mp3Info(filename:string;  
    var mpeginfo:Tmpeginfo;
    var ID3v2Tag: TID3v2Tag;
    var id3v1tag:Tid3v1tag);

implementation

function GetFramelength(layer:byte;bitrate:integer;Samplerate:integer;padding:byte):integer; overload;
var  
    framelength:integer;  
begin  
    if samplerate=0 then framelength := -2  
    else  
        if Layer=1 then  
            framelength := 12*bitrate*1000 DIV samplerate+padding*4  
        else  
            framelength := 144*bitrate*1000 DIV samplerate+padding;  
    result := framelength;  
end;  

 

 
function GetFramelength(Header:TMPEGHeader):integer; overload;
var mpeginfo:Tmpeginfo;
    framelength:integer;  
    bitrate,version,samplerate:integer;  
    layer:integer;  
    bitrateindex,padding,samplerateindex: byte;  
begin  
    if (header[1]=$FFAND (header[2]>=$E0)then  
    begin  
        //Byte 1 und 2: AAAAAAAA AAABBCCD  
        case ((header[2shr 3and 3of  
            0: version := 3//eigentlich ist das Version 2.5 aber wegen dem Array-Index...  
            1: version := 0//Reserved  
            2: version := 2;  
            3: version := 1;  
        end;  
        Layer := 4-((header[2shr 1and 3);  
        // Byte 3: EEEEFFGH  
        bitrateindex := (header[3shr 4AND $F;  
        bitrate := MPEG_BIT_RATES[version][layer][bitrateindex];  
        padding := (header[3shr 1AND 1;  
        samplerateindex := (header[3shr 2AND 3;  
        samplerate := sample_rates[version][samplerateindex];  
        if samplerate=0 then framelength := -2  
        else  
            if Layer=1 then  
                framelength := 12*bitrate*1000 DIV samplerate+padding*4  
            else  
                framelength := 144*bitrate*1000 DIV samplerate+padding;  
    end else  
    begin  
        framelength := -1;  
    end;  
    result := framelength;  
end;  

 
{Im ID3v2 Tag können verschiedene Codierungen der Strings auftreten}  
function ReadToString(buffer: TBuffer; start:integer; Art:byte; alength: integer):string;  
var tmp:string;  
  tmpws:Widestring;  
begin  
    if alength<0 then  
    begin  
        result:='';  
        exit;  
    end;  
    case Art of  
        0:begin  
           { ISO-8859-1 [ISO-8859-1]. Terminated with $00.}  
           setlength(tmp,alength);  
           move(buffer[start], tmp[1], alength);  
        end;  
       1begin  
           { UTF-16 [UTF-16] encoded Unicode [UNICODE] __with__ BOM. All  
             strings in the same frame SHALL have the same byteorder.  
             Terminated with $00 00. }
  
            setlength(tmpws,alength DIV 2 - 1);  
            move(buffer[start+2], tmpws[1], 2*length(tmpws));  
            tmp := UTF8ToAnsi(UTF8Encode(tmpws));  
        end;  
        2begin  
           { UTF-16BE [UTF-16] encoded Unicode [UNICODE] __without__ BOM.  
             Terminated with $00 00 }
 // LE  
            setlength(tmpws,alength DIV 2);  
            move(buffer[start], tmpws[1], 2*length(tmpws));  
            tmp := UTF8ToAnsi(UTF8Encode(tmpws));  
        end;  
        3begin  
            {03   UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.}  
            setlength(tmp,alength);  
            move(buffer[start], tmp[1], alength);  
            tmp:=UTF8ToAnsi(tmp);  
        end;  
        else tmp:='';  
    end//case  
   result:=trim(tmp);  
end

procedure mp3Info(filename:string;
    var mpeginfo:Tmpeginfo;
    var ID3v2Tag: TID3v2Tag;
    var id3v1tag:Tid3v1tag);
{  Wer sich schonmal mit dem Thema auseinandergesetzt hat, der weiss wahrscheinlich, dass in mp3-Files  
   neben den Audio-Daten eine Menge drin stehen kann. Auch scheint nicht wirklich festgelegt zu sein  
   wo die Audiodaten anfangen.  
   Es ist durch die unvollkommenheit dieser Prozedur durchaus möglich, dass in einem gültigen mp3 kein  
   mpeg-header gefunden wird. In diesem Fall ist der Wert  
   mpegheader.position=-1, und die anderen Werte im mpegheader-Record enthalten sinnlose,falsche Daten.  
   Bei meinen relativ umfangreichen Tests haben ich aber bisher keinen solchen Fall feststellen können ;-)  
 
   Diese Prozedur  
    - liest Teile des ID3v2-Tags aus,  
    - sucht den ersten MPEG-Header der Datei (kann evtl. auch mal schiefgehen)  
    - sucht im ersten MPEG Frame einen Xing-Header, der von vielen Encodern für VBR verwendet wird  
    - berechnet aus den Daten des MPEG- und XING-Headers  
      die Spieldauer des Stückes in Sekunden (und bei VBR durchschnittliche Bitrate)  
    - liest den id3v1-tag aus, falls dieser vorhanden  
      Dabei wird die erweiterte version id3v1.1 erkannt  
 
    für interessierte, was man noch machen könnte:  
    (+) evtl. weitere Header erkennen und berücksichtigen  
        - LAME-Header. Dieser ist wohl ein erweiterter Xing-Header.  
          Am Ende eines mp3s kann auch "LAME-Gedöns" stehen.  
          Zumindest habe ich in einigen Dateien am Ende recht große Blöcke gefunden, in denen immer  
          wieder der Text "LAME3.92" auftaucht, gefolgt von dutzenden Bytes mit gleichem Wert.  
          Leider bisher dazu keine Doku gefunden.  
        - LYRICS3 Tag in den verschiedenen Versionen  
          (http://www.id3.org/lyrics3200.html), der direkt vor dem id3v1 Tag stehen kann  
    (+) der id3v2Tag muss nicht am Anfang stehen, sondern kann auch am Ende sein.  
        Dann muss vom Ende der Datei nach dem id3v2-Footer gesucht werden.  
        Es ist auch möglich, MEHRERE id3v2Tags in einem File zu haben. Der erste sollte dann  
        einen SEEK - Frame haben, der die Position des nächsten angibt...  
 
    Problem ist, dass die ganzen Nicht-Audio-Frames/Header, die ich hier NICHT erkenne, die Spiellängenberechnung  
    verfälschen können. Das gilt besonders für evtl. vorhandene mehrere id3v2Tags, da diese theoretisch  
    eine Größe von je fast 256MB (!!) haben können.  
    Dagegen ist der LyricsTag relativ harmlos, (weniger als 300 kb)  
}
  
var  
    F: File;  
    id3v1_tag:array[1..128of char;  
    mp3_header: TMPEGHeader;  
    buffer: TBuffer;//array of byte;  
    fsize:longint;  
    i:integer;  
    id3v1_size:integer;  
    xing_header_size:integer;  

 
    //ID3v2 Variablen  
    id3_version, id3_number,id3_flags:byte;  
    id3_unsync,id3_extended,id3_footer:boolean;  
    id3_frame_id:string[4];  
    id3_pos:integer;  
    id3_size,id3_extended_size,id3_frame_size:longint;  

 
    //mpeg-header Variablen  
    bitrateindex, versionindex: byte;  
    valid:boolean;  
    position:integer;  
    padding,samplerateindex:byte;  
    framelength:longint;  
    neueframelength:longint;  
    Xing_Offset:integer;  
    Xing_Flags:byte;  
begin  
    xing_header_size:=0;  
    AssignFile(F, filename);  
    FileMode := 0;  
    Reset(F,1);  
    fsize := filesize(f);  
    MPeginfo.dateigroesse:=fsize;  

 
    if fsize=0 then  
    begin  
        CloseFile(F);  
        mpeginfo.position:=-1;  
        exit;  
    end;  

 
    if fsize>=6000 then setlength(buffer,6000else  
        setlength(buffer,fsize);  
    // Die 6000 sind ein willkürlich gewählter Wert.  
    // In den ersten ~6kb sollte ein MPEG-Header stecken, wenn kein ID3v2Tag kommt.  
    // Zwingend notwendig scheint das aber auch nicht zu sein.  

 
    blockread(f,buffer[0],length(buffer));  
    Seek(F,FileSize(F)-128);
    BlockRead(F, id3v1_tag, 128);  
    CloseFile(F);  
    position:=-1;  

 

 
//******************** id3v1 Tag auslesen*****************  
    id3v1tag.exists:=false;
    if (id3v1_tag[1]='T')  
        AND (id3v1_tag[2]='A')  
        AND (id3v1_tag[3]='G')  
    then begin  
        id3v1tag.exists:=true;  
        id3v1tag.id := 'TAG';
        id3v1_size := 128;  
        id3v1tag.Titel := trim(copy(id3v1_tag,4,30));  
        id3v1tag.Artist := trim(copy(id3v1_tag,34,30));  
        id3v1tag.Album := trim(copy(id3v1_tag,64,30));  
        id3v1tag.Year := trim(copy(id3v1_tag,94,4));  
        // Variante id3v1.1 benutzt das letzte Byte des Comment-Feldes für die Track-Nr.  
        // Für eine Unterscheidung, ob das letzte Byte zum Kommentar gehört, oder die Nr. ist,  
        // wird das vorletzte Byte auf 0 überprüft  
        if ord(id3v1_tag[126])=0 then
        begin  
            // id3v1.1, TrackNr gespeichert, ODER: Kommentar kürzer als 28 Zeichen  
            id3v1tag.Comment :=trim(copy(id3v1_tag,98,28));  
            id3v1tag.Track := ord(id3v1_tag[127]);  
        end else begin  
            // id3v1, TrackNr nicht gespeichert, Kommentar 29 oder 30 Zeichen lang  
            id3v1tag.Comment := trim(copy(id3v1_tag,98,30));  
            id3v1tag.Track := 0;  
        end;  
        id3v1tag.Genre := ord(id3v1_tag[128]);  
    end;  

 
//*****************ID3v2 Tag auslesen************************  
    id3v2tag.exists:=False;  
    if (buffer[0]=$49AND (buffer[1]=$44AND (buffer[2]=$33then  
    {'ID3'}  
    begin // ID3v2 Tag gefunden  
        id3v2tag.exists := True;
        id3_version := buffer[3];
        id3_number := buffer[4];  
        id3_flags := buffer[5];  
        // flags: %abcd0000  
        id3_unsync := ((id3_flags shr 7AND 1)=1;  
        id3_extended := ((id3_flags shr 6AND 1)=1;       //sollte 0 sein in V2.2  
        // den experimental-flag ignoriere ich
        id3_footer := ((id3_flags shr 4AND 1)=1;  
        // die Grösse wird in Syncsafe-integer gespeichert, daher nicht *256, sondern *128.  
        id3_size := 2097152 * buffer[6]  
            + 16384 * buffer[7]  
            + 128 * buffer[8]  
            + buffer[9] + 10;  
        if id3_footer then inc(id3_size,10);  

 
        // Auch dieser Wert 3000 ist willkürlich  
        // es ist nämlich nicht sicher, dass nach dem id3v2 Tag direkt der erste mpeg-frame kommt.  
        // ich erkenne somit mp3s, die zwischen id3v2Tag und mpeg-Header weniger als 2996 Bytes Platz lassen  
        if id3_size+3000>length(buffer) then  
        begin  
            Reset(F,1);  
            if fsize>=id3_size+3000 then setlength(buffer,id3_size+3000)  
                else setlength(buffer,fsize);  
            blockread(f,buffer[0],length(buffer));  
            CloseFile(F);  
        end;  

 
        if id3_extended then  
        begin  
            //in den nächsten 4 Bytes steht die Größe des Extended Headers  
            id3_extended_size := 2097152 * buffer[10]  
            + 16384 * buffer[11]  
            + 128 * buffer[12]  
            + buffer[13];  
        end else id3_extended_size := 0;  
        // Den extended Header überspringe ich einfach.  
        // Die Informationen dadrin sind nicht wirklich wichtig für meine Zwecke.  
        // Nach dem optionalen Extended Header kommen die einzelnen Frames, in denen die Informationen stecken  
        // 4 Byte Frame-ID  
        // 4 Byte unsync-Integer-Größenangabe, wieder ohne die 10 Bytes im Frame-Header  
        // 2 Bytes Flag  
        // Daten  

 
        // Bei der Suche des mpeg-Headers nach dem id3v2Tag beginnen  
        position := id3_size-1// position gibt das Ende des id3v2 Tags an  

 
        id3_pos:=10+id3_extended_size;  
        // hier sollte der erste id3v2-Frame anfangen  
        if id3_version=$02 then  
            while (id3_pos<position) do  
            begin  
                id3_frame_id := chr(buffer[id3_pos])                // V2.2: 3 Byte ID  
                    + chr(buffer[id3_pos+1])                      //       3 Byte Frame Size  
                    + chr(buffer[id3_pos+2]);                     //       1 Byte Text-Encoding  
                id3_frame_size := 16384 *buffer[id3_pos+3]         //       xxx Text  
                    + 128 * buffer[id3_pos+4]  
                    + buffer[id3_pos+5] + 6;  
                // auf id3_pos+6 ist nei Textframes der Zeichensatz kodiert,  
                if id3_frame_id='TAL' then  // Album-Name  
                    id3v2Tag.album :=  
                    ReadToString(buffer, id3_pos+7, byte(buffer[id3_pos+6]), id3_frame_size-7);  
                if id3_frame_id='TT2' then  // Titel  
                    id3v2Tag.titel :=  
                    ReadToString(buffer, id3_pos+7, byte(buffer[id3_pos+6]), id3_frame_size-7);  
                if id3_frame_id='TP1' then  // Artist  
                    id3v2Tag.Artist :=  
                    ReadToString(buffer, id3_pos+7, byte(buffer[id3_pos+6]), id3_frame_size-7);  
                inc(id3_pos,id3_frame_size);  
            end  
            else  // Version 2.3 oder höher  
            while (id3_pos<position) do  
            begin  
                id3_frame_id := chr(buffer[id3_pos])               // V2.3+: 4 Byte ID  
                    + chr(buffer[id3_pos+1])                       //        4 Byte Frame Size  
                    + chr(buffer[id3_pos+2])                       //        1 Byte Text-Encoding  
                    + chr(buffer[id3_pos+3]);                      //        xxx Text  
                id3_frame_size := 2097152 * buffer[id3_pos+4]  
                    + 16384 * buffer[id3_pos+5]  
                    + 128 * buffer[id3_pos+6]  
                    + buffer[id3_pos+7] + 10;  
                // auf id3_pos+10 ist nei Textframes der Zeichensatz kodiert,  
                if id3_frame_id='TALB' then  // Album-Name  
                    id3v2Tag.album :=  
                    ReadToString(buffer, id3_pos+11, byte(buffer[id3_pos+10]), id3_frame_size-11);  
                if id3_frame_id='TIT2' then  // Titel  
                    id3v2Tag.titel :=  
                    ReadToString(buffer, id3_pos+11, byte(buffer[id3_pos+10]), id3_frame_size-11);  
                if id3_frame_id='TPE1' then  // Artist  
                    id3v2Tag.Artist :=  
                    ReadToString(buffer, id3_pos+11, byte(buffer[id3_pos+10]), id3_frame_size-11);  
                inc(id3_pos,id3_frame_size);  
            end;  
    end else id3_size := 0;  
//******************* ID3v2Tag fertig gelesen*********************  

 
//*******************Start des MPEG-Headers***********************  
    valid:=false;  
    mpeginfo.position:=-1;  
    // Bei der Suche des mpeg-Headers nach dem id3v2Tag beginnen  
    position:=id3_size-1;  
    while NOT ((valid) or (position>length(buffer)+4)) do  
    begin  
        inc(position);  
        // Ein MPEG Header startet mit 11 gesetzten Bits ($FF E0 = 11111111 11100000)  
        if (buffer[position]=$FFAND (buffer[position+1]>=$E0)  
        then begin  
            valid := true; // erstmal positiv denken, dann überprüfen ;-)  
            // ein mpeg-Header besteht aus 4 Bytes  
            //Byte 1 und 2: AAAAAAAA AAABBCCD  
            //A=1 (11 Sync bytes) am Anfang  
            //B: Version, bei Standard-mp3 (=MPEG1, Layer3) sollte BB=11 sein  
            //C: Layer, III ist CC=01  
            //D: Protection BIT wenn gesetzt, folgt dem mpeg-header 16bit CRC  
            Versionindex := (buffer[position+1shr 3and 3;  
            case versionindex of  
                0: mpeginfo.version := 3//eigentlich ist das Version 2.5 aber wegen dem Array-Index...  
                1: mpeginfo.version := 0//Reserved  
                2: mpeginfo.version := 2;  
                3: mpeginfo.version := 1;  
            end;  
            mpeginfo.Layer := 4-((buffer[position+1shr 1and 3);  
            mpeginfo.protection := (buffer[position+1AND 1)=0;  

 
            // Byte 3: EEEEFFGH  
            // E: Bitrate-Index  
            // F: Samplerate-Index  
            // G: Padding Bit  
            // H: Private Bit  
            bitrateindex := (buffer[position+2shr 4AND $F;  
            mpeginfo.bitrate := MPEG_BIT_RATES[mpeginfo.version][mpeginfo.layer][bitrateindex];  
            if bitrateindex=$F then  
                valid := false; // Bad Value !  
            samplerateindex := (buffer[position+2shr 2AND 3;  
            mpeginfo.samplerate := sample_rates[mpeginfo.version][samplerateindex];  
            padding := (buffer[position+2shr 1AND 1;  

 
            // Byte 4: IIJJKLMM  
            // I: Channel mode  
            // J: Mode extension (for Joint Stereo)  
            // K: copyright  
            // L: original  
            // M: Emphasis  meistens =0  
            mpeginfo.channelmode := ((buffer[position+3shr 6AND 3);  
            mpeginfo.extension := ((buffer[position+3shr 4AND 3);  
            mpeginfo.copyright := ((buffer[position+3shr 3AND 1)=1;  
            mpeginfo.original := ((buffer[position+3shr 2AND 1)=1;  
            mpeginfo.emphasis := (buffer[position+3AND 3);  

 
            // "For Layer II there are some combinations of bitrate and mode which are not allowed."  
            if mpeginfo.layer=2 then begin  
                if (mpeginfo.bitrate=32AND (mpeginfo.channelmode<>3then valid := false;  
                if (mpeginfo.bitrate=48AND (mpeginfo.channelmode<>3then valid := false;  
                if (mpeginfo.bitrate=56AND (mpeginfo.channelmode<>3then valid := false;  
                if (mpeginfo.bitrate=80AND (mpeginfo.channelmode<>3then valid := false;  
                if (mpeginfo.bitrate=224AND (mpeginfo.channelmode=3then valid := false;  
                if (mpeginfo.bitrate=256AND (mpeginfo.channelmode=3then valid := false;  
                if (mpeginfo.bitrate=320AND (mpeginfo.channelmode=3then valid := false;  
                if (mpeginfo.bitrate=384AND (mpeginfo.channelmode=3then valid := false;  
            end;  
//***************damit ist der MPEG Header komplett eingelesen***********************************  

 
            // Framelength bestimmen  
            framelength:=GetFramelength(mpeginfo.layer,  
                                        mpeginfo.bitrate,  
                                        mpeginfo.Samplerate,  
                                        padding);  
            if framelength<=0 then valid := false;  

 
 {Bei (allen/den meisten/vielen/einigen ??) VBR-Files ist der erste MPEG-Frame in Wirklichkeit ein "XING-Header"  
 d.h. anstelle von Audio-Daten kommen weitere Informationen über die Datei  
 wenn dieser fehlt, heisst das aber nicht, dass das File CBR ist. (Oder doch??)  
 die vbr-Detektion mache ich auf jeden Fall über diesen XING Header.}
  

 
//**************Einlesen des ersten MPEG-Frames / XING-Headers***************************  
            if mpeginfo.version=1 then  
                if mpeginfo.channelmode<>3 then xing_offset := 32+4  
                    else xing_offset := 17+4  
            else  
                if mpeginfo.channelmode<>3 then xing_offset := 17+4  
                    else xing_offset := 9+4;  
            if (buffer[position+xing_offset]=$58)        {'Xing'}  
               AND (buffer[position+xing_offset+1]=$69)  
               AND (buffer[position+xing_offset+2]=$6E)  
               AND (buffer[position+xing_offset+3]=$67)  
               then // Xing Tag vorhanden  
            begin  
                    // Die nächsten 4 Bytes sind die "Flags", wobei eigentlich nur das 4te interessant ist  
                    Xing_flags := buffer[position+xing_offset+7];  
                    if (Xing_flags AND 1)=1 then  
                    begin //Anzahl der frames in den nächsten 4 Bytes gespeichert  
                        mpeginfo.frames := 16777216 * buffer[position+xing_offset+8]  
                            + 65536 * buffer[position+xing_offset+9]  
                            + 256 * buffer[position+xing_offset+10]  
                            + buffer[position+xing_offset+11];  
                    end  
                    else mpeginfo.frames := 0;  
                    // Warnung: evtl. liegt hier eine Fehlerquelle:  
                    //      Wenn der Xing header so fehlerhaft ist,  
                    //      werden weitrere Berechnungen aufgrund des ersten vbr-Frames erstellt!  
                    xing_header_size := framelength;  
                    try  
                        mpeginfo.bitrate := trunc((mpeginfo.samplerate/1000*(fsize-id3_size-id3v1_size-xing_header_size))/(mpeginfo.frames*144));  
                    except  
                        valid := false;  
                    end;  
                    mpeginfo.vbr := true;  
            end  
            else  
            begin   // mpegframe ist echter mpegframe, nicht XING  
                try  
                    mpeginfo.frames := trunc((fsize-id3_size-id3v1_size-xing_header_size)/framelength);  
                except  
                    valid := false;  
                end;  
                mpeginfo.vbr := false; // wahrscheinlich (?)  
                xing_header_size := 0;  
            end;  
//**************Erster MPEG-Frame / XING-Header Ende***************************  

 
            { Es muss überprüft werden, ob die gesammelten Informationen sinnvoll sind.  
              Dazu teste ich, ob dem ersten Frame weitere Frames folgen.}
  
            if (position+framelength>length(buffer)-4AND (position+framelength+4<fsize) then  
            begin  
                Reset(F,1);  
                Seek(F,position+framelength);  
                blockread(f,mp3_header,4);  
                CloseFile(F);  
            end else  
            begin  
                try  
                    mp3_header[1] := buffer[position+framelength];  
                    mp3_header[2] := buffer[position+framelength+1];  
                    mp3_header[3] := buffer[position+framelength+2];  
                    mp3_header[4] := buffer[position+framelength+3];  
                except  
                    mp3_header[1] := 0// <> $FF  
                    mp3_header[2] := 0;  
                    mp3_header[3] := 0;  
                    mp3_header[4] := 0;  
                end;  
            end;  
            // Wenn dort kein weiterer Header ist, dann waren die borherigen Berechnungen für die Katz'  
            if (mp3_header[1]<>$FFor (mp3_header[2]<$E0then valid:=false;  

 
            { Überprüfe nun abermals, ob der neue Frame ein echter Frame ist - Aber nur die Kurzform,  
              d.h. Berechne die Länge und teste, ob danach ein weiterer Frame steht.  
              Das sollte dann als Sicherheit reichen.}
  
                
            //  neue Framelength bestimmen  
            neueframelength := GetFramelength(mp3_header);  
            if neueframelength<=0 then valid:=false  
            else  
            // evtl. mehr aus der Datei lesen  
                if (position+framelength+neueframelength>length(buffer)-2AND (position+framelength+neueframelength+4<fsize) then  
                begin  
                    Reset(F,1);  
                    Seek(F,position+framelength+neueframelength);  
                    blockread(f,mp3_header,4);  
                    CloseFile(F);  
                end else  
                begin  
                    try  
                        mp3_header[1] := buffer[position+framelength+neueframelength];  
                        mp3_header[2] := buffer[position+framelength+neueframelength+1];  
                    except  
                        mp3_header[1] := 0// <> $FF  
                        mp3_header[2] := 0;  
                    end;  
                end;  
            //ist dort wieder ein neuer Header?  
            if (mp3_header[1]<>$FFor (mp3_header[2]<$E0then valid:=false;  

 
            // Die Überprüfung ist durchgelaufen, also:  
            if valid then begin  
                try  
                    mpeginfo.dauer := ((fsize-id3_size-id3v1_size-xing_header_size)*8div ((mpeginfo.bitrate)*1000);  
                except  
                    valid := false;  
                end;  
                mpeginfo.position := position;  
            end;  
        end;  
    end//while  
end


end.


schreib mal ein kleines anwendungsbeispiel plz ;)
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 480

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Sa 06.08.05 18:26 
Absolutes Minimal-Beispiel: Form mit einer Memo, einem Edit, einem Button.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TForm1.Button1Click(Sender: TObject);
var mpegInfo:TMpegInfo;
    ID3v1Tag: TID3v1Tag;
    ID3v2Tag: TID3v2Tag;
begin
  mpegInfo := TmpegInfo.Create;
  ID3v1Tag := TID3v1Tag.Create;
  ID3v2Tag := TID3v2Tag.Create;
  mp3Info(Edit1.Text,mpeginfo,ID3v2Tag,id3v1tag);
  memo1.lines.add(ID3v1Tag.Artist + ' - ' + id3v1Tag.Titel);
  mpegInfo.free;
  ID3v1Tag.free;
  ID3v2Tag.Free;
end;
Besser wäre es z.B. wenn man eine weitere Klasse TMp3File oder so definiert, die dann aus den teilweise recht technischen Informationen die wichtigen herausfiltert. Um sie z.B. in einem Treeview oder so anzuzeigen. Z.B.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
if ID3v2Tag.exists then 
  myMp3File.Artist := ID3v2Tag.artist
else 
  if ID3v1Tag.exists then 
    myMp3File.Artist := ID3v1Tag.artist 
  else myMp3File.Artist := 'Unknown Artist'


Fehler exists ist jetzt auch behoben.

_________________
We are, we were and will not be.
F34r0fTh3D4rk
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 5284
Erhaltene Danke: 27

Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
BeitragVerfasst: So 07.08.05 12:34 
das beispiel reicht mir, danke :D

@exists: s.o. :wink:

bei mir geht's nur so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  Song: string;
  mpegInfo: TMpegInfo;
  ID3v1Tag: TID3v1Tag;
  ID3v2Tag: TID3v2Tag;
begin
  mp3Info(Filename, mpeginfo, ID3v2Tag, ID3v1Tag);
  Song := ID3v1Tag.Artist + ' - ' + ID3v1Tag.Titel;
end;


etwas langsamer als die andere prozedur, aber dafür ja auch besser ;)
adler
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 511

Windows XP
Delphi 05 Pers
BeitragVerfasst: Sa 29.04.06 15:25 
Hey!

Ma so ne Frage, wo kommt das mp3info her?
Da sagt er mir ist nicht deklariert, ja wo auch :D

_________________
Wo's nix zu gucken gibt, gibts vielleicht was zu hören.
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 480

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Sa 29.04.06 18:14 
Mp3Info ist sie eine der langen Prozeduren aus den anderen Postings. Es handelt sich dabei um eine Vorgängerversion von meinen MP3FileUtils. Ein Anwendungsbeispiel liegt dabei. Alternativ kann ich auch diesen FAQ-Beitrag empfehlen.

_________________
We are, we were and will not be.
adler
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 511

Windows XP
Delphi 05 Pers
BeitragVerfasst: So 30.04.06 13:08 
Ja ich sehs, mit der Demo, aber er gibt mir keine Werte.
Reicht es eigentlich nicht, wenn ich nur so eine Tid3v1 erstelle, dort loadfromfile und dann die tags auslese?
Weil dann sind die Strings immer leer

_________________
Wo's nix zu gucken gibt, gibts vielleicht was zu hören.
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 480

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: So 30.04.06 13:11 
Wenn die Strings leer sind, dann könnte das daran liegen, dass kein ID3v1-Tag vorhanden ist, sondern nur der v2-Tag. Diese sind vom Aufbau her vollkommen verschieden und zueinander inkompatibel.

Prinzipiell reicht es aber, nur den ID3v1-Teil aus der Unit zu nehmen. Nur hat man dann halt nur den v1-tag, nicht den in der Version 2.

Was sagen denn andere Programme (z.B. Winamp) zu den Tags?

_________________
We are, we were and will not be.
adler
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 511

Windows XP
Delphi 05 Pers
BeitragVerfasst: So 30.04.06 13:27 
Hehe, danke, ich hab irgendwie immer ein Lied erwischt, wo er auch bei TagExists False zurück gibt.
Bei manchen gehts ;)
Lese jetzt beide aus, kann es sein das es MP3s gibts, die beides nicht haben?

_________________
Wo's nix zu gucken gibt, gibts vielleicht was zu hören.
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8554
Erhaltene Danke: 480

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: So 30.04.06 13:44 
Natürlich. Der ID3v1-Tag ist ein 128Byte großer optionaler Bereich am Ende der Datei, der einige Textinformationen speichern kann.
Der ID3v2-Tag ist ein optionaler Bereich am Anfang (meistens) der Datei, der afaik bis zu 256MB groß sein kann und beliebige Informationen speichern kann: Texte, Bilder, Videos, sogar andere mp3-Dateien (die wiederrum einen ID3v2-Tag haben können - rekursiv aufgebaute mp3s sind somit möglich :lol:)

Weder das eine noch das andere muss vorhanden sein.

_________________
We are, we were and will not be.
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: So 30.04.06 19:09 
user profile iconadler hat folgendes geschrieben:
kann es sein das es MP3s gibts, die beides nicht haben?


Zitat:
The audio format MPEG layer I, layer II and layer III (MP3) has no native way of saving information about the contents, except for some simple yes/no parameters like "private", "copyrighted" and "original home" (meaning this is the original file and not a copy). A solution to this problem was introduced with the program "Studio3" by Eric Kemp alias NamkraD in 1996. By adding a small chunk of extra data in the end of the file one could get the MP3 file to carry information about the audio and not just the audio itself.

Ich glaube das beantwortet alles ;). (Quelle: www.id3.org)