// checks the streams for issues (missing ID3V2, Unknown streams, inconsistencies, ...) void Mp3Handler::analyze(const QualThresholds& qualThresholds) { NoteColl& notes (m_notes); // for MP3_NOTE() for (int i = 0, n = cSize(m_vpAllStreams); i < n; ++i) { DataStream* pDs (m_vpAllStreams[i]); { Id3V230Stream* p (dynamic_cast<Id3V230Stream*>(pDs)); if (0 != p) { if (0 == m_pId3V230Stream) m_pId3V230Stream = p; else MP3_NOTE (p->getPos(), twoId3V230); } } { Id3V240Stream* p (dynamic_cast<Id3V240Stream*>(pDs)); if (0 != p) { if (0 == m_pId3V240Stream) m_pId3V240Stream = p; else MP3_NOTE (p->getPos(), twoId3V240); } } { Id3V1Stream* p (dynamic_cast<Id3V1Stream*>(pDs)); if (0 != p) { if (0 == m_pId3V1Stream) m_pId3V1Stream = p; else MP3_NOTE (p->getPos(), twoId3V1); } } { LameStream* p (dynamic_cast<LameStream*>(pDs)); if (0 != p) { if (0 == m_pLameStream) m_pLameStream = p; else MP3_NOTE (p->getPos(), twoLame); } } { XingStreamBase* p (dynamic_cast<XingStreamBase*>(pDs)); if (0 != p) { if (0 == m_pXingStream) { m_pXingStream = p; if (i < n - 2 && p->isBrokenByMp3Fixer(m_vpAllStreams[i + 1], m_vpAllStreams[i + 2])) { MP3_NOTE (p->getPos(), xingAddedByMp3Fixer); } if (i < n - 1) { MpegStream* q (dynamic_cast<MpegStream*>(m_vpAllStreams[i + 1])); if (0 != q && p->getFrameCount() != q->getFrameCount()) { MP3_NOTE (p->getPos(), xingFrameCountMismatch); } } } else MP3_NOTE (p->getPos(), twoXing); } } { VbriStream* p (dynamic_cast<VbriStream*>(pDs)); if (0 != p) { if (0 == m_pVbriStream) m_pVbriStream = p; else MP3_NOTE (p->getPos(), twoVbri); } } { MpegStream* p (dynamic_cast<MpegStream*>(pDs)); if (0 != p) { if (0 == m_pMpegStream) m_pMpegStream = p; else MP3_NOTE (p->getPos(), twoAudio); //const MpegFrame& frm (p->getFirstFrame()); if (p->isVbr()) { switch (p->getChannelMode()) { // ttt2 should be possible that changing qual thresholds in config triggers notes being added / removed case MpegFrame::STEREO: if (p->getBitrate() < qualThresholds.m_nStereoVbr) { MP3_NOTE (p->getPos(), lowQualAudio); } break; case MpegFrame::JOINT_STEREO: if (p->getBitrate() < qualThresholds.m_nJointStereoVbr) { MP3_NOTE (p->getPos(), lowQualAudio); } break; case MpegFrame::DUAL_CHANNEL: if (p->getBitrate() < qualThresholds.m_nDoubleChannelVbr) { MP3_NOTE (p->getPos(), lowQualAudio); } break; case MpegFrame::SINGLE_CHANNEL: if (p->getBitrate() < qualThresholds.m_nDoubleChannelVbr / 2) { MP3_NOTE (p->getPos(), lowQualAudio); } break; } } else { switch (p->getChannelMode()) { case MpegFrame::STEREO: if (p->getBitrate() < qualThresholds.m_nStereoCbr) { MP3_NOTE (p->getPos(), lowQualAudio); } break; case MpegFrame::JOINT_STEREO: if (p->getBitrate() < qualThresholds.m_nJointStereoCbr) { MP3_NOTE (p->getPos(), lowQualAudio); } break; case MpegFrame::DUAL_CHANNEL: if (p->getBitrate() < qualThresholds.m_nDoubleChannelCbr) { MP3_NOTE (p->getPos(), lowQualAudio); } break; case MpegFrame::SINGLE_CHANNEL: if (p->getBitrate() < qualThresholds.m_nDoubleChannelCbr / 2) { MP3_NOTE (p->getPos(), lowQualAudio); } break; } } } } { ApeStream* p (dynamic_cast<ApeStream*>(pDs)); if (0 != p) { if (0 == m_pApeStream) m_pApeStream = p; else MP3_NOTE (p->getPos(), twoApe); } } { LyricsStream* p (dynamic_cast<LyricsStream*>(pDs)); if (0 != p) { if (0 == m_pLyricsStream) m_pLyricsStream = p; else MP3_NOTE (p->getPos(), twoLyr); } } { NullDataStream* p (dynamic_cast<NullDataStream*>(pDs)); if (0 != p) { m_vpNullStreams.push_back(p); } } { UnknownDataStream* p (dynamic_cast<UnknownDataStream*>(pDs)); if (0 != p) { m_vpUnknownStreams.push_back(p); } } { BrokenDataStream* p (dynamic_cast<BrokenDataStream*>(pDs)); if (0 != p) { m_vpBrokenStreams.push_back(p); } } { UnsupportedDataStream* p (dynamic_cast<UnsupportedDataStream*>(pDs)); if (0 != p) { m_vpUnsupportedStreams.push_back(p); } } { TruncatedMpegDataStream* p (dynamic_cast<TruncatedMpegDataStream*>(pDs)); if (0 != p) { m_vpTruncatedMpegStreams.push_back(p); } } } if (0 == m_pMpegStream) { MP3_NOTE (-1, noAudio); } if (0 == m_pId3V230Stream) { MP3_NOTE (-1, noId3V230); } if (0 != m_pId3V230Stream && 0 != m_pId3V240Stream) { MP3_NOTE (m_pId3V240Stream->getPos(), bothId3V230_V240); } if (0 != m_pVbriStream) { MP3_NOTE (m_pVbriStream->getPos(), vbriFound); } //if (0 != m_pLyricsStream) { MP3_NOTE (m_pLyricsStream->getPos(), lyricsNotSupported); } if (0 != m_pId3V1Stream && 0 == m_pId3V230Stream && 0 == m_pId3V240Stream && 0 == m_pApeStream) { MP3_NOTE (m_pId3V1Stream->getPos(), onlyId3V1); } if (0 == m_pId3V1Stream && 0 == m_pId3V230Stream && 0 == m_pId3V240Stream && 0 == m_pApeStream) { MP3_NOTE (-1, noInfoTag); } if (!m_vpNullStreams.empty()) { MP3_NOTE (m_vpNullStreams[0]->getPos(), foundNull); } if (0 != m_pVbriStream && 0 != m_pXingStream) { MP3_NOTE (m_pVbriStream->getPos(), foundVbriAndXing); } if (0 != m_pXingStream && 0 != m_pMpegStream && m_pXingStream->getIndex() != m_pMpegStream->getIndex() - 1) { MP3_NOTE (m_pXingStream->getPos(), xingNotBeforeAudio); } if (0 != m_pXingStream && 0 != m_pMpegStream && !m_pXingStream->matchesStructure(*m_pMpegStream)) { MP3_NOTE (m_pXingStream->getPos(), incompatXing); } if (0 != m_pId3V1Stream && 0 != m_pMpegStream && m_pId3V1Stream->getIndex() < m_pMpegStream->getIndex()) { MP3_NOTE (m_pId3V1Stream->getPos(), id3v1BeforeAudio); } if (0 != m_pId3V230Stream && 0 != m_pId3V230Stream->getIndex()) { MP3_NOTE (m_pId3V230Stream->getPos(), id3v230AfterAudio); } if (0 == m_pXingStream && 0 != m_pMpegStream && m_pMpegStream->isVbr()) { MP3_NOTE (m_pMpegStream->getPos(), missingXing); } if (0 != m_pMpegStream && m_pMpegStream->isVbr() && (MpegFrame::MPEG1 != m_pMpegStream->getFirstFrame().getVersion() || MpegFrame::LAYER3 != m_pMpegStream->getFirstFrame().getLayer())) { MP3_NOTE (m_pMpegStream->getPos(), vbrUsedForNonMpg1L3); } if ( (0 != m_pMpegStream) && (0 == m_pApeStream || !m_pApeStream->hasMp3Gain()) && (0 == m_pId3V230Stream || !m_pId3V230Stream->hasReplayGain()) && (0 == m_pId3V240Stream || !m_pId3V240Stream->hasReplayGain())) //ttt2 not quite OK when mutliple ID3V2 streams are found { MP3_NOTE (m_pMpegStream->getPos(), noMp3Gain); } if (!m_vpBrokenStreams.empty()) { if (1 == cSize(m_vpBrokenStreams) && m_vpBrokenStreams.back() == m_vpAllStreams.back()) { MP3_NOTE (m_vpBrokenStreams.back()->getPos(), brokenAtTheEnd); } else { MP3_NOTE (m_vpBrokenStreams.back()->getPos(), brokenInTheMiddle); } } if (!m_vpUnknownStreams.empty()) { if (1 == cSize(m_vpUnknownStreams) && m_vpUnknownStreams.back() == m_vpAllStreams.back()) { MP3_NOTE (m_vpUnknownStreams.back()->getPos(), unknownAtTheEnd); } else { MP3_NOTE (m_vpUnknownStreams.back()->getPos(), unknownInTheMiddle); } } if (!m_vpUnsupportedStreams.empty()) { MP3_NOTE (m_vpUnsupportedStreams.back()->getPos(), unsupportedFound); } if (!m_vpTruncatedMpegStreams.empty()) { if (1 == cSize(m_vpTruncatedMpegStreams) && m_vpTruncatedMpegStreams.back() == m_vpAllStreams.back()) { MP3_NOTE (m_vpTruncatedMpegStreams.back()->getPos(), truncAudioWithWholeFile); } else { MP3_NOTE (m_vpTruncatedMpegStreams.back()->getPos(), truncAudio); } } m_notes.sort(); //sort(m_notes.begin(), m_notes.end(), notePtrCmp); }