Seite 1 von 1
mci zum auslesen von audiocd -> freedb abfragen
Verfasst: Dienstag 9. November 2004, 09:03
von genrich
Hab das Projekt gefunden:
http://sourceforge.net/projects/cddb-py/
(Wobei es mittlerweile mit freeDB funktioniert)
Bei diesem Projekt wird eine Audio CD mit mci.dll (eine in C geschriebenes Modul) ausgelesen, um die DiscID zu erhalten...
Leider ist diese Modul für Python 2.0 kompiliert... Nun hab ich bei
http://www.pkshiu.com/tech/cddb.html folgendes gefunden:
The currently CDDB project works well under *NIX and Python 2.0. However, the pre-compiled mci.dll for windows is built for Python 2.0. To make it work with 2.1, edit it with an editor (emacs works nicely) and search for reference to the python 2.0 dll. Change 2.0 to 2.1. ALso the python source files have some references to UNIX only API calls that is not in the Windows platform (geteuid). Comment out that line will make the software work.
Leider kann ich das Beschrieben aber nicht umsetzen. Hab keinen blassen schimmer, wie man mit C kompiliert...
Generell wäre es auch schön, wenn man system unabhängig an's CD-ROM dran kommt, um die DicsID zu bestimmen... Gibt es da was???
Verfasst: Dienstag 9. November 2004, 11:58
von genrich
Hab was gefunden!
http://www.pygame.org
Ist zwar eigentlich für Spiele...
Hat aber ein CDROM Modul:
http://www.pygame.org/docs/ref/pygame_cdrom.html
Hier ein Tutorial:
http://www.pygame.org/pcr/cd_tutorial/index.php
Das ganze gibt es Windows, verschiedene Unix Distributions, MacOS und BeOS.
Jetzt muß ich mir nur was basten, das ich aus den Tack-Information die freeDB DiscID berechnen kann...
Verfasst: Dienstag 9. November 2004, 12:55
von genrich
Habe mir jetzt aus verschiedenen Quellen ein skript zusammengebastelt, welches eigentlich eine DiscID einer Audio CD generieren soll... Es klappt aber nicht wie erwartet, die DiscID ist einfach falsch

Das liegt wohl daran, das ich die gefundenen Codestücke nicht richtig verstanden habe...
[alter CODE gelöscht, s. aktueller Version unten]
ich bekomme auch einen Fehler, mit dem ich nicht's anfangen kann:
FutureWarning: x<<y losing bits or changing sign will return a long in Python 2.4 and up
Verfasst: Dienstag 9. November 2004, 15:49
von genrich
Hier eine aktualisierte Version... Doch leider stimmt die DiscID immer noch nicht
[alter CODE gelöscht, s. aktueller Version unten]
Verfasst: Dienstag 9. November 2004, 17:43
von Dookie
Hi,
in der Pythonmailingliste die Nummer stimmt aber.
zum Umwandeln in Hex kannst Du auch die eingebaute Funktion hex() nutzen:
das 0x bekommst du einfach so weg:
Gruß
Dookie
Verfasst: Dienstag 9. November 2004, 18:04
von genrich
ist doch das selbe wie
Zur eigentlichen Datenbankabfrage nutze ich ja
http://sourceforge.net/projects/cddb-py/
Ich hab per Hand nach der CD gesucht... Es kommen nur folgende IDs in Frage:
d40e9010
db0e9210
d60e5910
Also es liegt wohl noch ein Grundlegendes Problem vor

Ich habe mit noch mal genau die Spezifikationen angeschaut:
http://www.freedb.org/modules.php?name= ... le&artid=6
Ganz merkwürdig finde ich die berechnung von t:
t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) -
((cdtoc[0].min * 60) + cdtoc[0].sec);
Und was mich auch mulmig stimmt sind die:
Important note for clients using the MS-Windows MCI interface
Ich weiß leider nicht ob pygames eigentlich nur auf das MCI zurückgreift...
Naja die Trackzeiten mit Sek und Frames stimmen ja zumindest mit den angaben von EAC überein...
Gibt es vielleicht doch nicht etwas fertiges in der Richtung??? Ich schein das ja nicht auf die Kette zu bekommen

Verfasst: Dienstag 9. November 2004, 18:05
von genrich
Ach so, hier noch mal eine aktualisierte Version:
Code: Alles auswählen
from pygame import cdrom
cdrom.init()
print "CDROM initialized: ", cdrom.get_init()
print "Number of CDROMs: ", cdrom.get_count()
def GetHMS(s):
h,s = divmod(s,3600)
m,s=divmod(s,60)
return "%02.f:%02.f:%02.f" % (h, m, s)
def Sek_Frames(s):
nurSek = int( s )
Frames = int( round( (s - nurSek) * 75 ) )
return nurSek, Frames
def cddb_sum(n):
# cddb_sum(123456789) => 1+2+3+4+5+6+7+8+9 = 45
ret = 0
while n > 0:
ret += (n % 10)
n = n / 10
return ret
def DiscID(cd_object):
print "\n Nr. Länge"
print "--------------------------"
checksum=0
TotalFrames=0
TotalSek=0
for i in range(TrackAnzahl):
track_length = cd_object.get_track_length(i)
nurSek, Frames = Sek_Frames( track_length )
checksum += cddb_sum(nurSek)
print " %2d %s.%2d Frames" % (i, GetHMS(nurSek), Frames)
TotalSek += nurSek
nurSek, Frames = Sek_Frames( TotalSek )
print "\nTotalZeit:",nurSek,"sek"
print "TotalZeit: %s.%d" % (GetHMS(nurSek),Frames)
sek_LetzterTrack = Sek_Frames(cd_object.get_track_length(TrackAnzahl-1))[0]
sek_ErsterTrack = Sek_Frames(cd_object.get_track_length(0))[0]
t=sek_LetzterTrack - sek_ErsterTrack
RAWdiscID=( checksum % 0xff << 24 | t << 8 | TrackAnzahl )
for i in range(cdrom.get_count()):
cd_object = cdrom.CD(i)
print "\nNr.:",i," Laufwerk:",cd_object.get_name()
print "Initialisierte..."
cd_object.init()
TrackAnzahl = cd_object.get_numtracks()
if TrackAnzahl!=0:
RAWdiscID = DiscID(cd_object)
print "\nRAW discID..:", RAWdiscID
print "freedb ID...: %08lx" % RAWdiscID
verbesserte Version
Verfasst: Dienstag 9. November 2004, 18:13
von Dookie
ich denke mal hier liegt der Fehler:
Code: Alles auswählen
sek_LetzterTrack = Sek_Frames(cd_object.get_track_length(i))[0]
i zeigt da nicht auf den letzten Track sondern hat den wert TrackAnzahl
nimm besser
Code: Alles auswählen
sek_LetzterTrack = Sek_Frames(cd_object.get_track_length(TrackAnzahl-1))[0]
Gruß
Dookie
Verfasst: Dienstag 9. November 2004, 18:27
von genrich
Hast recht, danke...
Hab ich verbessert... Jetzt bekomme ich als ID "be00b210"... Stimmt leider auch nicht

Verfasst: Dienstag 9. November 2004, 19:28
von Dookie
Hi genrich,
firsttrack muss auch nicht unbeding track0 sein.
noch ein Tipp, lad dir das CDDB-1.3.tar.gz runter und entpacke es, dann kannst Du in das DiskID.py reinschauen, da ist auch eine Funktion disc_id drinnen mit der Berechnung.
Gruß
Dookie
Verfasst: Dienstag 9. November 2004, 19:41
von Dookie
Du brauchsts nichtmal runterladen, einfach hier gucken:
http://csl.cse.ucsc.edu/~ben/python/CDDB/DiscID.py
Gruß
Dookie
Verfasst: Dienstag 9. November 2004, 19:44
von Dookie
nochwas,
in dem source findest Du öfter solche Zeilen:
mach da am besten die Klammern vorne weg:
Dann klappts auch mit Python2.3
Gruß
Dookie
Verfasst: Dienstag 9. November 2004, 20:14
von genrich
Darauf basiert mein Code... In diesem Projekt ist eigentlich alles enthalten, nur es benutze eine eigene C-Lib um an die TOC Informationen der Audio CD zu gelangen... Es ist auch eigentlich ein fertig kompilierte DLL dabei, die läuft allerdings nur mit Python v2.0
Auf der Mailingliste von pygames habe ich ein anderes Problem erfahren:
Und zwar braucht man eigentlich die "lead in" und "lead out" Daten... Die sind glaube ich nicht immer die selben, wie die normalen Start und Längen angaben... Kenne mich da aber auch nicht so dolle aus...
Ich habe auch ein Source mitgeschickt bekommen... Ich habs mal ein wenig angepasst, sodas es direkt läuft, wenn man pygame installiert hat:
Altes Listing entfernt... s.unten
Bei meiner "Depeche Mode" - "Speak & Spell" CD bekomme ich damit folgende Werte:
Verfasst: Dienstag 9. November 2004, 22:53
von genrich
Mit dem Kommandozeilen Programm
http://www.freedb.org/software/cddbidgen.zip kann ich nun entlich sichergehen, welchen DiscID der richtige ist:
dd0e8e10
spuckt es aus und das passt:
http://www.freedb.org/freedb/data/dd0e8e10
Für mein Skript bringt mich das aber nicht weiter

Verfasst: Dienstag 9. November 2004, 22:59
von Dookie
warum, du kannst das Tool ja mit system oder os.popen oder sonstwas aufrufen und das Ergebnis so erhalten.
Gruß
Dookie
Verfasst: Mittwoch 10. November 2004, 06:35
von genrich
So jetzt habe ich es... Das Listing aus der Mailingliste war nicht ganz vollständig...
Code: Alles auswählen
from pygame import cdrom
def getDiskId( tracksInfo ):
"""
Returns disc id based on tracksInfo which is tuple of ( track_start, track_length )
"""
# check sum
def sumDigits( val ):
return int( reduce( lambda x, y: int( y )+ int( x ), str( val ) ) )
# check sum
checkSum= reduce( lambda x, y: x+ y, map( lambda x: sumDigits( int( x[ 0 ]/75 )), tracksInfo ) )
# total length
totalTime= reduce( lambda x, y: x+ y, map( lambda x: x[ 1 ], tracksInfo ) )
return ( ( long(checkSum % 0xff) << 24 ) + ( long( totalTime/75 ) << 8 ) + len( tracksInfo ) )
cdrom.init()
cd=cdrom.CD(0)
print "Initialisierte..."
cd.init()
RAW_ID = getDiskId( [ ( cd.get_track_start( x )*75,cd.get_track_length( x )*75 ) for x in xrange( cd.get_numtracks() ) ] )
print "RAW_ID......:",RAW_ID
print "freedb ID...: %08lx" % RAW_ID
Nun ist das Ergebniss korrekt:
Obwohl das Listing schön kompakt ist, will ich es etwas Normalisieren... Die ganzen lambda Dinger verstehe ich nämlich nicht wirklich...
Verfasst: Mittwoch 10. November 2004, 07:45
von genrich
So... Ich hab meine Variante mit der kompakten Varianten von oben gekreutzt...
Nun hab ich ein Listing, welches ich auch verstehe
Und es funktioniert!!!
Code: Alles auswählen
from pygame import cdrom
def DiscID(cd_object):
def getHMS_f(Frames):
# Umwandung von Frames in h:m:s.frames
s,Frames = divmod(Frames,75)
h,s = divmod(s,3600)
m,s=divmod(s,60)
return "%02.f:%02.f:%02.f.%02.f" % (h, m, s, Frames)
def sumDigits( n ):
# Berechnet die Quersumme
# n=123456789 => 1+2+3+4+5+6+7+8+9 = 45
ret = 0
for i in str(n):
ret += int(i)
return ret
checksum=0
total_length = 0
for i in range(TrackAnzahl):
track_length = cd_object.get_track_length(i)*75
print "%2d - %s" % (i, getHMS_f(track_length))
total_length += track_length
checksum += sumDigits( int(cd_object.get_track_start( i )) )
print "\nTotalZeit: %s -> %dsek." % (getHMS_f(total_length), round(total_length/75))
return (long(checksum % 0xff) << 24) + (long(total_length/75) << 8) + TrackAnzahl
cdrom.init()
cd_object=cdrom.CD(0)
print "Initialisierte..."
cd_object.init()
RAW_ID = getDiskId( cd_object )
print "RAW_ID......:",RAW_ID
print "freedb ID...: %08lx" % RAW_ID
Wobei... Ist es eigentlich ok, wenn man eine Funktion in einer Funktion packt??? Ich meine, eigentlich ist es praktisch, wenn man diese Funktion nur "lokal" braucht...
Um das selbe zu erreichen hätte ich normalerweise jetzt wieder eine Klasse draus gemacht...
Jetzt muß ich "nur" noch die eigentliche freedb Abfrage hinbekommen... Leider klappt das mit diesem alten CDDB.py spontan nicht... Ich erhalte immer
500 None zurück

Verfasst: Mittwoch 10. November 2004, 10:35
von genrich
So... Hab nun eine Abfrage fertig...
Code: Alles auswählen
#!/usr/bin/python
# -*- coding: cp1252 -*-
import urllib, string
__version__="0.1"
UserEMail="test+test"
MaxReadLines=50 # Max. zu lesende Zeilen
# Use protocol version 5 to get DYEAR and DGENRE fields.
protocol = 5
default_server = 'http://freedb.freedb.org/~cddb/cddb.cgi'
ClientInfo = "&hello=%s+%s+%s&proto=%i" % (UserEMail, __file__, __version__, protocol)
def parseFreedbInfo(txt):
"""
Wandelt die freedb Daten in einen Dictionary um
"""
Info={}
for i in txt:
zeile=string.strip(i)
if zeile==".": break
if zeile[0]!="#":
Item, txt = string.split( zeile , "=", 1)
if txt!="":
Info[Item]=txt
return Info
def readHeader(response):
Line = response.readline()
header = string.strip( Line )
code, msg = string.split( header , " ", 1)
return code, msg
def GenreList():
"""
Fragt die Liste aller verfügbarer Gernes bei freedb ab
"""
url = default_server+"?cmd=cddb+lscat"+ ClientInfo
#~ print url
print "Frage Liste aller Gernes ab...",
response = urllib.urlopen(url)
code, msg = readHeader(response)
print code
# alles auslesen, bis auf die letzte Zeile
#~ lines = response.readlines()[:-1]
Genres = []
for i in response.readlines()[:-1]:
Genres.append(string.strip(i))
return Genres
def DatenLesen(discID,AllItems=False):
"""
Fragt die Daten zur CD bei freedb ab.
AllItems=True -> Es werden alle Gernes abgefragt
AllItems=False -> Fragt nur das erste verfügbare Gerne ab
"""
freedbInfo={}
for Genre in GenreList():
url = default_server+"?cmd=cddb+read+"+Genre+"+"+discID+ ClientInfo
response = urllib.urlopen(url)
code, msg = readHeader(response)
print "%14s" % Genre,"-",
if code=="210":
print "Eintrag vorhanden!"
freedbInfo[Genre] = parseFreedbInfo(response.readlines())
if AllItems==False:
# Nach dem ersten gefunden Eintrag -> abbruch
break
else:
print "kein Eintrag vorhanden"
return freedbInfo
Die Abfrage geht mit
DatenLesen(freedbID)
Verfasst: Mittwoch 10. November 2004, 13:32
von Dookie
H genrich,
Wobei... Ist es eigentlich ok, wenn man eine Funktion in einer Funktion packt??? Ich meine, eigentlich ist es praktisch, wenn man diese Funktion nur "lokal" braucht...
Lokale Funktionen braucht man nicht oft, sind aber schon ok. Besonders wenn damit die Lesbarkeit eines Programmes erleichtert wird.
Gruß
Dookie