123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- #include "DAEPlay.h"
- #include "api__in_cdda.h"
- #include "../nu/AutoWide.h"
- #include <strsafe.h>
- int DAEPlay::getTrackInfo()
- {
- CDROM_TOC tableOfContents = {0};
- DWORD tocSize = sizeof(tableOfContents);
- if (!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &tableOfContents, tocSize, &tocSize, 0))
- {
- return 1;
- }
- for (int i = tableOfContents.FirstTrack - 1 ; i < tableOfContents.LastTrack ; i += 1)
- {
- if (i == g_track)
- {
- track_length = MSFToBlocks(tableOfContents.TrackData[i+1].Address) - (start_address = MSFToBlocks(tableOfContents.TrackData[i].Address));
- return 0;
- }
- }
- return 1;
- }
- DAEPlay::CDTextArray* DAEPlay::getCDText()
- {
- // if we've already cached it, return asap
- if (cd_text != 0)
- {
- return cd_text;
- }
- CDROM_TOC tableOfContents = {0};
- DWORD tocSize = sizeof(tableOfContents), returned = 0;
- if (!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &tableOfContents, tocSize, &tocSize, 0))
- {
- // issue accessing drive
- return (cd_text = (CDTextArray *)-1);
- }
- // MMC-3 Draft Revision 10g: Table 222 Q Sub-channel control field
- tableOfContents.TrackData[0].Control &= 5;
- if (!(tableOfContents.TrackData[0].Control == 0/* || tableOfContents.TrackData[0 - 1].Control == 1*/))
- {
- // invalid format
- return (cd_text = (CDTextArray *)-1);
- }
- CDROM_READ_TOC_EX tableOfContentsEx = {0};
- tableOfContentsEx.Format = CDROM_READ_TOC_EX_FORMAT_CDTEXT;
- WORD tocSizeEx = 0;
- // Read the contents to get the size of the actual data
- if (!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC_EX, &tableOfContentsEx, sizeof(tableOfContentsEx), &tocSizeEx, sizeof(tocSizeEx), &returned, 0))
- {
- // issue accessing drive
- return (cd_text = (CDTextArray *)-1);
- }
- // The bytes are swapped so we need to switch them around
- tocSizeEx = ((tocSizeEx>>8) | (tocSizeEx<<8)) + sizeof(tocSizeEx);
- // Allocate a buffer for reading the actual CD Text data block
- char *pCDTextData = new char[tocSizeEx];
- if (!pCDTextData)
- {
- return (cd_text = (CDTextArray *)-1);
- }
- memset(pCDTextData, 0, tocSizeEx);
- // Now read the data
- if(!DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC_EX, &tableOfContentsEx, sizeof(tableOfContentsEx), pCDTextData, tocSizeEx, &returned, 0))
- {
- delete []pCDTextData;
- return (cd_text = (CDTextArray *)-1);
- }
- tocSizeEx = (WORD)(returned - sizeof(CDROM_TOC_CD_TEXT_DATA));
- if(!tocSizeEx)
- {
- delete []pCDTextData;
- return (cd_text = (CDTextArray *)-1);
- }
- // This is the stuff we really need. It's an array of packs with the data (mostly text)
- CDROM_TOC_CD_TEXT_DATA_BLOCK* pCDTextBlock = ((CDROM_TOC_CD_TEXT_DATA*)(BYTE*)pCDTextData)->Descriptors;
- // As we go through the packets we'll store the strings in this array. The strings are often in different packets and need to be concatenated together.
- cd_text = new CDTextArray[CD_TEXT_NUM_PACKS];
- UINT m_nGenreCode = -1;
- // Loop through all the packets extracting the data we need. Each packet starts with a packet type, the track number, language code,
- // character offset, and character size. The packets end with a 2 byte CRC. We don't need most of this stuff to display the info. We can get
- // the packets for any track and they'll have all the data for each track (it's duplicated).
- for( ;tocSizeEx >= sizeof(CDROM_TOC_CD_TEXT_DATA_BLOCK); tocSizeEx -= sizeof(CDROM_TOC_CD_TEXT_DATA_BLOCK), pCDTextBlock++)
- {
- if (pCDTextBlock->TrackNumber > tableOfContents.LastTrack) // Track is beyond what is on the disc
- continue;
-
- // Only looking for CD Text item packets
- if ((pCDTextBlock->PackType -= 0x80) >= 0x10)
- continue;
-
- // Genre is encoded with the code and the supplemental text in the same packet
- if (m_nGenreCode == -1 && pCDTextBlock->PackType == CD_TEXT_GENRE)
- {
- // TODO
- m_nGenreCode = pCDTextBlock->Text[0]*16 + pCDTextBlock->Text[1];
- /*CString Text = !pCDTextBlock->Unicode
- ? CString(CStringA((CHAR*)pCDTextBlock->Text+2, CD_TEXT_FIELD_LENGTH-2))
- : CString(CStringW((WCHAR*)pCDTextBlock->WText+2, CD_TEXT_FIELD_LENGTH-2));
- cd_text[pCDTextBlock->PackType][0] = Text;*/
- }
- else
- {
- // Parse the text. There could be more than one item in the text block separated by null bytes so we need to check the whole thing
- // The text is in a block of up to 12 characters. We'll just keep adding to our buffers until we go through all of the packets.
- int nLengthRemaining = CD_TEXT_FIELD_LENGTH;
- UINT nTrack = pCDTextBlock->TrackNumber;
- UINT nOffset = 0;
- // We're at the end of text when:
- // Used up 12 chars
- // Got to a null
- // On the last track
- while (nTrack <= tableOfContents.LastTrack && nLengthRemaining > 0 && nOffset < CD_TEXT_FIELD_LENGTH)
- {
- wchar_t *text = (wchar_t*)calloc(nLengthRemaining + 1, sizeof(wchar_t));
- if (!text) continue;
- lstrcpyn(text, (pCDTextBlock->Unicode ? pCDTextBlock->WText + nOffset : AutoWide((char *)pCDTextBlock->Text + nOffset)), nLengthRemaining + 1);
- if (!cd_text[pCDTextBlock->PackType][nTrack])
- cd_text[pCDTextBlock->PackType][nTrack] = (wchar_t*)calloc(lstrlen(text) + 1, sizeof(wchar_t));
- else // TODO error handling
- cd_text[pCDTextBlock->PackType][nTrack] = (wchar_t*)realloc(cd_text[pCDTextBlock->PackType][nTrack], (lstrlen(cd_text[pCDTextBlock->PackType][nTrack]) + lstrlen(text) + 1) * sizeof(wchar_t));
- if (text[0])
- lstrcat(cd_text[pCDTextBlock->PackType][nTrack], text);
- nOffset += lstrlen(text) + 1;
- nLengthRemaining = nLengthRemaining - lstrlen(text) - 1;
- ++nTrack;
- free(text);
- }
- }
- } while (0);
- delete []pCDTextData;
- return cd_text;
- }
- int DAEPlay::threadProc2()
- {
- while (1)
- {
- if (need_seek != -1)
- {
- current_sector = ((need_seek * CD_BLOCKS_PER_SECOND) / 1000) / DEF_SECTORS_PER_READ;
- bytes_in_sbuf = 0;
- line.outMod->Flush(need_seek);
- need_seek = -1;
- }
- if (fillBuffer(killswitch))
- {
- PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- return 0;
- }
- if (!bytes_in_sbuf && !killswitch)
- {
- //wait for output to be finished
- line.outMod->Write(NULL, 0);
- while (!killswitch && line.outMod->IsPlaying()) Sleep(10);
- if (!killswitch)
- {
- CloseHandle(hDrive);
- hDrive = INVALID_HANDLE_VALUE;
- PostMessage(line.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- }
- return 0;
- }
- if (killswitch)
- {
- CloseHandle(hDrive);
- hDrive = INVALID_HANDLE_VALUE;
- return 0;
- }
- char sample_buffer[576*4*2] = {0};
- int bytes = sizeof(sample_buffer) / 2; // enough room for dsp bullcrap
- bytes = min((int)bytes_in_sbuf, (int)bytes);
- memcpy(sample_buffer, sbuf, bytes);
- if (bytes_in_sbuf > bytes) memcpy(sbuf, sbuf + bytes, bytes_in_sbuf - bytes);
- bytes_in_sbuf -= bytes;
- line.VSAAddPCMData(sample_buffer, g_nch, 16, line.outMod->GetWrittenTime());
- line.SAAddPCMData(sample_buffer, g_nch, 16, line.outMod->GetWrittenTime());
- if (line.dsp_isactive())
- bytes = line.dsp_dosamples((short *)sample_buffer, bytes / g_nch / 2, 16, g_nch, 44100) * (g_nch * 2);
- while (line.outMod->CanWrite() < bytes && !killswitch) Sleep(66);
- if (killswitch)
- {
- CloseHandle(hDrive);
- hDrive = INVALID_HANDLE_VALUE;
- return 0;
- }
- line.outMod->Write(sample_buffer, bytes);
- }
- CloseHandle(hDrive);
- hDrive = INVALID_HANDLE_VALUE;
- return 0;
- }
- void DAEPlay::stop()
- {
- if (hThread)
- {
- killswitch = 1;
- WaitForSingleObject(hThread, INFINITE);
- }
- line.outMod->Close();
- }
- int DAEPlay::open(wchar_t drive, int track) //called by winampGetExtendedRead
- {
- g_track = track-1;
- if (g_track < 0)
- return 1;
- g_drive = drive;
- wchar_t CDDevice[8]=L"\\\\.\\x:";
- CDDevice[4] = drive;
- if (hDrive != INVALID_HANDLE_VALUE)
- CloseHandle(hDrive);
- hDrive = CreateFile(CDDevice, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (hDrive == INVALID_HANDLE_VALUE)
- return 1;
- // TODO only store the request track!
- if (getTrackInfo())
- return 1;
- g_playlength = (track_length / CD_BLOCKS_PER_SECOND) * 1000;
- bytes_in_sbuf = 0;
- if (!sbuf)
- sbuf = (unsigned char *)malloc(2352 * buf_size);
- if (!sbuf)
- return 1;
- data = new BYTE[DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR];
- if (!data)
- return 1;
- return 0;
- }
- int DAEPlay::play(wchar_t drive, int track) //called by winamp2's normal(old) play() interface
- {
- int old_drive = g_drive;
- if (open(drive, track)) return 1;
- // do this here as it helps to prevent an audio glitch on first playback and volume is set low
- setvolume(a_v, a_p);
- int maxlat = line.outMod->Open(44100, g_nch, 16, -1, -1);
- if (maxlat < 0)
- {
- g_drive = 0;
- return 1;
- }
- // to prevent re-spinning as we're not going to get cd-text later
- // if it's not able to be obtained when first opening the device.
- if (old_drive != drive)
- {
- if ((int)cd_text > 0)
- delete []cd_text;
- cd_text = NULL;
- getCDText();
- }
- line.SetInfo(1411, 44, g_nch, 1);
- line.SAVSAInit(maxlat, 44100);
- line.VSASetInfo(g_nch, 44100);
- line.is_seekable = 1;
- bytes_in_sbuf = 0;
- current_sector = 0;
- killswitch = 0;
- DWORD thread_id = 0;
- hThread = CreateThread(NULL, NULL, &threadProc, (LPVOID)this, NULL, &thread_id);
- SetThreadPriority(hThread, AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
- //open the device thru MCI (for getfileinfo to work properly)
- g_playtrack = track;
- return 0;
- }
- int DAEPlay::fillBuffer(int kill)
- {
- if (!kill && bytes_in_sbuf <= 0)
- {
- if (current_sector < (track_length / DEF_SECTORS_PER_READ))
- {
- // Contains an offset into the CD-ROM disc where data will be read. You can calculate this offset by multiplying the starting sector number for the request times 2048.
- RAW_READ_INFO _RawReadInfo = {0};
- _RawReadInfo.DiskOffset.QuadPart = ((current_sector * DEF_SECTORS_PER_READ) + start_address) * CDROM_COOKED_BYTES_PER_SECTOR;
- _RawReadInfo.TrackMode = CDDA;
- _RawReadInfo.SectorCount = DEF_SECTORS_PER_READ;
- DWORD data_length = DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR;
- if (!DeviceIoControl(hDrive, IOCTL_CDROM_RAW_READ, &_RawReadInfo, sizeof(_RawReadInfo),
- data, data_length, &data_length, 0))
- {
- CloseHandle(hDrive);
- hDrive = INVALID_HANDLE_VALUE;
- return 1;
- }
- // TODO make sure the buffer size is enough for our needs
- memcpy(sbuf, data, DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR);
- bytes_in_sbuf += DEF_SECTORS_PER_READ * CDROM_RAW_BYTES_PER_SECTOR;
- }
- current_sector++;
- }
- return 0;
- }
- int DAEPlay::read(char *dest, int len, int *killswitch) //called by winampGetExtendedRead_getData
- {
- int l = 0;
- // TODO make sure this will handle a CD ejection...
- while (l < len && !*killswitch)
- {
- if (fillBuffer(*killswitch))
- {
- return -1;
- }
- if (!bytes_in_sbuf) break;
- int bytes = min(bytes_in_sbuf, len - l);
- memcpy(dest + l, sbuf, bytes);
- if (bytes_in_sbuf > bytes) memcpy(sbuf, sbuf + bytes, bytes_in_sbuf - bytes);
- bytes_in_sbuf -= bytes;
- l += bytes;
- }
- return l;
- }
- DAEPlay::DAEPlay()
- {
- sbuf = NULL;
- data = NULL;
- cd_text = NULL;
- bytes_in_sbuf = 0;
- buf_size = 64; //make it configitem
- g_track = -1;
- // fix these three as that's normal
- g_nch = 2;
- g_srate = 44100;
- g_bps = 16;
- killswitch = 0;
- hDrive = INVALID_HANDLE_VALUE;
- hThread = NULL;
- need_seek = -1;
- current_sector = 0;
- start_address = 0;
- track_length = 0;
- }
- DAEPlay::~DAEPlay()
- {
- if (hDrive != INVALID_HANDLE_VALUE)
- {
- CloseHandle(hDrive);
- hDrive = INVALID_HANDLE_VALUE;
- }
- if (sbuf)
- {
- free(sbuf);
- sbuf = NULL;
- }
- if (data)
- {
- delete []data;
- data = NULL;
- }
- if ((int)cd_text > 0)
- {
- delete []cd_text;
- }
- cd_text = NULL;
- }
|