void APE::Properties::analyzeCurrent(File *file) { // Read the descriptor file->seek(2, File::Current); const ByteVector descriptor = file->readBlock(44); if(descriptor.size() < 44) { debug("APE::Properties::analyzeCurrent() -- descriptor is too short."); return; } const unsigned int descriptorBytes = descriptor.toUInt(0, false); if((descriptorBytes - 52) > 0) file->seek(descriptorBytes - 52, File::Current); // Read the header const ByteVector header = file->readBlock(24); if(header.size() < 24) { debug("APE::Properties::analyzeCurrent() -- MAC header is too short."); return; } // Get the APE info d->channels = header.toShort(18, false); d->sampleRate = header.toUInt(20, false); d->bitsPerSample = header.toShort(16, false); const unsigned int totalFrames = header.toUInt(12, false); if(totalFrames == 0) return; const unsigned int blocksPerFrame = header.toUInt(4, false); const unsigned int finalFrameBlocks = header.toUInt(8, false); d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; }
void RIFF::AIFF::Properties::read(const ByteVector &data) { d->channels = data.toShort(0U); d->sampleFrames = data.toUInt(2U); d->sampleWidth = data.toShort(6U); double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<const uchar *>(data.data() + 8)); d->sampleRate = (int)sampleRate; d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; }
void RIFF::WAV::Properties::read(const ByteVector &data) { d->format = data.toShort(0, false); d->channels = data.toShort(2, false); d->sampleRate = data.toUInt(4, false); d->sampleWidth = data.toShort(14, false); const uint byteRate = data.toUInt(8, false); d->bitrate = byteRate * 8 / 1000; d->length = byteRate > 0 ? d->streamLength / byteRate : 0; if(d->channels > 0 && d->sampleWidth > 0) d->sampleFrames = d->streamLength / (d->channels * ((d->sampleWidth + 7) / 8)); }
void RelativeVolumeFrame::parseFields(const ByteVector &data) { int pos = 0; d->identification = readStringField(data, String::Latin1, &pos); // Each channel is at least 4 bytes. while(pos <= (int)data.size() - 4) { ChannelType type = ChannelType(data[pos]); pos += 1; ChannelData &channel = d->channels[type]; channel.volumeAdjustment = data.toShort(static_cast<uint>(pos)); pos += 2; channel.peakVolume.bitsRepresentingPeak = data[pos]; pos += 1; int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak); channel.peakVolume.peakVolume = data.mid(pos, bytes); pos += bytes; } }
void APE::Properties::analyzeOld(File *file) { const ByteVector header = file->readBlock(26); if(header.size() < 26) { debug("APE::Properties::analyzeOld() -- MAC header is too short."); return; } const unsigned int totalFrames = header.toUInt(18, false); // Fail on 0 length APE files (catches non-finalized APE files) if(totalFrames == 0) return; const short compressionLevel = header.toShort(0, false); unsigned int blocksPerFrame; if(d->version >= 3950) blocksPerFrame = 73728 * 4; else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000)) blocksPerFrame = 73728; else blocksPerFrame = 9216; // Get the APE info d->channels = header.toShort(4, false); d->sampleRate = header.toUInt(6, false); const unsigned int finalFrameBlocks = header.toUInt(22, false); d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; // Get the bit depth from the RIFF-fmt chunk. file->seek(16, File::Current); const ByteVector fmt = file->readBlock(28); if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) { debug("APE::Properties::analyzeOld() -- fmt header is too short."); return; } d->bitsPerSample = fmt.toShort(26, false); }
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) : AudioProperties(style) { d = new PropertiesPrivate; MP4::Atom *moov = atoms->find("moov"); if(!moov) { debug("MP4: Atom 'moov' not found"); return; } MP4::Atom *trak = 0; ByteVector data; MP4::AtomList trakList = moov->findall("trak"); for (unsigned int i = 0; i < trakList.size(); i++) { trak = trakList[i]; MP4::Atom *hdlr = trak->find("mdia", "hdlr"); if(!hdlr) { debug("MP4: Atom 'trak.mdia.hdlr' not found"); return; } file->seek(hdlr->offset); data = file->readBlock(hdlr->length); if(data.mid(16, 4) == "soun") { break; } trak = 0; } if (!trak) { debug("MP4: No audio tracks"); return; } MP4::Atom *mdhd = trak->find("mdia", "mdhd"); if(!mdhd) { debug("MP4: Atom 'trak.mdia.mdhd' not found"); return; } file->seek(mdhd->offset); data = file->readBlock(mdhd->length); uint version = data[8]; if(version == 1) { if (data.size() < 36 + 8) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } const long long unit = data.toLongLong(28U); const long long length = data.toLongLong(36U); d->length = unit ? int(length / unit) : 0; } else { if (data.size() < 24 + 4) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } const unsigned int unit = data.toUInt(20U); const unsigned int length = data.toUInt(24U); d->length = unit ? length / unit : 0; } MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); if(!atom) { return; } file->seek(atom->offset); data = file->readBlock(atom->length); if(data.mid(20, 4) == "mp4a") { d->channels = data.toShort(40U); d->bitsPerSample = data.toShort(42U); d->sampleRate = data.toUInt(46U); if(data.mid(56, 4) == "esds" && data[64] == 0x03) { uint pos = 65; if(data.mid(pos, 3) == "\x80\x80\x80") { pos += 3; } pos += 4; if(data[pos] == 0x04) { pos += 1; if(data.mid(pos, 3) == "\x80\x80\x80") { pos += 3; } pos += 10; d->bitrate = (data.toUInt(pos) + 500) / 1000; } } } else if (data.mid(20, 4) == "alac") { if (atom->length == 88 && data.mid(56, 4) == "alac") { d->bitsPerSample = data.at(69); d->channels = data.at(73); d->bitrate = data.toUInt(80U) / 1000; d->sampleRate = data.toUInt(84U); } } MP4::Atom *drms = atom->find("drms"); if(drms) { d->encrypted = true; } }
void RIFF::WAV::Properties::read(File *file) { ByteVector data; uint streamLength = 0; uint totalSamples = 0; for(uint i = 0; i < file->chunkCount(); ++i) { const ByteVector name = file->chunkName(i); if(name == "fmt ") { if(data.isEmpty()) data = file->chunkData(i); else debug("RIFF::WAV::Properties::read() - Duplicate 'fmt ' chunk found."); } else if(name == "data") { if(streamLength == 0) streamLength = file->chunkDataSize(i) + file->chunkPadding(i); else debug("RIFF::WAV::Properties::read() - Duplicate 'data' chunk found."); } else if(name == "fact") { if(totalSamples == 0) totalSamples = file->chunkData(i).toUInt(0, false); else debug("RIFF::WAV::Properties::read() - Duplicate 'fact' chunk found."); } } if(data.size() < 16) { debug("RIFF::WAV::Properties::read() - 'fmt ' chunk not found or too short."); return; } if(streamLength == 0) { debug("RIFF::WAV::Properties::read() - 'data' chunk not found."); return; } d->format = data.toShort(0, false); if(d->format != FORMAT_PCM && totalSamples == 0) { debug("RIFF::WAV::Properties::read() - Non-PCM format, but 'fact' chunk not found."); return; } d->channels = data.toShort(2, false); d->sampleRate = data.toUInt(4, false); d->bitsPerSample = data.toShort(14, false); if(totalSamples > 0) d->sampleFrames = totalSamples; else if(d->channels > 0 && d->bitsPerSample > 0) d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8)); if(d->sampleFrames > 0 && d->sampleRate > 0) { const double length = d->sampleFrames * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } else { const uint byteRate = data.toUInt(8, false); if(byteRate > 0) { d->length = static_cast<int>(streamLength * 1000.0 / byteRate + 0.5); d->bitrate = static_cast<int>(byteRate * 8.0 / 1000.0 + 0.5); } } }
int ASF::File::readWORD() { ByteVector v = readBlock(2); return v.toShort(false); }
void MP4::Properties::read(File *file, Atoms *atoms) { MP4::Atom *moov = atoms->find("moov"); if(!moov) { debug("MP4: Atom 'moov' not found"); return; } MP4::Atom *trak = 0; ByteVector data; const MP4::AtomList trakList = moov->findall("trak"); for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) { trak = *it; MP4::Atom *hdlr = trak->find("mdia", "hdlr"); if(!hdlr) { debug("MP4: Atom 'trak.mdia.hdlr' not found"); return; } file->seek(hdlr->offset); data = file->readBlock(hdlr->length); if(data.containsAt("soun", 16)) { break; } trak = 0; } if(!trak) { debug("MP4: No audio tracks"); return; } MP4::Atom *mdhd = trak->find("mdia", "mdhd"); if(!mdhd) { debug("MP4: Atom 'trak.mdia.mdhd' not found"); return; } file->seek(mdhd->offset); data = file->readBlock(mdhd->length); const unsigned int version = data[8]; long long unit; long long length; if(version == 1) { if(data.size() < 36 + 8) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } unit = data.toUInt(28U); length = data.toLongLong(32U); } else { if(data.size() < 24 + 8) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } unit = data.toUInt(20U); length = data.toUInt(24U); } if(unit > 0 && length > 0) d->length = static_cast<int>(length * 1000.0 / unit + 0.5); MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); if(!atom) { return; } file->seek(atom->offset); data = file->readBlock(atom->length); if(data.containsAt("mp4a", 20)) { d->codec = AAC; d->channels = data.toShort(40U); d->bitsPerSample = data.toShort(42U); d->sampleRate = data.toUInt(46U); if(data.containsAt("esds", 56) && data[64] == 0x03) { unsigned int pos = 65; if(data.containsAt("\x80\x80\x80", pos)) { pos += 3; } pos += 4; if(data[pos] == 0x04) { pos += 1; if(data.containsAt("\x80\x80\x80", pos)) { pos += 3; } pos += 10; d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5); } } } else if(data.containsAt("alac", 20)) { if(atom->length == 88 && data.containsAt("alac", 56)) { d->codec = ALAC; d->bitsPerSample = data.at(69); d->channels = data.at(73); d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5); d->sampleRate = data.toUInt(84U); } } MP4::Atom *drms = atom->find("drms"); if(drms) { d->encrypted = true; } }