Desde hace mucho tiempo estan disponibles dos servicios en Internet cuyo cometido es almacenar una gigantesca base de datos con los títulos de las canciones de cualquier CD de música que se haya editado. La primera es Gracenote (también conocida antes del 2000 por cddb), de la cual no hablaremos en este artículo por requerir licencia. La segunda es freedb la cual sigue siendo gratuita y con una simple página web podemos consultar la información que nos interesa. En la sección developers podreis encontrar la información necesaria para trabajar con esta tecnología.
Básicamente consiste en obtener una identificación única a partir de la información de la tabla de contenidos de un CD (TOC). Una vez obtenida la identificación, solo hay que hacer una petición web al servidor de freedb con la misma para que nos devuelva el título del CD y la lista de títulos de canciones y sus respectivos autores de las distintas pistas que conforman el CD.
El algoritmo para hallar el identificador único consiste en:
-Sumar los números en segundos de lo que dura cada pista. Es decir, si una cancion dura 4:17, entónces son 257 segundos, luego la suma es 2+5+7=14.
-Una vez obtenido el sumatorio de todas las pistas con el anterior algoritmo, se le saca el módulo de 255 y a continuación se le multiplica por 16777216 (2^24); a este resultado se le suma el tiempo total del CD multiplicado por 256 (2^8) y finalmente a este resultado se le suma el número total de pistas que contiene el CD.
Una vez hallado el identificador único, se transforma a un número hexadecimal y se hace una peticion a freedb de la siguiente manera:
http://freedb.freedb.org/~cddb/cddb.cgi?cmd=cddb+read+misc+<ID UNICO>&hello=name+host.com+appname+1.0&proto=1
cddb read misc <ID UNICO> del parámetro cmd es el comando que indica que estamos haciendo una petición a la base de datos con el ID UNICO. Por ejemplo cddb read misc c510910d.
name host.com appname 1.0 del parámetro hello indica que persona, dominio, aplicación y versión de la misma esta solicitando la información. Por ejemplo: sistemasorp sistemasorp.blogspot.com testeoCDDB 1.0.
Y finalmente el 1 del parámetro proto indica que versión del protocolo queremos usar para hacer la petición a la base de datos, devolviendonos acorde a la versión una información formateada.
Así por ejemplo si lanzamos esta petición:
Nos devuelve lo siguiente (terminado en una linea con un punto, al estilo del SMTP):
210 misc c510910d CD database entry follows (until terminating `.’)
# xmcd CD database file
#
# Track frame offsets:
# 150
# 24460
# 45650
# 71127
# 98425
# 118997
# 146585
# 164707
# 188897
# 216340
# 240932
# 271642
# 297722
#
# Disc length: 4243 seconds
#
# Revision: 2
# Processed by: cddbd v1.5PL3 Copyright (c) Steve Scherf et al.
# Submitted via: CDex 1.40Beta9
#
DISCID=c510910d
DTITLE=Various / Rave Massacre [Disk 1]
TTITLE0=Raving Bastards – Love Time
TTITLE1=Chill’n Force – Move Raver (Kemo Mix)
TTITLE2=M.A.F. X-Perience – Dreamland
TTITLE3=Society for Psychical Research – Silversky
TTITLE4=Obsessiv – Tune In, Tune On, Drop Out
TTITLE5=Nettuno – I Cry
TTITLE6=Razor – Is It Love
TTITLE7=Sunbeam – Outside World
TTITLE8=Raver’s Nature – Tricky Symphony
TTITLE9=NIP Collective – I’m About
TTITLE10=RBM – Banyo Love
TTITLE11=Paranoia X – Party Program
TTITLE12=NR-Gizer – Raving Generation
EXTD= YEAR: 1994 ID3G: 31
EXTT0=
EXTT1=
EXTT2=
EXTT3=
EXTT4=
EXTT5=
EXTT6=
EXTT7=
EXTT8=
EXTT9=
EXTT10=
EXTT11=
EXTT12=
PLAYORDER=
.
Donde entre comentarios (con la almohadilla delante) da información sobre el disco, copyright y demás; continuando con una lista de parejas nombre/valor que contienen la información que queremos:
DISCID: El mismo identificador de disco que le hemos pasado
DTITLE: El autor y título del CD
TTITLEX: El título de canción que está en la pista X
EXTD: Información extendida sobre el disco
EXTTX: Información extendida sobre la canción que está en la pista X
PLAYORDER: El orden en el que se deberían reproducir las pistas
YEAR: El año de edición del disco
ID3G: La categoría o clase de música que contiene el CD
A continuación expongo dos codigos fuentes (el primero para windows y el segundo para linux) que muestran como recoger el identificador único de un cd y a continuación solicitar la información de ese cd a freedb. Ambos se ejecutan en la consola de texto pasandoles como parámetro la unidad o dispositivo de cd.
WINDOWS(ejecutable y fuentes)
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 |
#include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock.h> #include <windows.h> #define IOCTL_CDROM_READ_TOC 0x00024000 typedef struct { UCHAR Reserved; UCHAR Control : 4; UCHAR Adr : 4; UCHAR TrackNumber; UCHAR Reserved1; UCHAR Address[4]; } TRACK_DATA; typedef struct { UCHAR Length[2]; UCHAR FirstTrack; UCHAR LastTrack; TRACK_DATA TrackData[100]; } CDROM_TOC; /* Funcion: lee_CDTOC Parámetros: sDispositivo: ruta del dispositivo de CD. toc: estructura donde se almancena la tabla de contenidos del CD. Descripción: Esta función se encarga de guardar en la variable toc la información de la tabla de contenidos del CD ubicado en el dispostivo lector de cdrom. */ void lee_CDTOC(char *sLetra,CDROM_TOC *toc) { HANDLE manejador; DWORD nTamBuffer; char sUnidad[7]; sprintf(sUnidad,"\\\\.\\%s",sLetra); manejador = CreateFile(sUnidad, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(manejador == INVALID_HANDLE_VALUE) { fprintf(stderr,"No se pudo acceder al disco de la unidad de CD\n"); return; } if (DeviceIoControl(manejador, IOCTL_CDROM_READ_TOC, NULL, 0, toc, sizeof(CDROM_TOC), &nTamBuffer, NULL) == FALSE) fprintf(stderr,"No se pudo leer el contenido del CD\n"); CloseHandle(manejador); } /* Funcion: lee_InfoCDDB: Devuelve una cadena con los resultados de la petición. Parámetros: sIDDisco: identificador único del cd. sBuffer: buffer donde almacenar los resultados de la petición. Descripción: Esta función se encarga de pedir a freedb la información relacionada con un identificador de cd determinado y devolver todo el contenido de la petición. */ char *lee_InfoCDDB(char *sIDDisco,char *sBuffer) { WSAData data; SOCKET sock; struct hostent *servidor; struct sockaddr_in destino; char sPeticion[256]; int nEnviados,nRecibidos; if(WSAStartup(MAKEWORD( 2, 2 ),&data) == 0) { if((sock=socket(PF_INET,SOCK_STREAM,0)) == INVALID_SOCKET) fprintf(stderr,"No se pudo crear la conexion"); else { if((servidor=gethostbyname("freedb.freedb.org")) == NULL) fprintf(stderr,"No se pudo obtener la IP del servidor"); else { destino.sin_family = AF_INET; destino.sin_port = htons(80); destino.sin_addr = *((struct in_addr *)servidor->h_addr_list[0]); memset(&(destino.sin_zero), '', 8); if(connect(sock, (struct sockaddr *)&destino, sizeof(struct sockaddr)) == SOCKET_ERROR) fprintf(stderr,"No se pudo conectar al servidor"); else { sprintf(sPeticion,"GET /~cddb/cddb.cgi?cmd=cddb+read+misc+%s&hello=sistemasorp+sistemasorp.blogspot.com+testeoCDDB+1.0&proto=1 HTTP/1.0\r\n",sIDDisco); strcat(sPeticion,"Host: freedb.freedb.org\r\n\r\n"); nEnviados=send(sock,sPeticion,strlen(sPeticion),0); if(nEnviados == SOCKET_ERROR) fprintf(stderr,"No se pudo enviar la peticion al servidor"); nRecibidos=0; while(strstr(sBuffer,"\r\n.\r\n") == NULL) { nRecibidos+=recv(sock,sBuffer+nRecibidos,1024,0); sBuffer[nRecibidos]=''; } } } } WSACleanup(); } else fprintf(stderr,"No se puedo inicializar la conexion"); return sBuffer; } /* Funcion: cddb_Suma: Devuelve un número sacado a través de una suma de cifras. Parámetros: nDuracion: Duración en segundos de una pista. Descripción: Esta función se encarga de sumar todas las cifras de distinto peso entre si para posteriormente devolver el resultado. */ int cddb_Suma(int nDuracion) { int nSuma = 0; while (nDuracion > 0) { nSuma = nSuma + (nDuracion % 10); nDuracion = nDuracion / 10; } return nSuma; } /* Funcion: cddb_IDDisco: Devuelve en número que identifica al CD inequivocamente. Parámetros: toc: Información de la tabla de contenidos del CD Descripción: Esta función se encarga de calcular el identificador único de un CD a través de su tabla de contenidos */ unsigned long cddb_IDDisco(CDROM_TOC *toc) { int nIndice=0, nTotalTiempo = 0, nSumatorio = 0; int nTotalPistas; nTotalPistas=toc->LastTrack-toc->FirstTrack+1; while (nIndice < nTotalPistas) { nSumatorio = nSumatorio + cddb_Suma((toc->TrackData[nIndice].Address[1] * 60) + toc->TrackData[nIndice].Address[2]); nIndice++; } nTotalTiempo = ((toc->TrackData[toc->LastTrack].Address[1] * 60) + toc->TrackData[toc->LastTrack].Address[2]) - ((toc->TrackData[0].Address[1] * 60) + toc->TrackData[0].Address[2]); return ((nSumatorio % 0xff) << 24 | nTotalTiempo << 8 | nTotalPistas); } int main(int argc, char *argv[ ]) { CDROM_TOC toc; char sIDDisco[9]; char *sBuffer; if(argc==2) { memset(&toc,0,sizeof(CDROM_TOC)); lee_CDTOC(argv[1],&toc); if(toc.LastTrack>0) { sprintf(sIDDisco,"%08x",cddb_IDDisco(&toc)); printf("El ID del disco es %s\n",sIDDisco); sBuffer=(char *)malloc(4096); if(sBuffer == NULL) { fprintf(stderr,"No se pudo alojar memoria"); return 1; } *sBuffer=''; lee_InfoCDDB(sIDDisco,sBuffer); if(*sBuffer=='') return 1; printf("La información del disco es:\n%s",sBuffer); free(sBuffer); } else return 1; } else { fprintf(stderr,"La sintaxis es: %s <unidad de cd>\nEjemplo: %s d:\n",argv[0],argv[0]); return 1; } return 0; } |
LINUX(ejecutable y fuentes)
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 |
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <stdarg.h> #include <errno.h> #include <netdb.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> #if defined linux #include <linux/cdrom.h> #elif defined sun #include <sys/cdio.h> #endif typedef struct { int minutos, segundos; } TRACK_DATA; typedef struct { unsigned char FirstTrack; unsigned char LastTrack; TRACK_DATA TrackData[100]; } CDROM_TOC; /* Funcion: lee_CDTOC Parámetros: sDispositivo: ruta del dispositivo de CD. toc: estructura donde se almancena la tabla de contenidos del CD. Descripción: Esta función se encarga de guardar en la variable toc la información de la tabla de contenidos del CD ubicado en el dispostivo lector de cdrom. */ void lee_CDTOC(char *sDispositivo,CDROM_TOC *toc) { struct cdrom_tochdr tochdr; struct cdrom_tocentry tocentry; int nIndice, manejador; manejador= open(sDispositivo, O_RDONLY | O_NONBLOCK); if(manejador == -1) { fprintf(stderr,"No se pudo acceder al disco de la unidad de CD\n"); return; } if (ioctl(manejador, CDROMREADTOCHDR, &tochdr) == -1) fprintf(stderr,"No se pudo leer el contenido del CD\n"); else { toc->FirstTrack=tochdr.cdth_trk0; toc->LastTrack=tochdr.cdth_trk1; for (nIndice = toc->FirstTrack; nIndice <= toc->LastTrack; nIndice++) { tocentry.cdte_track = nIndice; tocentry.cdte_format = CDROM_MSF; ioctl(manejador, CDROMREADTOCENTRY, &tocentry); toc->TrackData[nIndice-1].minutos = tocentry.cdte_addr.msf.minute; toc->TrackData[nIndice-1].segundos = tocentry.cdte_addr.msf.second; } tocentry.cdte_track = 0xAA; tocentry.cdte_format = CDROM_MSF; ioctl(manejador, CDROMREADTOCENTRY, &tocentry); toc->TrackData[toc->LastTrack].minutos = tocentry.cdte_addr.msf.minute; toc->TrackData[toc->LastTrack].segundos = tocentry.cdte_addr.msf.second; } close(manejador); } /* Funcion: lee_InfoCDDB: Devuelve una cadena con los resultados de la petición. Parámetros: sIDDisco: identificador único del cd. sBuffer: buffer donde almacenar los resultados de la petición. Descripción: Esta función se encarga de pedir a freedb la información relacionada con un identificador de cd determinado y devolver todo el contenido de la petición. */ char *lee_InfoCDDB(char *sIDDisco,char *sBuffer) { int sock; struct hostent *servidor; struct sockaddr_in destino; char sPeticion[256]; int nEnviados,nRecibidos; if((sock=socket(PF_INET,SOCK_STREAM,0)) == -1) fprintf(stderr,"No se pudo crear la conexion"); else { if((servidor=gethostbyname("freedb.freedb.org"))==NULL) fprintf(stderr,"No se pudo obtener la IP del servidor"); else { destino.sin_family = AF_INET; destino.sin_port = htons(80); destino.sin_addr = *((struct in_addr *)servidor->h_addr_list[0]); memset(&(destino.sin_zero), '', 8); if(connect(sock, (struct sockaddr *)&destino, sizeof(struct sockaddr)) == -1) fprintf(stderr,"No se pudo conectar al servidor"); else { sprintf(sPeticion,"GET /~cddb/cddb.cgi?cmd=cddb+read+misc+%s&hello=sistemasorp+sistemasorp.blogspot.com+testeoCDDB+1.0&proto=1 HTTP/1.0\r\n",sIDDisco); strcat(sPeticion,"Host: freedb.freedb.org\r\n\r\n"); nEnviados=send(sock,sPeticion,strlen(sPeticion),0); if(nEnviados == -1) fprintf(stderr,"No se pudo enviar la peticion al servidor"); nRecibidos=0; while(strstr(sBuffer,"\r\n.\r\n") == NULL) { nRecibidos+=recv(sock,sBuffer+nRecibidos,1024,0); sBuffer[nRecibidos]=''; } } } } return sBuffer; } /* Funcion: cddb_Suma: Devuelve un número sacado a través de una suma de cifras. Parámetros: nDuracion: Duración en segundos de una pista. Descripción: Esta función se encarga de sumar todas las cifras de distinto peso entre si para posteriormente devolver el resultado. */ int cddb_Suma(int nDuracion) { int nSuma = 0; while (nDuracion > 0) { nSuma = nSuma + (nDuracion % 10); nDuracion = nDuracion / 10; } return nSuma; } /* Funcion: cddb_IDDisco: Devuelve en número que identifica al CD inequivocamente. Parámetros: toc: Información de la tabla de contenidos del CD Descripción: Esta función se encarga de calcular el identificador único de un CD a través de su tabla de contenidos */ unsigned long cddb_IDDisco(CDROM_TOC *toc) { int nIndice=0, nTotalTiempo = 0, nSumatorio = 0; int nTotalPistas; nTotalPistas=toc->LastTrack-toc->FirstTrack+1; while (nIndice < nTotalPistas) { nSumatorio = nSumatorio + cddb_Suma((toc->TrackData[nIndice].minutos * 60) + toc->TrackData[nIndice].segundos); nIndice++; } nTotalTiempo = ((toc->TrackData[toc->LastTrack].minutos* 60) + toc->TrackData[toc->LastTrack].segundos) - ((toc->TrackData[0].minutos * 60) + toc->TrackData[0].segundos); return ((nSumatorio % 0xff) << 24 | nTotalTiempo << 8 | nTotalPistas); } int main(int argc, char *argv[ ]) { CDROM_TOC toc; char sIDDisco[9]; char *sBuffer; if(argc==2) { memset(&toc,0,sizeof(CDROM_TOC)); lee_CDTOC(argv[1],&toc); if(toc.LastTrack>0) { sprintf(sIDDisco,"%08x",cddb_IDDisco(&toc)); printf("El ID del disco es %s\n",sIDDisco); sBuffer=(char *)malloc(4096); if(sBuffer == NULL) { fprintf(stderr,"No se pudo alojar memoria"); return 1; } *sBuffer=''; lee_InfoCDDB(sIDDisco,sBuffer); if(*sBuffer=='') return 1; printf("La información del disco es:\n%s",sBuffer); free(sBuffer); } else return 1; } else { fprintf(stderr,"La sintaxis es: %s <dispositivo de cd>\nEjemplo: %s /dev/cdrom\n",argv[0],argv[0]); return 1; } return 0; } |
Podrías escribir código para Vb6?
Gracias
El enlace para la descarga no funciona, ¿me lo puede enviar al correo electronico?.
Tanto para Linux como para Windows.
¡Gracias!
Jose María, ya tienes los enlaces arreglados.
hola, he descargado el ejecutable en linux. Me podrías decir como compilarlo i lanzarlo en ubuntu.? Se puede lanzar como si fuera un script?. He intentado compilarlo con configure y make pero….pues no. Agradecido por cualquier sugerencia.
Hola. Me temo que el servicio ha dejado de estar disponible.