/* cdrdao - write audio CD-Rs in disc-at-once mode * * Copyright (C) 1998-2001 Andreas Mueller * * 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. */ #include #include #include #include #include #include #include #include #include #include "TrackData.h" #include "Msf.h" #include "util.h" #ifdef UNIXWARE extern "C" { extern int strcasecmp(const char *, const char *); } #endif // creates an object representing a portion of an audio data file TrackData::TrackData(const char *filename, long offset, unsigned long start, unsigned long length) { init(filename, offset, start, length); } TrackData::TrackData(const char *filename, unsigned long start, unsigned long length) { init(filename, 0, start, length); } void TrackData::init(const char *filename, long offset, unsigned long start, unsigned long length) { assert(offset >= 0); length_ = length; if (strcmp(filename, "-") == 0) { type_ = STDIN; fileType_ = RAW; // currently only raw data filename_ = strdupCC("STDIN"); } else { type_ = DATAFILE; filename_ = strdupCC(filename); fileType_ = audioFileType(filename_); } effFilename_ = NULL; offset_ = offset; startPos_ = start; swapSamples_ = 0; mode_ = AUDIO; subChannelMode_ = SUBCHAN_NONE; audioCutMode_ = 1; } // creates an AUDIO mode object that contains constant zero data TrackData::TrackData(unsigned long length) { type_ = ZERODATA; mode_ = AUDIO; subChannelMode_ = SUBCHAN_NONE; audioCutMode_ = 1; filename_ = NULL; effFilename_ = NULL; fileType_ = RAW; startPos_ = 0; offset_ = 0; length_ = length; swapSamples_ = 0; } // creates an object that contains constant data with specified mode TrackData::TrackData(Mode m, SubChannelMode sm, unsigned long length) { type_ = ZERODATA; mode_ = m; subChannelMode_ = sm; audioCutMode_ = 0; filename_ = NULL; effFilename_ = NULL; fileType_ = RAW; startPos_ = 0; offset_ = 0; length_ = length; swapSamples_ = 0; } // creates a file object with given mode TrackData::TrackData(Mode m, SubChannelMode sm, const char *filename, long offset, unsigned long length) { init(m, sm, filename, offset, length); } void TrackData::init(Mode m, SubChannelMode sm, const char *filename, long offset, unsigned long length) { assert(offset >= 0); mode_ = m; subChannelMode_ = sm; audioCutMode_ = 0; if (strcmp(filename, "-") == 0) { type_ = STDIN; fileType_ = RAW; // currently only raw data filename_ = strdupCC("STDIN"); } else { type_ = DATAFILE; filename_ = strdupCC(filename); if (mode_ == AUDIO) fileType_ = audioFileType(filename_); else fileType_ = RAW; // data files are always raw } offset_ = offset; length_ = length; effFilename_ = NULL; startPos_ = 0; swapSamples_ = 0; } TrackData::TrackData(Mode m, SubChannelMode sm, const char *filename, unsigned long length) { assert(filename != NULL && *filename != 0); mode_ = m; subChannelMode_ = sm; audioCutMode_ = 0; type_ = FIFO; fileType_ = RAW; filename_ = strdupCC(filename); effFilename_ = NULL; offset_ = 0; length_ = length; startPos_ = 0; swapSamples_ = 0; } // copy constructor TrackData::TrackData(const TrackData &obj) { type_ = obj.type_; mode_ = obj.mode_; subChannelMode_ = obj.subChannelMode_; audioCutMode_ = obj.audioCutMode_; offset_ = obj.offset_; switch (type_) { case DATAFILE: case STDIN: case FIFO: filename_ = strdupCC(obj.filename_); effFilename_ = (obj.effFilename_ ? strdupCC(obj.effFilename_) : NULL); startPos_ = obj.startPos_; fileType_ = obj.fileType_; break; case ZERODATA: filename_ = NULL; effFilename_ = NULL; startPos_ = 0; fileType_ = RAW; break; } length_ = obj.length_; swapSamples_ = obj.swapSamples_; } TrackData::~TrackData() { if (filename_) { delete[] filename_; } if (effFilename_) delete[] effFilename_; } unsigned long TrackData::length() const { return length_; } // Determines length of data by inspecting the data file. The available data // from the specified start point up to the end of file is stored in 'length_'. // Return: 0: OK // 1: cannot open or access the file (see 'errno') // 2: start pos or offset exceeds length of file // 3: track needs conversion to WAV // 4: format is not supported int TrackData::determineLength() { unsigned long len; if (type_ == DATAFILE) { if (mode_ == AUDIO) { switch (audioDataLength(filename_, offset_, &len)) { case 1: message(-2, "Cannot open audio file \"%s\": %s", filename_, strerror(errno)); return 1; break; case 2: message(-2, "Cannot determine length of audio file \"%s\": %s", filename_, strerror(errno)); return 1; break; case 3: message(-2, "Header of audio file \"%s\" is corrupted.", filename_); return 1; break; case 4: message(-2, "Invalid offset %ld for audio file \"%s\".", offset_, filename_); return 2; break; case 5: #ifndef HAVE_MP3_SUPPORT if (audioFileType(filename_) == MP3) { message (-2, "Can't read file \"%s\": cdrdao was compiled " "without MP3 support.", filename_); return 4; } #endif #ifndef HAVE_OGG_SUPPORT if (audioFileType(filename_) == OGG) { message (-2, "Can't read file \"%s\": cdrdao was compiled " "without Ogg/Vorbis support.", filename_); return 4; } #endif return 3; } if (audioCutMode()) { if (startPos_ < len) { length_ = len - startPos_; } else { message(-2, "Start position %lu exceeds available data of file \"%s\".", startPos_, filename_); return 2; } } else { length_ = len * sizeof(Sample); } } else { switch (dataFileLength(filename_, offset_, &len)) { case 1: message(-2, "Cannot open data file \"%s\": %s", filename_, strerror(errno)); return 1; break; case 2: message(-2, "Invalid offset %ld for audio file \"%s\".", offset_, filename_); return 2; break; } length_ = len; } } return 0; } // checks the consistency of object // return: 0: OK // 1: warning occured // 2: error occured int TrackData::check(int trackNr) const { switch (type_) { case ZERODATA: // always OK break; case STDIN: // cannot do much here... break; case FIFO: if (access(filename_, R_OK) != 0) { message(-2, "Track %d: Cannot access FIFO \"%s\": %s", trackNr, filename_, strerror(errno)); return 2; } break; case DATAFILE: if (mode_ == AUDIO) { unsigned long len = 0; if (fileType_ == WAVE && subChannelMode_ != SUBCHAN_NONE) { message(-2, "Track %d: WAVE audio files cannot contain sub-channel " "data.", trackNr); return 2; } switch (audioDataLength(filename_, offset_, &len)) { case 1: message(-2, "Track %d: Cannot open audio file \"%s\": %s", trackNr, filename_, strerror(errno)); return 2; break; case 2: message(-2, "Track %d: Cannot access audio file \"%s\": %s", trackNr, filename_, strerror(errno)); return 2; break; case 3: message(-2, "Track %d: %s: Unacceptable WAVE file.", trackNr, filename_); return 2; break; case 4: message(-2, "Track %d: Invalid offset %ld for audio file \"%s\".", trackNr, offset_, filename_); return 2; break; } if (length() == 0) { message(-2, "Track %d: Requested length for audio file \"%s\" is 0.", trackNr, filename_); return 2; } if (audioCutMode()) { if (startPos_ + length() > len) { // requested part exceeds file size message(-2, "Track %d: Requested length (%lu + %lu samples) exceeds " "length of audio file \"%s\" (%lu samples at offset %ld).", trackNr, startPos_, length(), filename_, len, offset_); return 2; } } else { if (length() > len * sizeof(Sample)) { message(-2, "Track %d: Requested length (%lu bytes) exceeds length of file \"%s\" (%lu bytes at offset %ld).", trackNr, length(), filename_, len, offset_); return 2; } } } else { // data mode unsigned long len; switch (dataFileLength(filename_, offset_, &len) != 0) { case 1: message(-2, "Track %d: Cannot open data file \"%s\": %s", trackNr, filename_, strerror(errno)); return 2; break; case 2: message(-2, "Track %d: Invalid offset %ld for data file \"%s\".", trackNr, offset_, filename_); return 2; break; } if (length() == 0) { message(-2, "Track %d: Requested length for data file \"%s\" is 0.", trackNr, filename_); return 2; } if (length() > len) { message(-2, "Track %d: Requested length (%lu bytes) exceeds length of file \"%s\" (%lu bytes at offset %ld).", trackNr, length(), filename_, len, offset_); return 2; } } break; } return 0; } void TrackData::effectiveFilename(const char* name) { if (effFilename_) delete effFilename_; effFilename_ = filename_; filename_ = strdupCC(name); fileType_ = audioFileType(filename_); } // writes out contents of object in TOC file syntax void TrackData::print(std::ostream &out, bool conversions) const { unsigned long blen; const char *s; if (audioCutMode()) { // we're calculating in samples and not in bytes for audio data blen = SAMPLES_PER_BLOCK; } else { blen = dataBlockSize(mode(), subChannelMode()); } switch (type()) { case STDIN: case DATAFILE: if (audioCutMode()) { if (type() == STDIN) out << "FILE \"-\" "; else if (effFilename_ && !conversions) out << "FILE \"" << effFilename_ << "\" "; else out << "FILE \"" << filename_ << "\" "; if (swapSamples_) out << "SWAP "; if (offset_ > 0) out << "#" << offset_ << " "; if (startPos() != 0 && (startPos() % blen) == 0) out << Msf(startPos() / blen).str(); else out << startPos(); out << " "; } else { // data mode if (type() == STDIN) out << "DATAFILE \"-\" "; else if (effFilename_ && !conversions) out << "DATAFILE \"" << effFilename_ << "\" "; else out << "DATAFILE \"" << filename_ << "\" "; //out << mode2String(mode()) << " "; if (offset_ > 0) out << "#" << offset_ << " "; } if ((length() % blen) == 0) out << Msf(length() / blen).str(); else out << length(); if (!audioCutMode()) out << " // length in bytes: " << length(); out << std::endl; break; case FIFO: out << "FIFO \"" << filename_ << "\" "; if ((length() % blen) == 0) { out << Msf(length() / blen).str(); out << " // length in bytes: " << length(); } else { out << length(); } out << std::endl; break; case ZERODATA: if (audioCutMode()) { out << "SILENCE "; } else { out << "ZERO " << mode2String(mode()) << " "; } s = subChannelMode2String(subChannelMode()); if (*s != 0) out << s << " "; if ((length() % blen) == 0) out << Msf(length() / blen).str(); else out << length(); out << std::endl; break; } } void TrackData::split(unsigned long pos, TrackData **part1, TrackData **part2) { assert(mode_ == AUDIO); assert(pos > 0 && pos < length_); *part1 = new TrackData(*this); *part2 = new TrackData(*this); (*part1)->length_ = pos; (*part2)->length_ = length_ - pos; if (type_ == DATAFILE) (*part2)->startPos_ = startPos_ + pos; } TrackData *TrackData::merge(const TrackData *obj) const { if (mode_ != AUDIO || type_ != obj->type_ || mode_ != obj->mode_ || subChannelMode_ != obj->subChannelMode_ || offset_ != obj->offset_ || audioCutMode_ != obj->audioCutMode_) return NULL; TrackData *data = NULL; switch (type_) { case ZERODATA: data = new TrackData(*this); data->length_ += obj->length_; break; case DATAFILE: if (strcmp(filename_, obj->filename_) == 0 && startPos_ + length_ == obj->startPos_) { data = new TrackData(*this); data->length_ += obj->length_; } break; case STDIN: case FIFO: // can't merge this type at all break; } return data; } // Checks if given audio file is suitable for cdrdao. 'length' is filled // with number of samples in audio file on success. // return: 0: file is suitable // 1: cannot open or access file // 2: file has wrong format int TrackData::checkAudioFile(const char *fn, unsigned long *length) { int fd; int ret; struct stat buf; long headerLength = 0; enum FileType ft = audioFileType(fn); if (ft != WAVE && ft != RAW) { message(-2, "Checking audio file \"%s\": format not supported", fn); return 1; } if ((fd = open(fn, O_RDONLY)) < 0) return 1; ret = fstat(fd, &buf); close(fd); if (ret != 0) return 1; if (ft == WAVE) { if (waveLength(fn, 0, &headerLength, length) != 0) return 2; } else { if (buf.st_size % sizeof(Sample) != 0) { message(-1, "%s: Length is not a multiple of sample size (4).", fn); } *length = buf.st_size / sizeof(Sample); } return 0; } // Determines length of header and audio data for WAVE files. 'hdrlen' is // filled with length of WAVE header in bytes. 'datalen' is filled with // length of audio data in samples (if != NULL). // return: 0: OK // 1: error occured // 2: illegal WAVE file int TrackData::waveLength(const char *filename, long offset, long *hdrlen, unsigned long *datalen) { FILE *fp; char magic[4]; long headerLen = 0; long len; short waveFormat; short waveChannels; long waveRate; short waveBits; struct stat sbuf; #ifdef __CYGWIN__ if ((fp = fopen(filename, "rb")) == NULL) #else if ((fp = fopen(filename, "r")) == NULL) #endif { message(-2, "Cannot open audio file \"%s\" for reading: %s", filename, strerror(errno)); return 1; } if (offset != 0) { if (fseek(fp, offset, SEEK_SET) != 0) { message(-2, "Cannot seek to offset %ld in file \"%s\": %s", offset, filename, strerror(errno)); return 1; } } if (fread(magic, sizeof(char), 4, fp) != 4 || strncmp("RIFF", magic, 4) != 0) { message(-2, "%s: not a WAVE file.", filename); fclose(fp); return 2; } readLong(fp); if (fread(magic, sizeof(char), 4, fp) != 4 || strncmp("WAVE", magic, 4) != 0) { message(-2, "%s: not a WAVE file.", filename); fclose(fp); return 2; } // search for format chunk for (;;) { if (fread(magic, sizeof(char), 4, fp) != 4) { message(-2, "%s: corrupted WAVE file.", filename); fclose(fp); return 1; } len = readLong(fp); len += len & 1; // round to multiple of 2 if (strncmp("fmt ", magic, 4) == 0) { // format chunk found break; } // skip chunk data if (fseek(fp, len, SEEK_CUR) != 0) { message(-2, "%s: corrupted WAVE file.", filename); fclose(fp); return 1; } } if (len < 16) { message(-2, "%s: corrupted WAVE file.", filename); fclose(fp); return 1; } waveFormat = readShort(fp); if (waveFormat != 1) { // not PCM format message(-2, "%s: not in PCM format.", filename); fclose(fp); return 2; } waveChannels = readShort(fp); if (waveChannels != 2) { message(-2, "%s: found %d channel(s), require 2 channels.", filename, waveChannels); fclose(fp); return 2; } waveRate = readLong(fp); if (waveRate != 44100) { message(-2, "%s: found sampling rate %ld, require 44100.", filename, waveRate); fclose(fp); return 2; } readLong(fp); // average bytes/second readShort(fp); // block align waveBits = readShort(fp); if (waveBits != 16) { message(-2, "%s: found %d bits per sample, require 16.", filename, waveBits); fclose(fp); return 2; } len -= 16; // skip chunk data if (fseek(fp, len, SEEK_CUR) != 0) { message(-2, "%s: corrupted WAVE file.", filename); fclose(fp); return 1; } // search wave data chunk for (;;) { if (fread(magic, sizeof(char), 4, fp) != 4) { message(-2, "%s: corrupted WAVE file.", filename); fclose(fp); return 1; } len = readLong(fp); if (strncmp("data", magic, 4) == 0) { // found data chunk break; } len += len & 1; // round to multiple of 2 // skip chunk data if (fseek(fp, len, SEEK_CUR) != 0) { message(-2, "%s: corrupted WAVE file.", filename); fclose(fp); return 1; } } if ((headerLen = ftell(fp)) < 0) { message(-2, "%s: cannot determine file position: %s", filename, strerror(errno)); fclose(fp); return 1; } headerLen -= offset; if (fstat(fileno(fp), &sbuf) != 0) { message(-2, "Cannot fstat audio file \"%s\": %s", filename, strerror(errno)); fclose(fp); return 1; } fclose(fp); if (len + headerLen + offset > sbuf.st_size) { message(-1, "%s: file length does not match length from WAVE header - using actual length.", filename); len = sbuf.st_size - offset - headerLen; } if (len % sizeof(Sample) != 0) { message(-1, "%s: length of data chunk is not a multiple of sample size (4).", filename); } *hdrlen = headerLen; if (datalen != NULL) { *datalen = len / sizeof(Sample); } return 0; } // Returns length in samples for given audio file. // return: 1: file cannot be opened // 2: 'fstat' failed // 3: file header corruption // 4: invalid offset // 5: file need conversion // 0: OK int TrackData::audioDataLength(const char *fname, long offset, unsigned long *length) { int fd; struct stat buf; long headerLength = 0; int ret; *length = 0; if ((fd = open(fname, O_RDONLY)) < 0) return 1; ret = fstat(fd, &buf); close(fd); if (ret != 0) return 2; if (offset > buf.st_size) return 4; FileType ftype = audioFileType(fname); if (ftype == WAVE) { if (waveLength(fname, offset, &headerLength, length) != 0) return 3; } else if (ftype == MP3 || ftype == OGG) { return 5; } else { if (((buf.st_size - offset) % sizeof(Sample)) != 0) { message(-1, "Length of file \"%s\" is not a multiple of sample size (4).", fname); } *length = (buf.st_size - offset) / sizeof(Sample); } return 0; } // Sets 'length' to length of given data file. // return: 0: OK // 1: file cannot be opened or accessed // 2: invalid offset int TrackData::dataFileLength(const char *fname, long offset, unsigned long *length) { int fd; struct stat buf; int ret; *length = 0; if ((fd = open(fname, O_RDONLY)) < 0) return 1; ret = fstat(fd, &buf); close(fd); if (ret != 0) return 1; if (offset > buf.st_size) return 2; *length = buf.st_size - offset; return 0; } // determines type of audio file // return: RAW: raw samples // WAVE: wave file TrackData::FileType TrackData::audioFileType(const char *filename) { FileExtension p = fileExtension(filename); if (p == FE_WAV) return WAVE; if (p == FE_MP3) return MP3; if (p == FE_OGG) return OGG; return RAW; } unsigned long TrackData::subChannelSize(SubChannelMode sm) { unsigned long b = 0; switch (sm) { case SUBCHAN_NONE: b = 0; break; case SUBCHAN_RW: case SUBCHAN_RW_RAW: b = PW_SUBCHANNEL_LEN; break; } return b; } unsigned long TrackData::dataBlockSize(Mode m, SubChannelMode sm) { unsigned long b = 0; switch (m) { case AUDIO: case MODE1_RAW: case MODE2_RAW: b = AUDIO_BLOCK_LEN; break; case MODE0: b = MODE0_BLOCK_LEN; break; case MODE1: b = MODE1_BLOCK_LEN; break; case MODE2: case MODE2_FORM_MIX: b = MODE2_BLOCK_LEN; break; case MODE2_FORM1: b = MODE2_FORM1_DATA_LEN; break; case MODE2_FORM2: b = MODE2_FORM2_DATA_LEN; break; } b += subChannelSize(sm); return b; } const char *TrackData::mode2String(Mode m) { const char *ret = NULL; switch (m) { case AUDIO: ret = "AUDIO"; break; case MODE0: ret = "MODE0"; break; case MODE1: ret = "MODE1"; break; case MODE1_RAW: ret = "MODE1_RAW"; break; case MODE2: ret = "MODE2"; break; case MODE2_RAW: ret = "MODE2_RAW"; break; case MODE2_FORM1: ret = "MODE2_FORM1"; break; case MODE2_FORM2: ret = "MODE2_FORM2"; break; case MODE2_FORM_MIX: ret = "MODE2_FORM_MIX"; break; } return ret; } const char *TrackData::subChannelMode2String(SubChannelMode m) { const char *ret = NULL; switch (m) { case SUBCHAN_NONE: ret = ""; break; case SUBCHAN_RW: ret = "RW"; break; case SUBCHAN_RW_RAW: ret = "RW_RAW"; break; } return ret; } TrackDataReader::TrackDataReader(const TrackData *d) { trackData_ = d; open_ = 0; fd_ = -1; readPos_ = 0; headerLength_ = 0; readUnderRunMsgGiven_ = 0; } TrackDataReader::~TrackDataReader() { if (open_) { closeData(); } trackData_ = NULL; } void TrackDataReader::init(const TrackData *d) { if (open_) { closeData(); } trackData_ = d; } // initiates reading audio data, an audio data file is opened // return: 0: OK // 1: file could not be opened // 2: could not seek to start position int TrackDataReader::openData() { assert(open_ == 0); assert(trackData_ != NULL); if (trackData_->type_ == TrackData::DATAFILE) { if (trackData_->mode_ == TrackData::AUDIO) { long headerLength = 0; if (trackData_->fileType_ != TrackData::WAVE && trackData_->fileType_ != TrackData::RAW) { message(-2, "Cannot open audio file \"%s\": unsupported format", trackData_->filename_); return 1; } #ifdef __CYGWIN__ if ((fd_ = open(trackData_->filename_, O_RDONLY | O_BINARY)) < 0) #else if ((fd_ = open(trackData_->filename_, O_RDONLY)) < 0) #endif { message(-2, "Cannot open audio file \"%s\": %s", trackData_->filename_, strerror(errno)); return 1; } if (trackData_->fileType_ == TrackData::WAVE) { if (TrackData::waveLength(trackData_->filename_, trackData_->offset_, &headerLength) != 0) { message(-2, "%s: Unacceptable WAVE file.", trackData_->filename_); return 1; } } if (lseek(fd_, trackData_->offset_ + headerLength + (trackData_->startPos_ * sizeof(Sample)), SEEK_SET) < 0) { message(-2, "Cannot seek in audio file \"%s\": %s", trackData_->filename_, strerror(errno)); return 2; } headerLength_ = headerLength; } else { // data mode headerLength_ = 0; #ifdef __CYGWIN__ if ((fd_ = open(trackData_->filename_, O_RDONLY | O_BINARY)) < 0) #else if ((fd_ = open(trackData_->filename_, O_RDONLY)) < 0) #endif { message(-2, "Cannot open data file \"%s\": %s", trackData_->filename_, strerror(errno)); return 1; } if (trackData_->offset_ > 0) { if (lseek(fd_, trackData_->offset_ , SEEK_SET) < 0) { message(-2, "Cannot seek to offset %ld in file \"%s\": %s", trackData_->offset_, trackData_->filename_, strerror(errno)); return 2; } } } } else if (trackData_->type_ == TrackData::FIFO) { #ifdef __CYGWIN__ if ((fd_ = open(trackData_->filename_, O_RDONLY | O_BINARY)) < 0) #else if ((fd_ = open(trackData_->filename_, O_RDONLY)) < 0) #endif { message(-2, "Cannot open FIFO \"%s\": %s", trackData_->filename_, strerror(errno)); return 1; } headerLength_ = 0; } else if (trackData_->type_ == TrackData::STDIN) { headerLength_ = 0; fd_ = fileno(stdin); } readPos_ = 0; open_ = 1; readUnderRunMsgGiven_ = 0; return 0; } // ends reading audio data, an audio data file is closed void TrackDataReader::closeData() { if (open_ != 0) { if (trackData_->type_ == TrackData::DATAFILE || trackData_->type_ == TrackData::FIFO) { close(fd_); } fd_ = -1; open_ = 0; readPos_ = 0; } } // fills 'buffer' with 'len' samples (in case of audio mode) or with 'len' // bytes of data (for all other modes) // return: number of samples written to buffer long TrackDataReader::readData(Sample *buffer, long len) { long readLen = 0; assert(open_ != 0); if (len == 0) { return 0; } if (readPos_ + len > trackData_->length()) { if ((len = trackData_->length() - readPos_) <= 0) { return 0; } } switch (trackData_->type_) { case TrackData::ZERODATA: if (trackData_->audioCutMode()) memset(buffer, 0, len * sizeof(Sample)); else memset(buffer, 0, len); readLen = len; break; case TrackData::STDIN: case TrackData::DATAFILE: case TrackData::FIFO: if (trackData_->audioCutMode()) { readLen = fullRead(fd_, buffer, len * sizeof(Sample)); if (readLen < 0) { message(-2, "Read error while reading audio data from file \"%s\": %s", trackData_->filename_, strerror(errno)); } else if (readLen != (long)(len * sizeof(Sample))) { long pad = len * sizeof(Sample) - readLen; if (readUnderRunMsgGiven_ == 0) { message(-1, "Could not read expected amount of audio data from file \"%s\".", trackData_->filename_); message(-1, "Padding with zeros."); readUnderRunMsgGiven_ = 1; } // Adding zeros to the 'buffer' memset(buffer + readLen, 0, pad); readLen = len; } else { readLen = len; } } else { readLen = fullRead(fd_, buffer, len); if (readLen < 0) { message(-2, "Read error while reading data from file \"%s\": %s", trackData_->filename_, strerror(errno)); } else if (readLen != len) { message(-2, "Could not read expected amount of data from file \"%s\".", trackData_->filename_); readLen = -1; } } break; } if (readLen > 0) { if (trackData_->mode_ == TrackData::AUDIO && trackData_->subChannelMode_ == TrackData::SUBCHAN_NONE) { int swap = 0; if (trackData_->fileType_ == TrackData::WAVE) { // WAVE files contain little endian samples swap = 1; } if (trackData_->swapSamples_) swap = !swap; if (swap) { // swap samples if (trackData_->audioCutMode()) swapSamples(buffer, readLen); else swapSamples(buffer, readLen / sizeof(Sample)); } } readPos_ += readLen; } return readLen; } // Seeks to specified sample. // return: 0: OK // 10: sample out of range int TrackDataReader::seekSample(unsigned long sample) { assert(open_ != 0); if (sample >= trackData_->length()) return 10; if (trackData_->type_ == TrackData::DATAFILE) { if (trackData_->audioCutMode()) { // 'sample' has samples as unit if (lseek(fd_, trackData_->offset_ + headerLength_ + (sample * sizeof(Sample)) + (trackData_->startPos_ * sizeof(Sample)), SEEK_SET) < 0) { message(-2, "Cannot seek in audio file \"%s\": %s", trackData_->filename_, strerror(errno)); return 10; } } else { // 'sample' has byte as unit if (lseek(fd_, trackData_->offset_ + headerLength_ + sample, SEEK_SET) < 0) { message(-2, "Cannot seek in audio file \"%s\": %s", trackData_->filename_, strerror(errno)); return 10; } } } readPos_ = sample; return 0; } // Returns number of bytes that are still available for reading. unsigned long TrackDataReader::readLeft() const { assert(open_ != 0); assert(trackData_ != NULL); return trackData_->length() - readPos_; }