PQChannelEncoder.cc
上传用户:weiliju62
上传日期:2007-01-06
资源大小:619k
文件大小:12k
源码类别:
SCSI/ASPI
开发平台:
MultiPlatform
- /* cdrdao - write audio CD-Rs in disc-at-once mode
- *
- * Copyright (C) 1998 Andreas Mueller <mueller@daneb.ping.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- /*
- * $Log: PQChannelEncoder.cc,v $
- * Revision 1.3 1998/09/06 13:34:22 mueller
- * Use 'message()' for printing messages.
- *
- * Revision 1.2 1998/08/30 19:10:32 mueller
- * Added handling of Catalog Number and ISRC codes.
- *
- * Revision 1.1 1998/08/25 19:29:27 mueller
- * Initial revision
- *
- */
- static char rcsid[] = "$Id: PQChannelEncoder.cc,v 1.3 1998/09/06 13:34:22 mueller Exp $";
- #include <config.h>
- #include <stdio.h>
- #include <string.h>
- #include <assert.h>
- #include "PQChannelEncoder.h"
- #include "Msf.h"
- #include "Track.h"
- #include "util.h"
- PQChannelEncoder::PQChannelEncoder()
- {
- cueSheet_ = NULL;
- cueSheetLen_ = 0;
- toc_ = NULL;
- tocLen_ = 0;
- discType_ = 0;
- current_ = NULL;
- catalog_ = NULL;
- isrc_ = NULL;
- }
- PQChannelEncoder::~PQChannelEncoder()
- {
- int i;
- for (i = 0; i < tocLen_; i++) {
- delete toc_[i];
- }
- delete[] toc_;
- delete[] cueSheet_;
- delete current_;
- delete catalog_;
- delete isrc_;
- }
- int PQChannelEncoder::setCueSheet(SubChannel *chan, unsigned char discType,
- const unsigned char *sheet, int len,
- const Msf &leadInStart)
- {
- int tocEnt, i;
- subChannel_ = chan;
- if (!(discType == 0 || discType == 0x10 || discType == 0x20)) {
- message(-3, "Illegal disc type.");
- return 1;
- }
- if ((len % sizeof(CueSheetEntry)) != 0) {
- message(-3, "Illegal cue sheet length.");
- return 1;
- }
- discType_ = discType;
- cueSheetLen_ = len / sizeof(CueSheetEntry);
- cueSheet_ = new CueSheetEntry[cueSheetLen_ + 1];
- memcpy(cueSheet_, sheet, len);
- // mark end of queue sheet with a zeroed entry
- memset(cueSheet_ + cueSheetLen_, 0, sizeof(CueSheetEntry));
- // create some sub channel objects
- catalog_ = subChannel_->makeSubChannel(SubChannel::QMODE2);
- isrc_ = subChannel_->makeSubChannel(SubChannel::QMODE3);
- current_ = subChannel_->makeSubChannel(SubChannel::QMODE1DATA);
- if (analyzeCueSheet() != 0) {
- return 1;
- }
- // create PQ sub channels for toc in lead-in
- tocLen_ = lastTrackNr_ - firstTrackNr_ + 1 + 3/*A0, A1, A2*/;
- toc_ = new (SubChannel*)[tocLen_];
- for (i = 0; i < tocLen_; i++) {
- toc_[i] = subChannel_->makeSubChannel(SubChannel::QMODE1TOC);
- }
- CueSheetEntry *run;
- for (run = actCueSheetEntry_, tocEnt = 0;
- (run->ctlAdr & 0x0f) != 1 || run->trackNr != 0xaa; run++) {
- if ((run->ctlAdr & 0x0f) == 1 && run->trackNr > 0 && run->trackNr <= 99 &&
- run->indexNr == 1) {
- toc_[tocEnt]->ctl(run->ctlAdr & 0xf0);
- toc_[tocEnt]->point(run->trackNr);
- toc_[tocEnt]->pmin(run->min);
- toc_[tocEnt]->psec(run->sec);
- toc_[tocEnt]->pframe(run->frame);
- tocEnt++;
- }
- }
- toc_[tocEnt]->point(0xa0);
- toc_[tocEnt]->pmin(firstTrackNr_);
- toc_[tocEnt]->psec(discType_);
- tocEnt++;
- toc_[tocEnt]->point(0xa1);
- toc_[tocEnt]->pmin(lastTrackNr_);
- tocEnt++;
- toc_[tocEnt]->point(0xa2);
- toc_[tocEnt]->pmin(leadOutStart_.min());
- toc_[tocEnt]->psec(leadOutStart_.sec());
- toc_[tocEnt]->pframe(leadOutStart_.frac());
- tocEnt++;
- assert(tocEnt == tocLen_);
- // setup encoder dynamic data
- abslba_ = leadInStart.lba() - 450150;
- trlba_ = 0;
- writeIsrc_ = 0;
- deferredCatalog_ = 0;
- deferredIsrc_ = 0;
- actTocEnt_ = actTocCount_ = 0;
- run = nextCueSheetEntry(actCueSheetEntry_, 1);
- assert(run != NULL);
- nextTransitionLba_ = Msf(run->min, run->sec, run->frame).lba() - 150;
- run = nextCueSheetEntry(actCueSheetEntry_, firstTrackNr_, 1);
- assert(run != NULL);
- nextTrackStartLba_ = Msf(run->min, run->sec, run->frame).lba() - 150;
- return 0;
- }
- int PQChannelEncoder::analyzeCueSheet()
- {
- int i;
- CueSheetEntry *ent;
- int prevTrackNr = -1;
- long prevLba = -1;
- long lba;
- firstTrackNr_ = 0;
- lastTrackNr_ = 0;
- leadOutStart_ = 0;
- actCueSheetEntry_ = NULL;
- writeCatalog_ = 0;
- for (ent = cueSheet_, i = 0; i < cueSheetLen_; ent++, i++) {
- switch (ent->ctlAdr & 0x0f) {
- case 1:
- if (ent->min > 89 || ent->sec > 59 || ent->frame > 74) {
- message(-3, "Illegal time field value at cue sheet entry: %d",
- i);
- return 1;
- }
- if (ent->trackNr == 0) { // indicates lead-in
- if (i == 0 || (writeCatalog_ && i == 2)) { // must be first entry
- actCueSheetEntry_ = ent;
- }
- else {
- message(-3, "Illegal track number at cue sheet entry: %d", i);
- return 1;
- }
- }
- else if (ent->trackNr == 0xaa) { // indicates lead-out
- if (i == cueSheetLen_ - 1) { // must be last entry
- leadOutStart_ = Msf(ent->min, ent->sec, ent->frame);
- }
- else {
- message(-3, "Illegal track number at cue sheet entry: %d", i);
- return 1;
- }
- }
- else if (ent->trackNr <= 99) { // data track
- if (firstTrackNr_ == 0) {
- firstTrackNr_ = ent->trackNr;
- prevTrackNr = ent->trackNr;
- }
- else {
- if (ent->trackNr != prevTrackNr && ent->trackNr != prevTrackNr + 1) {
- message(-3,
- "Wrong track number sequence at cue sheet entry: %d", i);
- return 1;
- }
- prevTrackNr = ent->trackNr;
- }
- lastTrackNr_ = ent->trackNr;
- }
- else {
- message(-3, "Illegal track number at cue sheet entry: %d", i);
- return 1;
- }
- if (ent->trackNr != 0) {
- lba = Msf(ent->min, ent->sec, ent->frame).lba();
- if (lba <= prevLba) {
- message(-3,
- "Time field does not increase at cue sheet entry: %d", i);
- return 1;
- }
- prevLba = lba;
- }
- break;
- case 2:
- if (i != 0) {
- message(-3, "Catalog number must be first cue sheet entry.");
- return 1;
- }
- if ((cueSheet_[1].ctlAdr & 0x0f) != 2) {
- message(-3, "Missing second catalog number entry.");
- return 1;
- }
- writeCatalog_ = 1;
- catalog_->catalog(ent->trackNr, ent->indexNr, ent->dataForm, ent->scms,
- ent->min, ent->sec, ent->frame, cueSheet_[1].trackNr,
- cueSheet_[1].indexNr, cueSheet_[1].dataForm,
- cueSheet_[1].scms, cueSheet_[1].min, cueSheet_[1].sec);
- // skip next entry
- ent++;
- i++;
- break;
- case 3:
- if (((ent + 1)->ctlAdr & 0x0f) != 3) {
- message(-3, "Missing second ISRC code entry.");
- return 1;
- }
- // skip next entry
- ent++;
- i++;
- break;
- default:
- message(-3, "Illegal adr field at cue sheet entry: %d.", i);
- return 1;
- break;
- }
- }
- if (actCueSheetEntry_ == NULL) {
- message(-3, "Cue sheet contains no lead-in entry.");
- return 1;
- }
- if (leadOutStart_.lba() == 0) {
- message(-3, "Cue sheet contains no lead-out entry.");
- return 1;
- }
- if (firstTrackNr_ == 0) {
- message(-3, "Cue sheet contains no data track.");
- return 1;
- }
- return 0;
- }
- void PQChannelEncoder::encode(long lba, unsigned char *in, long blocks,
- unsigned char *out)
- {
- long clen = subChannel_->dataLength();
- long outBlockSize = AUDIO_BLOCK_LEN + clen;
- long inBlockSize = AUDIO_BLOCK_LEN;
- const SubChannel *chan;
- long i;
- for (i = 0; i < blocks; i++, lba++) {
- chan = encodeSubChannel(lba);
- clen = chan->dataLength();
- memcpy(out + i * outBlockSize, in + i * inBlockSize, inBlockSize);
- memcpy(out + i * outBlockSize + inBlockSize, chan->data(), clen);
- }
- }
- const SubChannel *PQChannelEncoder::encodeSubChannel(long lba)
- {
- int newTransition = 0;
- SubChannel *chan = NULL;
- // check consistency of internal lba with external lba
- assert(lba == abslba_);
- if (lba == nextTransitionLba_) {
- // switch to next transition
- //message(2, "Switching to next transition at lba: %ld", lba);
- nextTransition();
- newTransition = 1;
- }
- if (actCueSheetEntry_->trackNr == 0) {
- // lead-in sector
- chan = toc_[actTocEnt_];
- Msf m(trlba_);
- chan->min(m.min());
- chan->sec(m.sec());
- chan->frame(m.frac());
- // advance to next to entry
- if (++actTocCount_ == 3) {
- actTocCount_ = 0;
- if (++actTocEnt_ == tocLen_) {
- actTocEnt_ = 0;
- }
- }
- }
- else {
- // data or lead-out sector
- // Q channel setup
- // catalog number
- if (writeCatalog_ &&
- (deferredCatalog_ || (abslba_ % 90) == 0)) {
- if (newTransition) {
- deferredCatalog_ = 1;
- }
- else {
- deferredCatalog_ = 0;
- chan = catalog_;
- chan->aframe(Msf(abslba_ + 150).frac());
- }
- }
- // ISRC code
- if (writeIsrc_ &&
- (deferredIsrc_ || (abslba_ % 90) == 50 || (abslba_ % 90) == -40)) {
- if (newTransition) {
- deferredIsrc_ = 1;
- }
- else {
- deferredIsrc_ = 0;
- chan = isrc_;
- chan->aframe(Msf(abslba_ + 150).frac());
- }
- }
- if (chan == NULL) {
- // Current position
- chan = current_;
- Msf m(trlba_ < 0 ? -trlba_ : trlba_);
- chan->min(m.min());
- chan->sec(m.sec());
- chan->frame(m.frac());
- m = Msf(abslba_ + 150);
- chan->amin(m.min());
- chan->asec(m.sec());
- chan->aframe(m.frac());
- }
- // P channel setup
- if (trlba_ <= 0 ||
- (abslba_ >= nextTrackStartLba_ - 150 &&
- abslba_ <= nextTrackStartLba_)) {
- // set P channel flag in pre-gap and 2 seconds before next track starts
- chan->pChannel(1);
- }
- else if (abslba_ >= leadOutStart_.lba() ) {
- // P channel flag is 0 2 secs from start of lead-out, after that
- // flag alternates at 2 Hz.
- chan->pChannel(((trlba_ - 150) % 38) < 19 ? 1 : 0);
- }
- else {
- chan->pChannel(0);
- }
- }
- abslba_++;
- trlba_++;
- assert(chan != NULL);
- chan->calcCrc();
- return chan;
- }
- void PQChannelEncoder::nextTransition()
- {
- CueSheetEntry *nextEnt, *ent , *ent1;
- nextEnt = nextCueSheetEntry(actCueSheetEntry_, 1);
- assert(nextEnt != NULL);
- if (nextEnt->trackNr != actCueSheetEntry_->trackNr) {
- // new track started
- // check for ISRC code cue sheet entry
- if ((ent = nextCueSheetEntry(actCueSheetEntry_, 3)) != NULL &&
- ent->trackNr == nextEnt->trackNr) {
- ent1 = ent + 1;
- isrc_->isrc(ent->indexNr, ent->dataForm, ent->scms, ent->min, ent->sec,
- ent->frame, ent1->indexNr, ent1->dataForm, ent1->scms,
- ent1->min, ent1->sec, ent1->frame);
- writeIsrc_ = 1;
- }
- else {
- writeIsrc_ = 0;
- }
- deferredIsrc_ = 0;
- // setup 'trlba'
- if (nextEnt->indexNr == 0) {
- // track has a pre-gap -> determine length
- ent = nextCueSheetEntry(nextEnt, nextEnt->trackNr, 1);
- assert(ent != NULL);
- trlba_ = Msf(nextEnt->min, nextEnt->sec, nextEnt->frame).lba() -
- Msf(ent->min, ent->sec, ent->frame).lba();
- }
- else {
- trlba_ = 0;
- }
- }
- actCueSheetEntry_ = nextEnt;
- current_->ctl(actCueSheetEntry_->ctlAdr & 0xf0);
- current_->trackNr(actCueSheetEntry_->trackNr);
- current_->indexNr(actCueSheetEntry_->indexNr);
- if (writeCatalog_)
- catalog_->ctl(actCueSheetEntry_->ctlAdr & 0xf0);
- if (writeIsrc_)
- isrc_->ctl(actCueSheetEntry_->ctlAdr & 0xf0);
- if (actCueSheetEntry_->trackNr != 0xaa) {
- // find next transition point
- ent = nextCueSheetEntry(actCueSheetEntry_, 1);
- assert(ent != NULL);
- nextTransitionLba_ = Msf(ent->min, ent->sec, ent->frame).lba() - 150;
- if (actCueSheetEntry_->indexNr == 1) {
- // find next track start lba
- if (actCueSheetEntry_->trackNr == lastTrackNr_)
- ent = nextCueSheetEntry(actCueSheetEntry_, 0xaa, 1);
- else
- ent = nextCueSheetEntry(actCueSheetEntry_,
- actCueSheetEntry_->trackNr + 1, 1);
- assert(ent != NULL);
- nextTrackStartLba_ = Msf(ent->min, ent->sec, ent->frame).lba() - 150;
- }
- }
- }
- CueSheetEntry *PQChannelEncoder::nextCueSheetEntry(CueSheetEntry *act,
- int adr)
- {
- if (act->trackNr == 0xaa) {
- return NULL;
- }
- act++;
- while (act->ctlAdr != 0) {
- if ((act->ctlAdr & 0x0f) == adr) {
- return act;
- }
- act++;
- }
- return NULL;
- }
- CueSheetEntry *PQChannelEncoder::nextCueSheetEntry(CueSheetEntry *act,
- int trackNr, int indexNr)
- {
- if (act->trackNr == 0xaa) {
- return NULL;
- }
- act++;
- while (act->ctlAdr != 0) {
- if ((act->ctlAdr & 0x0f) == 1 && act->trackNr == trackNr &&
- act->indexNr == indexNr) {
- return act;
- }
- act++;
- }
- return NULL;
- }