/* cdrdao - write audio CD-Rs in disc-at-once mode
*
* Copyright (C) 1998-2001 Andreas Mueller <andreas@daneb.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.
*/
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#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_;
}
syntax highlighted by Code2HTML, v. 0.9.1