title
Products            Buy            Support Forum            Professional            About            Codec Central
 

AccurateRip CRC Calculation

Collapse
This topic is closed.
X
This is a sticky topic.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Spoon
    Administrator
    • Apr 2002
    • 44547

    AccurateRip CRC Calculation

    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

    Code:
        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 :

    Code:
    	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.FreedBIdent);
    Read the track CRC records (multiple CRCs from different pressings are saved in the same file, walk the list until one matches):

    Code:
    	// 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:

    Code:
    	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

    Code:
    //-----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
    www.dbpoweramp.com
  • Spoon
    Administrator
    • Apr 2002
    • 44547

    #2
    Re: AccurateRip CRC Calculation

    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:

    Code:
        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
    Last edited by Spoon; April 04, 2013, 08:23 AM.
    Spoon
    www.dbpoweramp.com

    Comment

    Working...

    ]]>