Click here to Skip to main content
15,913,722 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: (untagged)
I am trying to extract song information from a CD to build a database of songs that I have. I have gleaned code from a variety of places and can get the song infotmation from a few of my CDs but the ones that show an album cover in Windows Media to do work with the code shown below. Is there a different way to extract the song information?

///////////////////////////////////////////////////////////////////////////////
int CAudioCD::GetTrackInfo(HANDLE cdHandl,
						std::vector<struct trackInfoRec *> &trackInfoList)
{
	int status = -1;
	CDROM_READ_TOC_EX cdTocEx;
	WORD size = 0;
	BYTE *cdTextData = NULL;
	DWORD bytesRead;
	int trackNumber = 0;
	std::string discTitle;
	std::string discArtist;
	std::string songWriter;
	std::string composer;
	std::string arranger;
	std::string messages;
	std::string diskId;
	std::string info;
	std::string info2;
	std::string ean;
	std::string closedInfo;
	memset(&cdTocEx, 0, sizeof(cdTocEx));
	cdTocEx.Format = CDROM_READ_TOC_EX_FORMAT_CDTEXT;
	cdTocEx.SessionTrack = 0;
	if (DeviceIoControl(cdHandl, IOCTL_CDROM_READ_TOC_EX, &cdTocEx,
					sizeof(cdTocEx), &size, sizeof(size), &bytesRead, 0) == TRUE)
	{
		// byte swap the size
		size = ((size>>8)|(size<<8)) + sizeof(size);
		// could also set size as
		//     size = 2048; or
		//     size = 2352;
		// to create a really larger buffer to hold anything
		cdTextData = new BYTE[size];
		memset(cdTextData, 0, size);
		CString text;
		if(DeviceIoControl(cdHandl, IOCTL_CDROM_READ_TOC_EX, &cdTocEx,
							sizeof(cdTocEx), cdTextData, size, &bytesRead, 0))
		{
			size = (WORD)(bytesRead - sizeof(CDROM_TOC_CD_TEXT_DATA));
			CDROM_TOC_CD_TEXT_DATA *textData = (CDROM_TOC_CD_TEXT_DATA*)cdTextData;
			CDROM_TOC_CD_TEXT_DATA *dataBlock = textData->Descriptors;
 			CString last;
			while (size >= sizeof(CDROM_TOC_CD_TEXT_DATA_BLOCK))
			{
				const int len = _countof(dataBlock->Text);
				CString text = !dataBlock->Unicode 
					? CString(CStringA((CHAR*)dataBlock->Text, len))
					: CString(CStringW((WCHAR*)dataBlock->WText, len));
	//			int tlen = text.GetLength();  - this was in the original but does not work right
				int tlen = strlen(text.GetString());
				CString tmp = (tlen < 12-1)
					? (!dataBlock->Unicode 
						? CString(CStringA((CHAR*)dataBlock->Text+tlen+1, len-(tlen+1)))
						: CString(CStringW((WCHAR*)dataBlock->WText+tlen+1, len-(tlen+1))))
					: _T("");
				switch(dataBlock->PackType)
				{
					case CDROM_CD_TEXT_PACK_ALBUM_NAME:
						if (last.GetLength() > 0)
						{
							discTitle = last;
						}
						discTitle += text.GetString();
					break;
					case CDROM_CD_TEXT_PACK_PERFORMER:
						discArtist += text;
					break;
					case CDROM_CD_TEXT_PACK_SONGWRITER:
						songWriter += text;
					break;
					case CDROM_CD_TEXT_PACK_COMPOSER:
						composer += text;
					break;
					case CDROM_CD_TEXT_PACK_ARRANGER:
						arranger += text;
					break;
					case CDROM_CD_TEXT_PACK_MESSAGES:
						messages += text;
					break;
					case CDROM_CD_TEXT_PACK_DISC_ID:
						diskId += text;
					break;
					case CDROM_CD_TEXT_PACK_GENRE:
						// still need code to parse genre - it is binary in the text block
					break;
					case CDROM_CD_TEXT_PACK_TOC_INFO:
						info += text;
					break;
					case CDROM_CD_TEXT_PACK_TOC_INFO2:
						info2 += text;
					break;
					case CDROM_CD_TEXT_PACK_UPC_EAN:
						ean += text;
					break;
					case CDROM_CD_TEXT_PACK_SIZE_INFO:
		
					break;
					case CDROM_CD_TEXT_PACK_CLOSED_INFO:
						closedInfo += text;
					break;
					default:
					break;
				}
				last = tmp;
				size -= sizeof(CDROM_TOC_CD_TEXT_DATA_BLOCK);
				dataBlock++;
				if (dataBlock->TrackNumber != trackNumber)
				{
					trackNumber = dataBlock->TrackNumber;
					struct trackInfoRec *trackInfoPtr = new struct trackInfoRec;
					if (trackInfoPtr != NULL)
					{
						trackInfoList.push_back(trackInfoPtr);
						trackInfoPtr->trackNumber = trackNumber;
						trackInfoPtr->discTitle = discTitle;
						trackInfoPtr->discArtist = discArtist;
						trackInfoPtr->songWriter = songWriter;
						trackInfoPtr->composer = composer;
						trackInfoPtr->arranger = arranger;
						trackInfoPtr->messages = messages;
						trackInfoPtr->diskId = diskId;
						trackInfoPtr->info = info;
						trackInfoPtr->info2 = info2;
						trackInfoPtr->ean = ean;
						trackInfoPtr->closedInfo = closedInfo;
					}
					discTitle.erase();
					discArtist.erase();
					songWriter.erase();
					composer.erase();
					arranger.erase();
					messages.erase();
					diskId.erase();
					info.erase();
					info2.erase();
					ean.erase();
					closedInfo.erase();
					status = 0;
				}
			}
		}
	}
	return(status);
}
Posted

1 solution

Most CD Identification[^] apps use Gracenote or freedb[^] to lookup CDs against a Disc ID[^] generated by lots of horrible IOCTL calls
 
Share this answer
 
Comments
Bruce Clay 18-Jun-12 19:44pm    
While this is a possibility it is not an ideal solution. The sites that show how to create a disk-id all state that the algorithim does not guarentee uniqueness. That would require manual intervention in the process to see if the right information was returned from MusicBrainz or GraceNote.

Somewhere I thought I have seen a project that allows code to extract the embedded CD "jacket" and reprint it but I can't find that project anymore.
barneyman 18-Jun-12 20:05pm    
from experience, there's ~5% clash rate

this?
Bruce Clay 18-Jun-12 21:59pm    
I dis see some of the SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER stuff when I was doing this project but I did not see how it gave any more information than the simipler vsrion using the ioctl. I may have overlooked something though. Thanks for your suggestions.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900