#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2009 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using Nuclex.Support.Tracking;
namespace Nuclex.Audio.Metadata {
///
/// Opens connections to a CDDB database and allows querying of CD titles
///
public static class Cddb {
#region struct Credentials
/// Credentials with which to log into a CDDB server
public struct Credentials {
/// Initializes a new CDDB login credentials structure
/// Login name of the user
/// Host name of the client
/// NAme of the connecting client software
/// Version number of the client software
public Credentials(
string user, string hostName, string clientName, string version
) {
this.User = user;
this.HostName = hostName;
this.ClientName = clientName;
this.Version = version;
}
/// Login name of user. Example: johndoe
public string User;
/// Host name of client. Example: abc.fubar.com
public string HostName;
///
/// The name of the connecting client. Example: xmcd, cda, EasyCD, et cetera.
///
///
/// Do not use the name of another client which already exists.
///
public string ClientName;
/// Version number of client software. Example: v1.0PL0
public string Version;
}
#endregion // struct Credentials
#region struct DTitle
/// Stores the contents of a CDDB DTITLE field
public struct DTitle {
/// Initializes a new CDDB DTITLE structure
/// Artist that released the song or the album
/// Name of the song or of the album
public DTitle(string artist, string title) {
this.Artist = artist;
this.Title = title;
}
/// Artist that released the song or the album
public string Artist;
/// Name of the song or of the album
public string Title;
}
#endregion // struct DTitle
#region struct Disc
/// Informations about a disc that has been queried for
public struct Disc {
/// Initializes a new disc informations structure
/// Category the disc is catalogued under
/// CDDB disc id of the disc
/// The artist of the disc
/// The title of the disc
public Disc(string category, int discId, string artist, string title) {
this.Category = category;
this.DiscId = discId;
this.Artist = artist;
this.Title = title;
}
/// Musical category of the CD, possibly wrong
///
/// When CDDB came to existence, only 11 musical categories were defined with
/// a strong bias towards the creator's musical knowledge and/or taste.
/// Furthermore, CDDB servers a wary of changing these categories due to
/// crappy clients that are likely to break. Thus, today the categories are
/// mainly used to provide additional storage slots if duplicate disc ids occur.
///
public string Category;
/// CDDB disc id of the disc
public int DiscId;
/// Artist of the CD
public string Artist;
/// Title of the CD
public string Title;
}
#endregion // struct Disc
#region struct DatabaseEntry
/// Stores the contents of an XMCD database file
public struct DatabaseEntry {
/// Frame offsets of the individual tracks on the CD
public int[] TrackFrameOffsets;
/// Total length of the CD in seconds
public int DiscLengthSeconds;
/// Revision number of the database entry
public int Revision;
/// Application that has submitted the CDDB database entry
public string Submitter;
/// CDDB disc ids for this disc
///
/// Storing multiple CDDB disc ids in a database entry has been deprecated,
/// normally you should only encounter entries with a single CDDB disc id.
///
public int[] DiscIds;
/// Name of the artist who released this album
///
/// If the CD is a sampler consisting of tracks from several artists, this
/// field will be set to 'Various'
///
public string Artist;
/// Name of this album
public string Album;
/// Year that album was released in
public int Year;
/// Name of the genre the CD is categorized under
public string Genre;
/// Track titles of the individual tracks on the CD
public DTitle[] Tracks;
}
#endregion // struct DatabaseEntry
///
/// Calculates the 'disc id' of a CD for submission or retrieval from a CDDB database
///
///
/// Total length of the CD (from the beginning of the first track to the end of the
/// last track) in seconds
///
///
/// Offsets of the individual tracks on the CD in seconds
///
/// The CDDB disc id of a CD with the provided characteristics
///
/// Disc ids are not guaranteed to be unique. In fact, it is quite likely to find
/// duplicate ids all over the place. This is due to the CDDB disc id algorithm
/// which is severely flawed and has to be accepted. See the remarks on the
/// enumeration for how to get up to 11 CDs with the same
/// disc id enlisted in a CDDB database.
///
public static int CalculateDiscId(
int discLengthSeconds, int[] trackOffsetsSeconds
) {
int trackCount = trackOffsetsSeconds.Length;
// First, the checksum needs to be calculated. This is done by calculating the
// sum of digits for the starting offset (in seconds) of each track on the CD,
// which are then added together and taken modulo 255 to arrive at the checksum.
int offsetChecksum = 0;
for(int trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
int trackOffsetSeconds = trackOffsetsSeconds[trackIndex];
// Calculate the sum of digits for this track's starting offset
int sumOfDigits = 0;
while(trackOffsetSeconds > 0) {
sumOfDigits += trackOffsetSeconds % 10;
trackOffsetSeconds /= 10;
}
offsetChecksum += sumOfDigits;
}
offsetChecksum %= 255;
// The rest of the CDDB disc id calculation is trivial: after the checksum (stored in
// the highest byte), the next two bytes carry the total length of the CD in seconds
// and the lowest byte is the number of tracks on the cD.
int discId =
(offsetChecksum << 24) |
(discLengthSeconds << 8) |
(trackCount & 0xFF);
return discId;
}
/// Splits a CDDB DTITLE field into its artist and album name parts
///
/// String containing a CDDB DTITLE field that will be split
///
/// Two strings containing the artist and the album name
public static DTitle SplitDiscTitle(string discTitle) {
int separatorIndex = discTitle.IndexOf(" / ");
if(separatorIndex == -1) {
return new DTitle(discTitle, discTitle);
} else {
return new DTitle(
discTitle.Substring(0, separatorIndex),
discTitle.Substring(separatorIndex + 3)
);
}
}
/// Establishes a connection to a public freedb database mirror
/// Credentials by which to log in to the CDDB server
/// A request that provides the CDDB connection upon completion
public static Request Connect(Credentials credentials) {
return Connect("freedb.freedb.org", credentials);
}
///
/// Establishes a connection to the specified CDDB compatible database server
///
/// URL or IP address of the server to connect to
/// Credentials by which to log in to the CDDB server
/// A request that provides the CDDB connection upon completion
public static Request Connect(string server, Credentials credentials) {
Requests.CddbConnectionRequest connectionRequest =
new Requests.CddbConnectionRequest(server, 8880, credentials);
connectionRequest.Start();
return connectionRequest;
}
}
} // namespace Nuclex.Audio.Metadata