CdTextEncoder.cc
上传用户:weiliju62
上传日期:2007-01-06
资源大小:619k
文件大小:14k
源码类别:

SCSI/ASPI

开发平台:

MultiPlatform

  1. /*  cdrdao - write audio CD-Rs in disc-at-once mode
  2.  *
  3.  *  Copyright (C) 1998, 1999 Andreas Mueller <mueller@daneb.ping.de>
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19. /*
  20.  * $Log: CdTextEncoder.cc,v $
  21.  * Revision 1.1  1999/06/13 19:31:15  mueller
  22.  * Initial revision
  23.  *
  24.  */
  25. #include "CdTextEncoder.h"
  26. #include <stddef.h>
  27. #include <string.h>
  28. #include <assert.h>
  29. #include "util.h"
  30. #include "Toc.h"
  31. #include "CdTextItem.h"
  32. #include "PWSubChannel96.h"
  33. static char rcsid[] = "$Id: CdTextEncoder.cc,v 1.1 1999/06/13 19:31:15 mueller Exp mueller $";
  34. unsigned short CdTextEncoder::CRCTAB_[256] = {
  35.   0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
  36.   0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
  37.   0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
  38.   0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
  39.   0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
  40.   0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
  41.   0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
  42.   0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
  43.   0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
  44.   0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
  45.   0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
  46.   0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
  47.   0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
  48.   0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
  49.   0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
  50.   0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
  51.   0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
  52.   0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
  53.   0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
  54.   0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
  55.   0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
  56.   0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
  57.   0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
  58.   0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
  59.   0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
  60.   0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
  61.   0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
  62.   0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
  63.   0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
  64. };
  65. class CdTextPackEntry {
  66. public:
  67.   CdTextPackEntry(unsigned char packType, unsigned char trackNr, 
  68.   unsigned char packId);
  69.   int blockNr();
  70.   void blockNr(int);
  71.   void characterPos(int);
  72.   
  73.   union {
  74.     CdTextPack pack;
  75.     unsigned char packData[18];
  76.   };
  77.   CdTextPackEntry *next_;
  78. };
  79. CdTextPackEntry::CdTextPackEntry(unsigned char packType, unsigned char trackNr,
  80.  unsigned char packId)
  81. {
  82.   memset(&pack, 0, sizeof(pack));
  83.   next_ = NULL;
  84.   pack.packType = packType;
  85.   pack.trackNumber = trackNr;
  86.   pack.sequenceNumber = packId;
  87. }
  88. int CdTextPackEntry::blockNr()
  89. {
  90.   return (pack.blockCharacter >> 4) & 0x07;
  91. }
  92. void CdTextPackEntry::blockNr(int blockNr)
  93. {
  94.   pack.blockCharacter &= 0x8f;
  95.   pack.blockCharacter |= (blockNr << 4) & 0x70;
  96. }
  97. void CdTextPackEntry::characterPos(int pos)
  98. {
  99.   pack.blockCharacter &= 0xf0;
  100.   if (pos > 15) {
  101.     pack.blockCharacter |= 0x0f;
  102.   }
  103.   else {
  104.     pack.blockCharacter |= pos;
  105.   }
  106. }
  107. CdTextEncoder::CdTextEncoder(const Toc *toc)
  108. {
  109.   int i;
  110.   toc_ = toc;
  111.   packCount_ = 0;
  112.   packs_ = lastPack_ = NULL;
  113.   lastPackPos_ = 0;
  114.   subChannels_ = NULL;
  115.   subChannelCount_ = 0;
  116.   for (i = 0; i < 8; i++)
  117.     memset(&(sizeInfo_[i]), 0, sizeof(CdTextSizeInfo));
  118. }
  119. CdTextEncoder::~CdTextEncoder()
  120. {
  121.   long i;
  122.   CdTextPackEntry *pnext;
  123.   toc_ = NULL;
  124.   while (packs_ != NULL) {
  125.     pnext = packs_->next_;
  126.     delete packs_;
  127.     packs_ = pnext;
  128.   }
  129.   lastPack_ = NULL;
  130.   if (subChannels_ != NULL) {
  131.     for (i = 0; i < subChannelCount_; i++) {
  132.       delete subChannels_[i];
  133.       subChannels_[i] = NULL;
  134.     }
  135.     
  136.     delete[] subChannels_;
  137.     subChannels_ = NULL;
  138.   }
  139. }
  140. int CdTextEncoder::encode()
  141. {
  142.   buildPacks();
  143.   calcCrcs();
  144.   if (packs_ != NULL) {
  145.     message(3, "nCD-TEXT packs:");
  146.     CdTextPackEntry *prun;
  147.     for (prun = packs_; prun != NULL; prun = prun->next_) {
  148.       message(3, "%02x %02x %02x %02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x  CRC: %02x %02x", prun->pack.packType,
  149.       prun->pack.trackNumber, prun->pack.sequenceNumber,
  150.       prun->pack.blockCharacter, prun->pack.data[0],
  151.       prun->pack.data[1], prun->pack.data[2], prun->pack.data[3],
  152.       prun->pack.data[4], prun->pack.data[5], prun->pack.data[6],
  153.       prun->pack.data[7], prun->pack.data[8], prun->pack.data[9],
  154.       prun->pack.data[10], prun->pack.data[11],
  155.       prun->pack.crc0, prun->pack.crc1);
  156.     }
  157.   }
  158.   
  159.   buildSubChannels();
  160.   return 0;
  161. }
  162. const PWSubChannel96 **CdTextEncoder::getSubChannels(long *subChannelCount)
  163. {
  164.   *subChannelCount = subChannelCount_;
  165.   return (const PWSubChannel96 **)subChannels_;
  166. }
  167. void CdTextEncoder::buildPacks()
  168. {
  169.   int blockNr;
  170.   for (blockNr = 0; blockNr <= 7; blockNr++) {
  171.     if (toc_->cdTextLanguage(blockNr) >= 0) {
  172.       // only build the packs if the language code is defined
  173.       packId_ = 0;
  174.     
  175.       buildPacks(blockNr, CdTextItem::CDTEXT_TITLE);
  176.       buildPacks(blockNr, CdTextItem::CDTEXT_PERFORMER);
  177.       buildPacks(blockNr, CdTextItem::CDTEXT_SONGWRITER);
  178.       buildPacks(blockNr, CdTextItem::CDTEXT_COMPOSER);
  179.       buildPacks(blockNr, CdTextItem::CDTEXT_ARRANGER);
  180.       buildPacks(blockNr, CdTextItem::CDTEXT_MESSAGE);
  181.       buildPacks(blockNr, CdTextItem::CDTEXT_DISK_ID);
  182.       buildPacks(blockNr, CdTextItem::CDTEXT_GENRE);
  183.       buildPacks(blockNr, CdTextItem::CDTEXT_TOC_INFO1);
  184.       buildPacks(blockNr, CdTextItem::CDTEXT_TOC_INFO2);
  185.       buildPacks(blockNr, CdTextItem::CDTEXT_UPCEAN_ISRC);
  186.     }
  187.   }
  188.   buildSizeInfoPacks();
  189. }
  190. // Build packs for specified language (blockNr) and pack type. First the
  191. // global pack is created and then the track specific packs.
  192. void CdTextEncoder::buildPacks(int blockNr, CdTextItem::PackType type)
  193. {
  194.   const CdTextItem *globalItem;
  195.   const CdTextItem *item;
  196.   int i;
  197.   int n = toc_->nofTracks();
  198.   int tracks;
  199.   if ((globalItem = toc_->getCdTextItem(0, blockNr, type)) != NULL) {
  200.     encodeCdTextItem(0, blockNr, globalItem);
  201.   }
  202.   if (CdTextItem::isTrackPack(type)) {
  203.     // count tracks that have the current pack type defined
  204.     tracks = 0;
  205.     for (i = 1; i <= n; i++) {
  206.       if (toc_->getCdTextItem(i, blockNr, type) != NULL)
  207. tracks++;
  208.     }
  209.     if (tracks > 0) {
  210.       if (globalItem == NULL && type == CdTextItem::CDTEXT_UPCEAN_ISRC) {
  211. // Special handling for UPC/EAN field. If this field is not defined
  212. // but ISRC codes are defined for the tracks an pack with an empty
  213. // string is created.
  214. CdTextItem upcEan(CdTextItem::CDTEXT_UPCEAN_ISRC, blockNr, "");
  215. encodeCdTextItem(0, blockNr, &upcEan);
  216.       }
  217.       for (i = 1; i <= n; i++) {
  218. if ((item = toc_->getCdTextItem(i, blockNr, type)) == NULL)
  219.   item = globalItem;
  220. if (item != NULL)
  221.   encodeCdTextItem(i, blockNr, item);
  222.       }
  223.     }
  224.   }
  225. }
  226. // Build CD-TEXT packs for a CdTextItem. Space from the last pack is
  227. // used if the pack type and language are matching.
  228. void CdTextEncoder::encodeCdTextItem(int trackNr, int blockNr,
  229.      const CdTextItem *item)
  230. {
  231.   long dataLen = item->dataLen();
  232.   const unsigned char *data = item->data();
  233.   long pos, n;
  234.   CdTextPackEntry *pack = NULL;
  235.   int first = 1;
  236.   
  237.   if (CdTextItem::isBinaryPack(item->packType())) {
  238.     pos = 0;
  239.     while (dataLen > 0) {
  240.       switch (item->packType()) {
  241.       case CdTextItem::CDTEXT_GENRE:
  242. pack = new CdTextPackEntry(item->packType(), 0, packId_++);
  243. if (pos > 0)
  244.   pack->characterPos((pos * 12) - 2);
  245. break;
  246.       case CdTextItem::CDTEXT_TOC_INFO1:
  247. if (pos == 0)
  248.   pack = new CdTextPackEntry(item->packType(), 0, packId_++);
  249. else
  250.   pack = new CdTextPackEntry(item->packType(), ((pos - 1) * 4) + 1,
  251.      packId_++);
  252. break;
  253.       default:
  254. pack = new CdTextPackEntry(item->packType(), pos, packId_++);
  255. break;
  256.       }
  257.       n = (dataLen > 12) ? 12 : dataLen;
  258.       memcpy(pack->pack.data, data, n);
  259.       pack->blockNr(blockNr);
  260.       appendPack(pack);
  261.       lastPackPos_ = 12;
  262.       
  263.       data += n;
  264.       dataLen -= n;
  265.       pos++;
  266.     }
  267.   }
  268.   else {
  269.     pos = 0;
  270.     while (dataLen > 0) {
  271.       if (first && lastPack_ != NULL &&
  272.   lastPack_->pack.packType == item->packType() &&
  273.   lastPack_->blockNr() == blockNr &&
  274.   lastPackPos_ < 12) {
  275. // we can use space from previous block
  276. pack = lastPack_;
  277.       }
  278.       else {
  279. pack = new CdTextPackEntry(item->packType(), trackNr, packId_++);
  280. pack->blockNr(blockNr);
  281. pack->characterPos(pos);
  282. appendPack(pack);
  283. lastPackPos_ = 0;
  284.       }
  285.       n = (dataLen > 12 - lastPackPos_) ? 12 - lastPackPos_ : dataLen;
  286.       memcpy(pack->pack.data + lastPackPos_, data, n);
  287.       data += n;
  288.       lastPackPos_ += n;
  289.       pos += n;
  290.       dataLen -= n;
  291.       first = 0;
  292.     }
  293.   }
  294. }
  295. // Create the SIZE_INFO CD-TEXT packs which contains a summary about all
  296. // CD-TEXT packs.
  297. void CdTextEncoder::buildSizeInfoPacks()
  298. {
  299.   CdTextItem *sizeInfoItem;
  300.   int i;
  301.   int b;
  302.   if (lastPack_ == NULL) {
  303.     // no packs at all -> nothing to do
  304.     return;
  305.   }
  306.   for (b = 0; b < 8; b++) {
  307.     sizeInfo_[b].characterCode = 1; // ASCII (7bit)
  308.     sizeInfo_[b].firstTrack = 1; // we always start with track 1
  309.     sizeInfo_[b].lastTrack = toc_->nofTracks();
  310.     sizeInfo_[b].copyright = 3; // to look like all other CD-TEXT CDs
  311.     for (i = 0; i < 8; i++) {
  312.       if (sizeInfo_[b].lastSequenceNumber[i] > 0) {
  313. // set language code if we have at least one pack for this language
  314. sizeInfo_[b].languageCode[i] = toc_->cdTextLanguage(i);
  315. // adjust the last sequence number for the packs we will create
  316. sizeInfo_[b].lastSequenceNumber[i] += 3;
  317.       }
  318.     }
  319.     // adjust the counter for the packs we're currently creating
  320.     sizeInfo_[b].packTypeCount[15] += 3; // we create 3 packs 
  321.   }
  322.   for (b = 0; b < 8; b++) {
  323.     if (sizeInfo_[b].lastSequenceNumber[b] > 0) {
  324.       // ' - 3' because we've already adjusted the pack count
  325.       packId_ = sizeInfo_[b].lastSequenceNumber[b] - 3 + 1;
  326.       sizeInfoItem = new CdTextItem(CdTextItem::CDTEXT_SIZE_INFO, b, 
  327.     (unsigned char*)&(sizeInfo_[b]),
  328.     sizeof(CdTextSizeInfo));
  329.       encodeCdTextItem(0, b, sizeInfoItem);
  330.       delete sizeInfoItem;
  331.     }
  332.   }
  333. }
  334. // Calculates checksum for each CD-TEXT pack.
  335. void CdTextEncoder::calcCrcs()
  336. {
  337.   CdTextPackEntry *prun;
  338.   register unsigned char data;
  339.   register unsigned short crc;
  340.   register int i;
  341.   for (prun = packs_; prun != NULL; prun = prun->next_) {
  342.     crc = 0;
  343.     for (i = 0; i < 16; i++) {
  344.       data = prun->packData[i];
  345.       crc = CRCTAB_[(crc >> 8) ^ data] ^ (crc << 8);
  346.     }
  347.     crc = ~crc;
  348.     
  349.     prun->pack.crc0 = crc >> 8;
  350.     prun->pack.crc1 = crc;
  351.   }
  352. }
  353. // Builds the R-W sub-channel data for all CD-TEXT packs. The sub-channel
  354. // data of each sector can hold 4 CD-TEXT packs.
  355. void CdTextEncoder::buildSubChannels()
  356. {
  357.   long i;
  358.   CdTextPackEntry *prun;
  359.   unsigned char buf[72];
  360.   subChannelCount_ = packCount_ / 4;
  361.   if (packCount_ % 4 != 0)
  362.     subChannelCount_ += 1;
  363.   if (subChannelCount_ == 0) {
  364.     subChannels_ = NULL;
  365.     return;
  366.   }
  367.   subChannels_ = new (PWSubChannel96*)[subChannelCount_];
  368.   
  369.   prun = packs_;
  370.   for (i = 0; i < subChannelCount_; i++) {
  371.     subChannels_[i] = new PWSubChannel96;
  372.     memset(buf, 0, 72);
  373.     memcpy(buf, prun->packData, 18);
  374.     if ((prun = prun->next_) != NULL) {
  375.       memcpy(buf + 18, prun->packData, 18);
  376.       if ((prun = prun->next_) != NULL) {
  377. memcpy(buf + 36, prun->packData, 18);
  378. if ((prun = prun->next_) != NULL) {
  379.   memcpy(buf + 54, prun->packData, 18);
  380.   prun = prun->next_;
  381. }
  382.       }
  383.     }
  384.     subChannels_[i]->setRawRWdata(buf);
  385. #if 0
  386.     int j, k;
  387.     for (j = 0; j < 6; j++) {
  388.       message(0, "%ld:%d: ", i, j);
  389.       for (k = 0; k < 16; k++) {
  390. message(0, "%02x ", subChannels_[i]->data()[j * 16 + k]);
  391.       }
  392.       message(0, "");
  393.     }
  394.     unsigned char buf1[72];
  395.     subChannels_[i]->getRawRWdata(buf1);
  396.     message(0, "Match: %d", memcmp(buf, buf1, 72));
  397. #endif
  398.   }
  399. }
  400. void CdTextEncoder::appendPack(CdTextPackEntry *pack)
  401. {
  402.   int b;
  403.   assert(pack->blockNr() >= 0 && pack->blockNr() <= 7);
  404.   pack->next_ = NULL;
  405.   if (packs_ == NULL) {
  406.     packs_ = lastPack_ = pack;
  407.   }
  408.   else {
  409.     if (pack->blockNr() >= lastPack_->blockNr()) {
  410.       lastPack_->next_ = pack;
  411.       lastPack_ = pack;
  412.     }
  413.     else {
  414.       CdTextPackEntry *prun, *ppred;
  415.       for (ppred = NULL, prun = packs_; prun != NULL;
  416.    ppred = prun, prun = prun->next_) {
  417. if (pack->blockNr() < prun->blockNr())
  418.   break;
  419.       }
  420.       
  421.       if (prun == NULL) {
  422. // this case should have been handled above
  423. lastPack_->next_ = pack;
  424. lastPack_ = pack;
  425.       }
  426.       else {
  427. if (ppred == NULL) {
  428.   pack->next_ = packs_;
  429.   packs_ = pack;
  430. }
  431. else {
  432.   ppred->next_ = pack;
  433.   pack->next_ = prun;
  434. }
  435.       }
  436.     }
  437.   }
  438.   
  439.   packCount_++;
  440.   // update summary data used for creating the SIZE_INFO pack
  441.   sizeInfo_[pack->blockNr()].packTypeCount[pack->pack.packType - 0x80] += 1;
  442.   for (b = 0; b < 8; b++) {
  443.     if (pack->pack.sequenceNumber >
  444. sizeInfo_[b].lastSequenceNumber[pack->blockNr()]) {
  445.       sizeInfo_[b].lastSequenceNumber[pack->blockNr()] =
  446. pack->pack.sequenceNumber;
  447.     }
  448.   }
  449. }