Seite 1 von 2
Informationen aus MP3-Dateien auslesen
Verfasst: Fr, 03. Feb 2006 13:43
von peternmb
Hallo, vielleicht hat jemand einen Tipp für mich.
Ich möchte Informationen aus MP3-Dateien auslesen. Ich benötige die Spieldauer und die Sampling-Rate. Evtl. noch die Möglichkeit ID-Tags zu lesen oder zu ändern. Das MP3-Beispiel auf der Alaska-Website bringe ich leider nicht zum Laufen weil der angegebene Link für die mp3.dll nicht funktioniert. Auch mit anderen mp3.dll`s klappt nicht.
Verfasst: Fr, 03. Feb 2006 15:22
von Tom
Hallo, Peter.
Diese MP3.DLL gibt's anscheinend nicht mehr als Freeware; ich bin eine Weile durch die spanischsprachigen Sites gestolpert und immer wieder auf einer PayPal-Site gelandet.
Aber MPEG/MP3 sind vergleichsweise simpel. MP3 mit ID-Tags basiert auf MP2. Beide Formatbeschreibungen findest Du u.a. hier:
http://audio.layer2.de/das_mp2format.php
http://audio.layer2.de/das_mp3format.php
Einfach byteweise auslesen.
klappt nicht so richtig
Verfasst: Fr, 03. Feb 2006 16:22
von peternmb
ja, klingt theoretisch ganz einfach - ich bekomms aber nicht gebacken.
Habe mir eine kleine Function geschrieben, um die Infos byteweise auszulesen und anzeigen zu lassen. Ich bekomme aber nichts angezeigt. Vielleicht findet einer den Fehler
Code: Alles auswählen
#include "fileio.ch"
proc say_header(cDatei)
LOCAL nHandle := 0, inhalt:=space(32)
//
nHandle := FOPEN(cDatei, FO_READ + FO_SHARED)
//
IF nHandle > 0
FREAD(nHandle, inhalt,32) //32 byte auslesen
ENDIF
//
FCLOSE(nHandle)
//
msgbox(inhalt)
//
return
Bin für jeden Hiwneis dankbar. Die Links waren super
[/code]
Verfasst: Fr, 03. Feb 2006 16:33
von Tom
Verfasst: Sa, 04. Feb 2006 10:55
von peternmb
jetzt bekomme ich zumindest etwas angezeigt, aber nicht was ich erwartet habe. Es wird nur ID3 und ein seltsames Zeichen angezeigt. Leider bin ich mit Dateiheadern o.ä. nicht sehr vertraut. Vielleicht kannst du mir nochmal weiterhelfen
Verfasst: Di, 07. Feb 2006 15:03
von Siggi
Hallo Leute,
ich habe mal für einen Freund ein Tool geschrieben, daß den Titel, Interpret aus dem Dateiname in den ID3Tag schreibt.
Hier ohne Korrektur die Schreibroutine.
Code: Alles auswählen
********************
FUNCTION ID3PUT(cFZiel, Titel, Interpret, Album )
********************
LOCAL nWrite:=1, cT:="ID3"+chr(03)+chr(0)+chr(0)+chr(0)+chr(0)+chr(15)+"v", nNr, nH
LOCAL V1:="TAG", bV1:=.f., bV2:=.f., cBuffer:=space(127)
if nWrite >0
* ID3V2 wird an den Dateianfang gestellt
cT+="TIT2"+chr(0)+chr(0)+chr(0)+chr(len(Titel)+1)+chr(0)+chr(0)+chr(0)+Titel
cT+="TPE1"+chr(0)+chr(0)+chr(0)+chr(len(Interpret)+1)+chr(0)+chr(0)+chr(0)+Interpret
cT+="TALB"+chr(0)+chr(0)+chr(0)+chr(len(Album)+1)+chr(0)+chr(0)+chr(0)+Album
cT+= Replicate(chr(0), 100)
* ID3V1 kommt ans Dateiende
V1+=padr(Titel , 30, chr(0))
V1+=padr(Interpret, 30, chr(0))
V1+=padr(Album , 30, chr(0))
V1+=repl(chr(0), 34)+chr(255)
if ( nH := FOpen(cFZiel, FO_READWRITE+FO_SHARED)) # 0 .and. FSEEK (nH, 0, FS_SET) # -1
* ID3V2 wird geschrieben
if (nNr:=FWrite(nH ,cT )) # 0 ; bV2:=.t.; endif
* ID3V1 erst prüfen ob schon vorhanden
if (FSEEK (nH, -1, FS_END) # -1)
FRead(nH, @cBuffer, 1)
if substr(cBuffer,1,1) = chr(255) // FF wenn vorhanden
FSEEK (nH, -128, FS_END)
endif
if (nNr:=FWrite(nH ,V1 )) # 0 ; bV1:=.t.; endif
endif
Fclose(nH)
if ! bV2 ; StopBox("ID3V2 konnte nicht geschrieben werden !", cFZiel); endif
if ! bV1 ; StopBox("ID3V1 konnte nicht geschrieben werden !", cFZiel); endif
else; StopBOX("Die Datei ["+cFZiel+"] wurde nicht aktualisiert !;;Win-Fehlernummer: "+TTOC(FError() ),"ID3PUT()"); nWrite:=0
endif
endif
return (nWrite)
Byte & Bit
Verfasst: Di, 07. Feb 2006 18:35
von peternmb
irgendwie hängst es an der Umwandlung von Bytes in Bits.
Ich kann mir zwar die Bytes anzeigen lassen - ich bräuchte aber die Bits.
Das mit den ID-Tags ist zwar hilfreich, trifft aber glaube ich nicht ganz den Kern der Sache.
Ich bräuchte einfach die ersten 32 Bit des Headers der Datei. Was ich bisher gefunden habe geht das nur mit C oder VB
Re: Byte & Bit
Verfasst: Di, 07. Feb 2006 18:44
von Martin Altmann
Hallo Peter,
peternmb hat geschrieben:Ich bräuchte einfach die ersten 32 Bit des Headers der Datei. Was ich bisher gefunden habe geht das nur mit C oder VB
soweit ich weiß, kann man mit den Array-Operatoren bitweise auslesen. Es sollte also so gehen:
Code: Alles auswählen
inhalt[1] // entspricht dem 1. Bit
inhalt[2] // entspricht dem 2. Bit
...
inhalt[32] // entspricht dem 32. Bit
Viele Grüße,
Martin
Verfasst: Mi, 08. Feb 2006 11:26
von peternmb
nein, funktioniert leider nicht.
Es werden da auch nur die Bytes angezeigt.
Hat denn noch Keiner eine Function geschrieben, um den Datei-Header bitweise auszulesen - oder ist das einfach in xbase nicht möglich
Verfasst: Mi, 08. Feb 2006 11:31
von Martin Altmann
Ach Mensch - Du wolltest ja 32 Byte bitweise ausgelesen haben, sorry!! Ich war davon ausgegangen, dass in der Vairablen inhalt genau ein Byte steht!
Dann probiere es mal mit dem Konstrukt:
Code: Alles auswählen
inhalt[1][1] // entspricht dem 1. Bit des 1. Bytes
inhalt[1][2] // entspricht dem 2. Bit des 1. Bytes
...
inhalt[32][8] // entspricht dem 8. Bit des 32. Bytes
Klappt das denn dann wenigstens??
Viele Grüße,
Martin
Verfasst: Mi, 08. Feb 2006 13:23
von peternmb
Nein, haut leider auch nicht hin - ich erhalte eine Fehlermeldung. So wie es aussieht ist die Variable inhalt nur ein 1-dimensionales Array
Verfasst: Mi, 08. Feb 2006 13:31
von Martin Altmann
Hallo Peter,
peternmb hat geschrieben:So wie es aussieht ist die Variable inhalt nur ein 1-dimensionales Array
ja - das ist schon klar. Sonst wäre der Index ja auch [ 1, 2 ] gewesen.
Komisch - in der Alaska-Newsgroups gibt es mehrere Postings zu dem Thema. Und da wurde gesagt, dass ab Xbase++ Version 1.7 das bitweise aulsesen einer Variablen über die Array-Operanden funktioneren soll.
Ich selber habe das noch nie gemacht - darum hatte ich auch geschrieben:
Martin Altmann hat geschrieben:soweit ich weiß, kann man mit den Array-Operatoren bitweise auslesen. Es sollte also so gehen:
Tut mir wirklich leid, dass ich Dir scheinbar nicht helfen kann!
Du könntest höchstens noch mal den umständlichen Weg probieren - nur um sicher zu gehen:
Code: Alles auswählen
FOR i := 1 to 32
byte := inhalt[ i ]
FOR j := 1 to 8
bit := byte[ j ]
...
NEXT
NEXT
Viele Grüße,
Martin
Verfasst: Mi, 08. Feb 2006 14:04
von peternmb
ich habe es fast auf die gleiche Art probiert - Fehlermeldung.
Muss ich mich halt dochmal in der englischsprachigen Newsgroup quälen. Gesucht habe ich dort schonmal aber nichts gefunden.
Verstehe ich nicht, dass ich der Erste bin der sowas machen will
Erstmal vielen Dank.
Verfasst: Mi, 08. Feb 2006 17:23
von thomas
Hallo Peter,
habe mal vor langer Zeit mit Bits setzen und abfragen eine Programm geschrieben.
Schau die doch mal diese Programmzeilen genauer an, ich denke es könnte helfen.
Gruß
Thomas
Code: Alles auswählen
#include "fileio.ch"
proc say_header(cDatei)
LOCAL nHandle := 0, inhalt:=space(4)
nHandle := FOPEN(cDatei, FO_READ + FO_SHARED)
IF nHandle > 0
FREAD(nHandle, @inhalt,4) // 4 byte a 8Bit = 32 Bit auslesen
ENDIF
//
FCLOSE(nHandle)
// Test die ersten 8 Bits
cByte := SUBSTR(inhalt,1,1)
IF 2 $ VAL(cByte)
// Bit 1 ist gesetzt
ELSE
// Bit ist nicht gesetzt
ENDIF
IF 4 $ VAL(cByte)
// Bit 2 ist gesetzt
ENDIF
IF 2+8+32 $ VAL(cByte)
// Bit 1 und 3 und 5 sind gesetzt
ENDIF
cBitString := s_bit(VAL(cByte))
MsgBox(cBitString)
return
FUNCTION s_bit( nZahl )
LOCAL cStrg :=""
LOCAL nIx
FOR nIx = 7 TO 0 STEP -1
IF nZahl >= 2 ** nIx
nZahl := nZahl - 2 ** nIx
cStrg += "1"
ELSE
cStrg += "0"
ENDIF
NEXT nIx
RETURN( cStrg )
Verfasst: Mi, 08. Feb 2006 18:36
von Wolfgang Ciriack
Ansonsten könnte man noch aus den XbToolsIII die Funktion IsBit() nutzen (wenn man diese ebenfalls besitzt).
Verfasst: Mi, 08. Feb 2006 19:29
von Martin Altmann
Hallo Peter,
ich kann es nicht lassen
Ich habe mal gerade in der Doku von Xbase++ gestöbert. Der folgende Abschnitt ist interessant - die fett-Formatierung dient dem Hervorheben der wichtigen Dinge:
[] Operator hat geschrieben:Der Array-Operator ist nicht auf Werte vom Datentyp "Array" beschränkt, sondern kann auch auf Zeichenketten und numerische Werte angewandt werden. Hinweis: In der aktuellen Xbase++ Version ist der Operator bei Zuweisungen nur bei LOCAL oder STATIC Variablen, denen die Zeichenkette bzw. der numerischen Wert zugewiesen ist, anwendbar.
Zeichen Bei Zeichenketten gibt der Operator das Zeichen an der Position <nElement> aus der Zeichenkette zurück (siehe Beispiel unten). Das Ermitteln einzelner Zeichen aus einer Zeichenkette läuft mit dem Array-Operator sehr viel schneller ab als mit der Funktion SubStr().
Numerisch Wenn der Operand einen numerischen Wert hat, prüft der Operator, ob das Bit an der Position <nElement> gesetzt ist. Das Ergebnis der Operation ist .T. (wahr), falls das Bit gesetzt ist, andernfalls ist es .F. (falsch). Der Wert des Operanden kann auf Bit-Ebene verändert werden, indem der Wert .T. oder .F. für das Bit an der Position <nElement> zugewiesen wird. Der Wertebereich für <nElement> erstreckt sich bei numerischen Operanden von 1 bis 32.
Und hier mal das entsprechende Beispiel aus der Doku:
Code: Alles auswählen
// Array-Operator und numerische Werte
// Das Beispiel zeigt wie der Array-Operator mit
// Werten vom Datentyp Numerisch verwendet werden kann.
PROCEDURE Main
LOCAL n
? n := 6 // Ergebnis: 6
? n[1] // Ergebnis: .F. (Bit für 2^0 ist nicht gesetzt)
? n[2] // Ergebnis: .T. (Bit für 2^1 ist gesetzt)
? n[3] // Ergebnis: .T. (Bit für 2^2 ist gesetzt)
n[1] := .T. // Bit für 2^0 setzen
? n // Ergebnis: 7
RETURN
Viele Grüße,
Martin
Verfasst: Mi, 08. Feb 2006 20:55
von peternmb
muss ich mir nochmal in Ruhe ansehen, ich verstehe das bisher noch nicht so richtig
Ich bräuchte einfach die ersten 32 Bit des Dateiheaders als Zeichenkette.
Die 32 Zeichen würden dann, wenn ich alles halbwegs verstanden habe nur aus 32x 0 oder 1 bestehen.
Aber ich glaube, mit den bisherigen Infos ist es zu schaffen
Verfasst: Do, 09. Feb 2006 16:36
von andreas
Hallo Martin,
ich habe schon lange die Abfrage bzw. Setzen einzelner Bits gesucht.
Jetzt habe ich das aus deinem letzten Posting ausprobiert und in eine vorhandene Anwendung eingebaut.
Komischerweise kann ich die Bits abfragen aber nicht setzen.
Wenn ich aber den Code aus der Hilfe (auch in deimen Posting) als eigene Anwendung kompiliere, funktioniert es.
Was kann das sein?
Beispielcode von Thomas
Verfasst: Do, 09. Feb 2006 17:09
von peternmb
Hallo, ich habe den Beispielcode von Thomas mal so erweitert, dass die ersten 4 Byte à 8 Bit angezeigt werden, aber das Ergebniss verblüfft mich etwas. Es werden beim Einlesen von einer MP3-Datei nur die Bits 23 und 24 als gesetzt gezeigt. Der Rest sind alles Nullen. Das kann lt. der Header-Beschreibung aber nicht hinkommen. Ich glaube ich geb`s auf - ich kann mittlerweile keine Bits und Bytes mehr sehen
Verfasst: Do, 09. Feb 2006 17:43
von Martin Altmann
Hallo Andreas,
andreas hat geschrieben:Wenn ich aber den Code aus der Hilfe (auch in deimen Posting) als eigene Anwendung kompiliere, funktioniert es.
Was kann das sein?
keine Ahnung - außer dass Deine Variable vielleicht weder vom Typ local noch vom Typ static ist?
Viele Grüße,
Martin
Verfasst: Fr, 10. Feb 2006 7:45
von andreas
Hallo Martin,
du hast recht. Die Variable muss deklariert sein, sonst funktioniert nur die Abfrage!
re: MP3 TAG
Verfasst: Do, 16. Mär 2006 8:13
von AUGE_OHR
moin,
da ich mich auch gerade mit den MP3 ID TAG beschäftige habe,
ein paar Anmerkungen :
a.) es gibt MP3 ID TAG v1.0 & v1.1 welche beide am ENDE der
Datei die letzten 128 Byte füllen.
{ID3-TAGv1 sind die letzten 128 Bytes
der Datei und sind so aufgeteilt:
Byte 1-3 = Kennung 'TAG' 3
Byte 4-33 = Titel 30
Byte 34-63 = Artist 30
Byte 64-93 = Album 30
Byte 94-97 = Jahr 4
Byte 98-127 = Kommentar 30 (28 Kommentar + 2 TrackNo.)
Byte 128 = Genre} 1 (0-79)
b.) die v2.0x MP3 ID TAG Version von der ihr hier sprecht ist "ein wenig"
komplizierter aufgebaut. siehe link
http://www.id3.org/develop.html
beim letzten war ich dran, bis ich feststellte das ich anders rankomme
da ich Xbase++ RC2 mit WMP.OCX benutze. Also kann ich aus der
WMP Playliste mit Hilfe des WMP Event "CurrentItemChange" von
jedem Item die "Property" auslesen und hab dann alle Informationen
die ich benötige.
gruss by OHR
Jimmy
Verfasst: Do, 16. Mär 2006 10:37
von brandelh
@petermb
> Ich bräuchte einfach die ersten 32 Bit des Dateiheaders
> als Zeichenkette.
> Die 32 Zeichen würden dann, wenn ich alles halbwegs verstanden
> habe nur aus 32x 0 oder 1 bestehen.
NEIN, so funktioniert das nicht.
'0101' sind keine 4 Bit, sondern 4 Byte und der WERT der Variable intern ist nicht 0 oder 1 sondern '0' = ASC('0') = 48 und '1' = ASC('1') = 49
Wenn du eine String-Variable mit einem Byte hast, dann kann man leicht die Bits zuordnen:
00000000 -> chr(0)
00000001 -> chr(1) oder neuerdings bei numerisch nVal[1] := 1
00000010 -> chr(2) oder neuerdings bei numerisch nVal[2] := 1
00000011 -> chr(3) oder neuerdings bei numerisch nVal[2] := 1 und nVal[1] := 1
bei den neuen nVal[] Funktionen bin ich mir jetzt nicht sicher, da ich diese so nie brauche.
Ein 32 Bit Wert mit 1 sieht also intern so aus:
00000000 00000000 00000000 00000001 aber die Reihenfolge der Speicherung muß nicht unserer Lesart entsprechen.
In deinem Beispiel ganz am Anfang ließt du 32 BYTE ein, denn jeder Befehl in Xbase++, welcher auf Festplatten, Strings etc. zugreift arbeitet immer BYTE-weise !
Um 32 Bit zu bekommen mußt du 4 Byte einlesen (da 4 Byte * 8 Bit = 32 Bit). Wenn du dies aber in eine Stringvariable einliest, kannst du mit dem Arrayoperator nur auf einzelne BYTE (Zeichen, String = array of Byte in C ) zugreifen. Um mit dem Arrayoperator auf BIT Zugreifen zu können müßtest du die 4 Byte in eine Zahl verwandeln (VAL hilft hier nicht !), welche genaue Reihenfolge weiß ich im Moment auch nicht, aber im Prinzip so
asc(cString[1])+ asc(cString[2])*256 + asc(cString[3])*256*256 + asc(cString[4])*256*256*256
Das Problem ist, zu erkennen, welche der einzelnen Zeichen jetzt welchen Multiplikator braucht. Vor Jahren habe ich mit PowerBasic den Dateiheader von DBF Dateien zerlegt, da war zuerst das niederwertige Byte gespeichert dann das höherwertige - bei 16 Bit Werten.
Dummerweise gibt es in Xbase++ keine 32 Bit - INTEGER / DWORD Variablen. Hast du die Tools ? Dort gibt es Funktionen zu BIT Operationen, wie gut die sind weiß ich aber nicht. Mit den nVal[] Operatoren sollte es aber auch gehen.
Wichtig, man kann zwar aus einer 4 Byte Variablen (Stringlänge 4 oder Integer) einen 32 Byte langen String mit 0 und 1 machen, dieser hat aber keine 32 Bit sondern 32 BYTE, auch wenn er eventuell für dich nützlich wäre.
Solltest du dies bereits gewußt haben, sorry, aber ich habe den Eindruck, dass du bei Bit und Byte etwas durcheinander gebracht hattest.
Forsche nun nochmals die Beispiele weiter oben durch, eventuell paßt es dann ja.
Verfasst: Do, 16. Mär 2006 11:19
von brandelh
Hi,
ich habe hier mal ein kleines Programm erstellt, welches die Bit und Byte einer 32 Bitzahl ausleuchtet:
Code: Alles auswählen
procedure main
local cTxt, nWert, nSollWert
set alternate to test.txt
set alternate on
cls
? "Start"
for x := 1 to 32
cTxt := replicate("0",32) // immer löschen
cTxt[33-x] := "1" // wir lesen 1. Bit von rechts
nWert := 0 // immer löschen
nWert[x] := .t. // .t. = 1
nSollWert := int(2^(x-1))
? x,cTxt,nWert,nSollWert
next
? "Ende"
set alternate to
return
und nun noch das Ergebnis:
Code: Alles auswählen
Start
1 00000000000000000000000000000001 1 1
2 00000000000000000000000000000010 2 2
3 00000000000000000000000000000100 4 4
4 00000000000000000000000000001000 8 8
5 00000000000000000000000000010000 16 16
6 00000000000000000000000000100000 32 32
7 00000000000000000000000001000000 64 64
8 00000000000000000000000010000000 128 128
9 00000000000000000000000100000000 256 256
10 00000000000000000000001000000000 512 512
11 00000000000000000000010000000000 1024 1024
12 00000000000000000000100000000000 2048 2048
13 00000000000000000001000000000000 4096 4096
14 00000000000000000010000000000000 8192 8192
15 00000000000000000100000000000000 16384 16384
16 00000000000000001000000000000000 32768 32768
17 00000000000000010000000000000000 65536 65536
18 00000000000000100000000000000000 131072 131072
19 00000000000001000000000000000000 262144 262144
20 00000000000010000000000000000000 524288 524288
21 00000000000100000000000000000000 1048576 1048576
22 00000000001000000000000000000000 2097152 2097152
23 00000000010000000000000000000000 4194304 4194304
24 00000000100000000000000000000000 8388608 8388608
25 00000001000000000000000000000000 16777216 16777216
26 00000010000000000000000000000000 33554432 33554432
27 00000100000000000000000000000000 67108864 67108864
28 00001000000000000000000000000000 134217728 134217728
29 00010000000000000000000000000000 268435456 268435456
30 00100000000000000000000000000000 536870912 536870912
31 01000000000000000000000000000000 1073741824 1073741824
32 10000000000000000000000000000000 2147483648 2147483648
Ende
Im Speicher ist es also einfacher als ich dachte und laut Beschreibung des ID-Tag sollte es auch dort einfach abgelegt sein. Meine Erinnerung bezog sich auf gepackte Datumsfelder.
Verfasst: Do, 16. Mär 2006 11:43
von peternmb
Na, da werde ich mich doch nochmal mit den Bits & Byten befassen
Ich glaube das Posting von Hubert bringt mich da jetzt eine Schritt weiter. Vielen Dank.
Das mit den ID3-Tags (wie weiter oben angesprochen) ist zwar auch interessant, liefert mir aber nicht die Informationen die ich eigentlich haben möchte (z.B. Samplingrate).