PDA

View Full Version : AccurateRip CRC Calculation



Spoon
02-16-2010, 05:27 PM
There follows technical code on how AccurateRip TOC Identifiers and CRCs are calculated, the code is presented in c++.

Generate a AccurateRip disk ident from CD TOC



ARTOC TOC; // filled from CD drive
STAcRipDiscIdent DiscIdent;
AccurateRipFillInDiscIdent (&DiscIdent, &TOC);


(code and structures shown later)

Fetch an online track record, the records are in a standard HTTP get, url is saved in Fetch - is read from www.accuraterip.com :



char TrackOffsetsAdded[100], Fetch[255];
wsprintf(TrackOffsetsAdded, "%08x", DiscIdent.TrackOffsetsAdded);
wsprintf(Fetch, "/accuraterip/%c/%c/%c/dBAR-%03d-%08x-%08x-%08x.bin", TrackOffsetsAdded[7], TrackOffsetsAdded[6],TrackOffsetsAdded[5], DiscIdent.TrackCount, DiscIdent.TrackOffsetsAdded, DiscIdent.TrackOffsetsMultiplied,DiscIdent.FreedBI dent);


Read the track CRC records (multiple CRCs from different pressings are saved in the same file, walk the list until one matches):



// NB DiscIdent is filled with the disc ID needed, Filename is the filename of the fetched dBAR-xxx-xxxxxxx-xxxxxxx-xxxxxxx.bin file

HANDLE hFile = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
//-----find the record----
STAcRipDiscIdent AcRipDiscIdent;
STAcRipATrack AcRipATrack;
while (true)
{
DWORD Read;
if (!ReadFile(hFile, &AcRipDiscIdent, sizeof(STAcRipDiscIdent), &Read, NULL) || Read != sizeof(STAcRipDiscIdent))
break;

bool IsTheOne = true;
if (AcRipDiscIdent.TrackCount != DiscIdent.TrackCount ||
AcRipDiscIdent.TrackOffsetsAdded != DiscIdent.TrackOffsetsAdded ||
AcRipDiscIdent.TrackOffsetsMultiplied != DiscIdent.TrackOffsetsMultiplied ||
AcRipDiscIdent.FreedBIdent != DiscIdent.FreedBIdent)
IsTheOne = false;

//---entries---
for (int i = 0; i < AcRipDiscIdent.TrackCount; i++)
{
if (!ReadFile(hFile, &AcRipATrack, sizeof(STAcRipATrack), &Read, NULL) || Read != sizeof(STAcRipATrack))
{
IsTheOne = false;
break;
}
//----save it?----
if (IsTheOne)
{
// here we have one filled AcRipATrack with a track CRC, there could be many CRCs for one track, walk the list until get a matching
}
}
}
}




Calculate AccurateRip CRC from a tracks worth of audio data, AR_CRC is filled in with the track CRC:



DWORD *pAudioData = ; // this should point entire track audio data
int DataSize = ; // size of the data
int TrackNumber = ; // actual track number on disc, note that for the first & last track the first and last 5 sectors are skipped
int AudioTrackCount = ; // CD track count

//---------AccurateRip CRC checks------------
DWORD AR_CRC = 0, AR_CRCPosMulti = 1;
DWORD AR_CRCPosCheckFrom = 0;
DWORD AR_CRCPosCheckTo = DataSize / sizeof(DWORD);
*blooper*define SectorBytes 2352 // each sector
if (TrackNumber == 1) // first?
AR_CRCPosCheckFrom+= ((SectorBytes * 5) / sizeof(DWORD));
if (TrackNumber == AudioTrackCount) // last?
AR_CRCPosCheckTo-=((SectorBytes * 5) / sizeof(DWORD));


int DataDWORDSize = DataSize / sizeof(DWORD);
for (int i = 0; i < DataDWORDSize; i++)
{
if (AR_CRCPosMulti >= AR_CRCPosCheckFrom && AR_CRCPosMulti <= AR_CRCPosCheckTo)
AR_CRC+=(AR_CRCPosMulti * pAudioData[i]);

AR_CRCPosMulti++;
}


Advanced Usage: Offset finding CRC

The offset CRC is a drive offset finding CRC, it is an AccurateRip CRC calculation 450 frames in and is calculated with one frames data. This can be used when offset adjusted for the drive to find the matching CRCs of other possible pressings, finding their offset. When the offset of a pressing is known an additional CRC can be calculated using data which is offsetted by the pressing amount (using data from the adjacent tracks). The maximum offset can be +-5 frames, otherwise the first or last track would fail to verify against the pressing.

Functions and structures



//-----1 byte align-----
*blooper*pragma pack (1)

struct ARTOCTRACK // a Track in the CD table of contents
{
BYTE Reserved;
BYTE Adr;
BYTE TrackNumber;
BYTE Reserved2;
BYTE Address[4];
};

struct ARTOC // a CD Table of Contents (matches exactly with TOC taken off CD drive)
{
WORD TOCLength;
BYTE FirstTrack;
BYTE LastTrack;
ARTOCTRACK Tracks[100];
};



// A disc idenfifier
struct STAcRipDiscIdent {
unsigned char TrackCount;
DWORD TrackOffsetsAdded;
DWORD TrackOffsetsMultiplied;
DWORD FreedBIdent;
};

// A track AccurateRip CRC
struct STAcRipATrack {
unsigned char Confidence; // AccurateRip Confidence: times ripped, 200 = over 200
DWORD TrackCRC; // AccurateRip Track CRC: 0 = invalid (ie never been ripped)
DWORD OffsetFindCRC; // AccurateRip Track Offset finding CRC (can be ignored)
};


*blooper*pragma pack ()


//-----------------------------------------------------------------
//-----------------------------------------------------------------
void MSB2DWORD( DWORD *d, BYTE *b )
{
DWORD retVal;

retVal = (DWORD)b[0];
retVal = (retVal<<8) + (DWORD)b[1];
retVal = (retVal<<8) + (DWORD)b[2];
retVal = (retVal<<8) + (DWORD)b[3];

*d = retVal;
}

//---------------------------------------------------------------
//---------------------------------------------------------------
DWORD CDDBSum( DWORD n )
{
DWORD retVal = 0;
while( n > 0 )
{
retVal+= ( n % 10 );
n/= 10;
}
return retVal;
}

//-----------------------------------------------------------------
// MSF to LBA
//-----------------------------------------------------------------
void MSFToLBA( BYTE *b )
{
DWORD LBA = ( ( ( (DWORD)b[1] * (DWORD)60 ) + (DWORD)b[2] ) * (DWORD)75 ) + (DWORD)b[3] - (DWORD)150;
BYTE *pB = (BYTE *)&LBA;
b[3] = pB[0];
b[2] = pB[1];
b[1] = pB[2];
b[0] = pB[3];
}

//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
void LBA2MSF (BYTE *b )
{
DWORD LBA;
MSB2DWORD(&LBA, b);
LBA+=150;
int Mins = LBA / (60 * 75);
LBA-=(Mins * (60 * 75));
int Sec = (LBA / 75);
LBA-=(Sec * 75);
int Frames = LBA;

b[0] = 0;
b[1] = (BYTE)Mins;
b[2] = (BYTE)Sec;
b[3] = (BYTE)Frames;
}


//-------------------------------------------------------------------------------
void AccurateRipFillInDiscIdent (STAcRipDiscIdent *pRDI, ARTOC *pTOCSend)
{
ZeroMemory(pRDI, sizeof(STAcRipDiscIdent));

//----we dont want to touch orig TOC----
ARTOC TOC = *pTOCSend;
ARTOC *pTOC = &TOC;

//------create track lengths-----
DWORD lstRetTrackLBA[256]; // not possible to have more than 100, but set to 256 for safety
int lstRetTrackLBACount = 0;

DWORD dwStart, dwLen;
bool WereSomeTracks = false;
int i;
for( i = pTOC->FirstTrack; i <= pTOC->LastTrack; i++ )
{
WereSomeTracks = true;
//---only add if is audio!-----
if ( (pTOC->Tracks[i - 1].Adr & 4) == 0) // not a data track?
{
MSB2DWORD( &dwStart, pTOC->Tracks[i - 1].Address );
MSB2DWORD( &dwLen, pTOC->Tracks[i].Address );
dwLen-= dwStart;
lstRetTrackLBA[lstRetTrackLBACount] = dwStart;
lstRetTrackLBACount++;
}
}
//-------last track also------
if (WereSomeTracks) // if want addresses then give address of last END
{
MSB2DWORD( &dwStart, pTOC->Tracks[i - 1].Address );
lstRetTrackLBA[lstRetTrackLBACount] = dwStart;
lstRetTrackLBACount++;
}

pRDI->TrackCount = (unsigned char)lstRetTrackLBACount;
if (pRDI->TrackCount)
pRDI->TrackCount--; // as are addresses the last one is the end address

for (i = 0; i < lstRetTrackLBACount; i++)
{
DWORD LBA = lstRetTrackLBA[i];
pRDI->TrackOffsetsAdded+=LBA;
if (LBA == 0)
LBA++;
pRDI->TrackOffsetsMultiplied+=(LBA * (i+1));
}

//-----calc freedb-----
int NumTracks = pTOC->LastTrack - pTOC->FirstTrack + 1;
DWORD n = 0, j = 2;
DWORD pID[200];

for (int fr = pTOC->FirstTrack - 1; fr <= pTOC->LastTrack; fr++)
LBA2MSF(pTOC->Tracks[fr].Address); // conv to MSF

for( i = pTOC->FirstTrack - 1; i < pTOC->LastTrack; i++, j++ )
{
pID[j] = ((( pTOC->Tracks[i].Address[1] * 60) + pTOC->Tracks[i].Address[2]) * 75) + pTOC->Tracks[i].Address[3];
n+= CDDBSum( 60 * pTOC->Tracks[i].Address[1] + pTOC->Tracks[i].Address[2] );
}
int t = ((60 * pTOC->Tracks[NumTracks].Address[1]) + pTOC->Tracks[NumTracks].Address[2]) - ((60 * pTOC->Tracks[0].Address[1]) + pTOC->Tracks[0].Address[2]);
pID[0] = ((n%0xFF) << 24) | (t << 8) | NumTracks;
pRDI->FreedBIdent = pID[0];
}

Spoon
03-29-2010, 07:41 AM
AccurateRip 2 CRC

The CRC in AccurateRipv2 has improved accuracy, these new CRCs sit side by side with the old ones in the database (appear as a different pressing) and are calculated by:



DWORD AC_CRCNEW = 0;
DWORD MulBy = 1;
for (;; )
{
DWORD Value = {complete 32 bit sample comprising left and right channels};

unsigned __int64 CalcCRCNEW = (unsigned __int64)Value * (unsigned __int64)MulBy;
DWORD LOCalcCRCNEW = (DWORD)(CalcCRCNEW & (unsigned __int64)0xFFFFFFFF);
DWORD HICalcCRCNEW = (DWORD)(CalcCRCNEW / (unsigned __int64)0x100000000);
AC_CRCNEW+=HICalcCRCNEW;
AC_CRCNEW+=LOCalcCRCNEW;

MulBy++;
}


AR v2 CRCs are calculated over the same range as AR V1