コード例 #1
0
ファイル: soundsourcem4a.cpp プロジェクト: djsteph/mixxx
SINT SoundSourceM4A::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));

    if (frameIndex >= getMaxFrameIndex()) {
        // EOF
        m_curFrameIndex = getMaxFrameIndex();
        return m_curFrameIndex;
    }

    // NOTE(uklotzde): Resetting the decoder near to the beginning
    // of the stream when seeking backwards produces invalid sample
    // values! As a consequence the seeking test fails.
    if (isValidSampleBlockId(m_curSampleBlockId) &&
            (frameIndex <= kNumberOfPrefetchFrames)) {
        // Workaround: Reset the decoder when seeking near to the beginning
        // of the stream while decoding.
        reopenDecoder();
        skipSampleFrames(frameIndex);
    }
    if (frameIndex == m_curFrameIndex) {
        return m_curFrameIndex;
    }

    MP4SampleId sampleBlockId = kSampleBlockIdMin
            + (frameIndex / m_framesPerSampleBlock);
    DEBUG_ASSERT(isValidSampleBlockId(sampleBlockId));
    if ((frameIndex < m_curFrameIndex) || // seeking backwards?
            !isValidSampleBlockId(m_curSampleBlockId) || // invalid seek position?
            (sampleBlockId
                    > (m_curSampleBlockId + m_numberOfPrefetchSampleBlocks))) { // jumping forward?
        // Restart decoding one or more blocks of samples backwards
        // from the calculated starting block to avoid audible glitches.
        // Implementation note: The type MP4SampleId is unsigned so we
        // need to be careful when subtracting!
        if ((kSampleBlockIdMin + m_numberOfPrefetchSampleBlocks)
                < sampleBlockId) {
            sampleBlockId -= m_numberOfPrefetchSampleBlocks;
        } else {
            sampleBlockId = kSampleBlockIdMin;
        }
        restartDecoding(sampleBlockId);
        DEBUG_ASSERT(m_curSampleBlockId == sampleBlockId);
    }

    // Decoding starts before the actual target position
    DEBUG_ASSERT(m_curFrameIndex <= frameIndex);

    // Skip (= decode and discard) all samples up to the target position
    const SINT prefetchFrameCount = frameIndex - m_curFrameIndex;
    const SINT skipFrameCount = skipSampleFrames(prefetchFrameCount);
    DEBUG_ASSERT(skipFrameCount <= prefetchFrameCount);
    if (skipFrameCount < prefetchFrameCount) {
        qWarning() << "Failed to prefetch sample data while seeking"
                << skipFrameCount << "<" << prefetchFrameCount;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    return m_curFrameIndex;
}
コード例 #2
0
ファイル: soundsourceflac.cpp プロジェクト: jox58/mixxx
SINT SoundSourceFLAC::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    // Avoid unnecessary seeking
    // NOTE(uklotzde): Disabling this optimization might reveal rare
    // seek errors on certain FLAC files were the decoder loses sync!
    if (m_curFrameIndex == frameIndex) {
        return m_curFrameIndex;
    }

    // Discard decoded sample data before seeking
    m_sampleBuffer.reset();

    // Seek to the new position
    if (FLAC__stream_decoder_seek_absolute(m_decoder, frameIndex)) {
        // Set the new position
        m_curFrameIndex = frameIndex;
        DEBUG_ASSERT(FLAC__STREAM_DECODER_SEEK_ERROR != FLAC__stream_decoder_get_state(m_decoder));
    } else {
        qWarning() << "Seek error at" << frameIndex << "in" << m_file.fileName();
        // Invalidate the current position
        m_curFrameIndex = getMaxFrameIndex();
        if (FLAC__STREAM_DECODER_SEEK_ERROR == FLAC__stream_decoder_get_state(m_decoder)) {
            // Flush the input stream of the decoder according to the
            // documentation of FLAC__stream_decoder_seek_absolute()
            if (!FLAC__stream_decoder_flush(m_decoder)) {
                qWarning() << "Failed to flush input buffer of the FLAC decoder after seeking in"
                        << m_file.fileName();
                // Invalidate the current position...
                m_curFrameIndex = getMaxFrameIndex();
                // ...and abort
                return m_curFrameIndex;
            }
            // Discard previously decoded sample data before decoding
            // the next block of samples
            m_sampleBuffer.reset();
            // Trigger decoding of the next block to update the current position
            if (!FLAC__stream_decoder_process_single(m_decoder)) {
                qWarning() << "Failed to resync FLAC decoder after seeking in"
                        << m_file.fileName();
                // Invalidate the current position...
                m_curFrameIndex = getMaxFrameIndex();
                // ...and abort
                return m_curFrameIndex;
            }
            DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
            if (m_curFrameIndex < frameIndex) {
                // Adjust the current position
                skipSampleFrames(frameIndex - m_curFrameIndex);
            }
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    return m_curFrameIndex;
}
コード例 #3
0
ファイル: soundsourcem4a.cpp プロジェクト: EQ4/mixxx
SINT SoundSourceM4A::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    // Handle trivial case
    if (m_curFrameIndex == frameIndex) {
        // Nothing to do
        return m_curFrameIndex;
    }
    // Handle edge case
    if (getMaxFrameIndex() <= frameIndex) {
        // EOF reached
        m_curFrameIndex = getMaxFrameIndex();
        return m_curFrameIndex;
    }

    MP4SampleId sampleBlockId = kSampleBlockIdMin
            + (frameIndex / m_framesPerSampleBlock);
    DEBUG_ASSERT(isValidSampleBlockId(sampleBlockId));
    if ((frameIndex < m_curFrameIndex) || // seeking backwards?
            !isValidSampleBlockId(m_curSampleBlockId) || // invalid seek position?
            (sampleBlockId
                    > (m_curSampleBlockId + m_numberOfPrefetchSampleBlocks))) { // jumping forward?
        // Restart decoding one or more blocks of samples backwards
        // from the calculated starting block to avoid audible glitches.
        // Implementation note: The type MP4SampleId is unsigned so we
        // need to be careful when subtracting!
        if ((kSampleBlockIdMin + m_numberOfPrefetchSampleBlocks)
                < sampleBlockId) {
            sampleBlockId -= m_numberOfPrefetchSampleBlocks;
        } else {
            sampleBlockId = kSampleBlockIdMin;
        }
        restartDecoding(sampleBlockId);
        DEBUG_ASSERT(m_curSampleBlockId == sampleBlockId);
    }

    // Decoding starts before the actual target position
    DEBUG_ASSERT(m_curFrameIndex <= frameIndex);

    // Skip (= decode and discard) all samples up to the target position
    const SINT prefetchFrameCount = frameIndex - m_curFrameIndex;
    const SINT skipFrameCount = skipSampleFrames(prefetchFrameCount);
    DEBUG_ASSERT(skipFrameCount <= prefetchFrameCount);
    if (skipFrameCount < prefetchFrameCount) {
        qWarning() << "Failed to prefetch sample data while seeking"
                << skipFrameCount << "<" << prefetchFrameCount;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    return m_curFrameIndex;
}
コード例 #4
0
ファイル: soundsourcem4a.cpp プロジェクト: Kindamba/mixxx
SINT SoundSourceM4A::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    if (m_curFrameIndex != frameIndex) {
        MP4SampleId sampleBlockId = kSampleBlockIdMin
                + (frameIndex / kFramesPerSampleBlock);
        DEBUG_ASSERT(isValidSampleBlockId(sampleBlockId));
        if ((frameIndex < m_curFrameIndex) || // seeking backwards?
                !isValidSampleBlockId(m_curSampleBlockId) || // invalid seek position?
                (sampleBlockId
                        > (m_curSampleBlockId + kNumberOfPrefetchSampleBlocks))) { // jumping forward?
            // Restart decoding one or more blocks of samples backwards
            // from the calculated starting block to avoid audible glitches.
            // Implementation note: The type MP4SampleId is unsigned so we
            // need to be careful when subtracting!
            if ((kSampleBlockIdMin + kNumberOfPrefetchSampleBlocks)
                    < sampleBlockId) {
                sampleBlockId -= kNumberOfPrefetchSampleBlocks;
            } else {
                sampleBlockId = kSampleBlockIdMin;
            }
            restartDecoding(sampleBlockId);
            DEBUG_ASSERT(m_curSampleBlockId == sampleBlockId);
        }
        // Decoding starts before the actual target position
        DEBUG_ASSERT(m_curFrameIndex <= frameIndex);
        // Prefetch (decode and discard) all samples up to the target position
        const SINT prefetchFrameCount = frameIndex - m_curFrameIndex;
        const SINT skipFrameCount =
                skipSampleFrames(prefetchFrameCount);
        DEBUG_ASSERT(skipFrameCount <= prefetchFrameCount);
        if (skipFrameCount != prefetchFrameCount) {
            qWarning()
                    << "Failed to skip over prefetched sample frames after seeking @"
                    << m_curFrameIndex;
            return m_curFrameIndex; // abort
        }
    }
    DEBUG_ASSERT(m_curFrameIndex == frameIndex);
    return m_curFrameIndex;
}
コード例 #5
0
ファイル: soundsourcemp3.cpp プロジェクト: PetrBarborka/mixxx
SINT SoundSourceMp3::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    // Handle trivial case
    if (m_curFrameIndex == frameIndex) {
        // Nothing to do
        return m_curFrameIndex;
    }
    // Handle edge case
    if (getMaxFrameIndex() <= frameIndex) {
        // EOF reached
        m_curFrameIndex = getMaxFrameIndex();
        return m_curFrameIndex;
    }

    SINT seekFrameIndex = findSeekFrameIndex(
            frameIndex);
    DEBUG_ASSERT(SINT(m_seekFrameList.size()) > seekFrameIndex);
    const SINT curSeekFrameIndex = findSeekFrameIndex(
            m_curFrameIndex);
    DEBUG_ASSERT(SINT(m_seekFrameList.size()) > curSeekFrameIndex);
    // some consistency checks
    DEBUG_ASSERT((curSeekFrameIndex >= seekFrameIndex) || (m_curFrameIndex < frameIndex));
    DEBUG_ASSERT((curSeekFrameIndex <= seekFrameIndex) || (m_curFrameIndex > frameIndex));
    if ((getMaxFrameIndex() <= m_curFrameIndex) || // out of range
            (frameIndex < m_curFrameIndex) || // seek backward
            (seekFrameIndex > (curSeekFrameIndex + kMp3SeekFramePrefetchCount))) { // jump forward

        // Adjust the seek frame index for prefetching
        // Implementation note: The type SINT is unsigned so
        // need to be careful when subtracting!
        if (kMp3SeekFramePrefetchCount < seekFrameIndex) {
            // Restart decoding kMp3SeekFramePrefetchCount seek frames
            // before the expected sync position
            seekFrameIndex -= kMp3SeekFramePrefetchCount;
        } else {
            // Restart decoding at the beginning of the audio stream
            seekFrameIndex = 0;
        }

        m_curFrameIndex = restartDecoding(m_seekFrameList[seekFrameIndex]);
        if (getMaxFrameIndex() <= m_curFrameIndex) {
            // out of range -> abort
            return m_curFrameIndex;
        }
        DEBUG_ASSERT(findSeekFrameIndex(m_curFrameIndex) == seekFrameIndex);
    }

    // Decoding starts before the actual target position
    DEBUG_ASSERT(m_curFrameIndex <= frameIndex);

    // Skip (= decode and discard) all samples up to the target position
    const SINT prefetchFrameCount = frameIndex - m_curFrameIndex;
    const SINT skipFrameCount = skipSampleFrames(prefetchFrameCount);
    DEBUG_ASSERT(skipFrameCount <= prefetchFrameCount);
    if (skipFrameCount < prefetchFrameCount) {
        qWarning() << "Failed to prefetch sample data while seeking"
                << skipFrameCount << "<" << prefetchFrameCount;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    return m_curFrameIndex;
}
コード例 #6
0
SINT SoundSourceMediaFoundation::seekSampleFrame(
        SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_currentFrameIndex));

    if (frameIndex >= getMaxFrameIndex()) {
        // EOF
        m_currentFrameIndex = getMaxFrameIndex();
        return m_currentFrameIndex;
    }

    if (frameIndex > m_currentFrameIndex) {
        // seeking forward
        SINT skipFramesCount = frameIndex - m_currentFrameIndex;
        // When to prefer skipping over seeking:
        // 1) The sample buffer would be discarded before seeking anyway and
        //    skipping those already decoded samples effectively costs nothing
        // 2) After seeking we need to decode at least kNumberOfPrefetchFrames
        //    before reaching the actual target position -> Only seek if we
        //    need to decode more than  2 * kNumberOfPrefetchFrames frames
        //    while skipping
        SINT skipFramesCountMax =
                samples2frames(m_sampleBuffer.getSize()) +
                2 * kNumberOfPrefetchFrames;
        if (skipFramesCount <= skipFramesCountMax) {
            skipSampleFrames(skipFramesCount);
        }
    }
    if (frameIndex == m_currentFrameIndex) {
        return m_currentFrameIndex;
    }

    // Discard decoded samples
    m_sampleBuffer.reset();

    // Invalidate current position (end of stream)
    m_currentFrameIndex = getMaxFrameIndex();

    if (m_pSourceReader == nullptr) {
        // reader is dead
        return m_currentFrameIndex;
    }

    // Jump to a position before the actual seeking position.
    // Prefetching a certain number of frames is necessary for
    // sample accurate decoding. The decoder needs to decode
    // some frames in advance to produce the same result at
    // each position in the stream.
    SINT seekIndex = std::max(SINT(frameIndex - kNumberOfPrefetchFrames), AudioSource::getMinFrameIndex());

    LONGLONG seekPos = m_streamUnitConverter.fromFrameIndex(seekIndex);
    DEBUG_ASSERT(seekPos >= 0);
    PROPVARIANT prop;
    HRESULT hrInitPropVariantFromInt64 =
            InitPropVariantFromInt64(seekPos, &prop);
    DEBUG_ASSERT(SUCCEEDED(hrInitPropVariantFromInt64)); // never fails
    HRESULT hrSetCurrentPosition =
            m_pSourceReader->SetCurrentPosition(GUID_NULL, prop);
    PropVariantClear(&prop);
    if (SUCCEEDED(hrSetCurrentPosition)) {
        // NOTE(uklotzde): After SetCurrentPosition() the actual position
        // of the stream is unknown until reading the next samples from
        // the reader. Please note that the first sample decoded after
        // SetCurrentPosition() may start BEFORE the actual target position.
        // See also: https://msdn.microsoft.com/en-us/library/windows/desktop/dd374668(v=vs.85).aspx
        //   "The SetCurrentPosition method does not guarantee exact seeking." ...
        //   "After seeking, the application should call IMFSourceReader::ReadSample
        //    and advance to the desired position.
        SINT skipFramesCount = frameIndex - seekIndex;
        if (skipFramesCount > 0) {
            // We need to fetch at least 1 sample from the reader to obtain the
            // current position!
            skipSampleFrames(skipFramesCount);
            // Now m_currentFrameIndex reflects the actual position of the reader
            if (m_currentFrameIndex < frameIndex) {
                // Skip more samples if frameIndex has not yet been reached
                skipSampleFrames(frameIndex - m_currentFrameIndex);
            }
            if (m_currentFrameIndex != frameIndex) {
                qWarning() << kLogPreamble
                        << "Seek to frame"
                        << frameIndex
                        << "failed";
                // Jump to end of stream (= invalidate current position)
                m_currentFrameIndex = getMaxFrameIndex();
            }
        } else {
            // We are at the beginning of the stream and don't need
            // to skip any frames. Calling IMFSourceReader::ReadSample
            // is not necessary in this special case.
            DEBUG_ASSERT(frameIndex == AudioSource::getMinFrameIndex());
            m_currentFrameIndex = frameIndex;
        }
    } else {
        qWarning() << kLogPreamble
                << "IMFSourceReader::SetCurrentPosition() failed"
                << hrSetCurrentPosition;
        safeRelease(&m_pSourceReader); // kill the reader
    }

    return m_currentFrameIndex;
}
コード例 #7
0
ファイル: soundsourceflac.cpp プロジェクト: demos/mixxx
SINT SoundSourceFLAC::readSampleFrames(
        SINT numberOfFrames, CSAMPLE* sampleBuffer,
        SINT sampleBufferSize, bool readStereoSamples) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(getSampleBufferSize(numberOfFrames, readStereoSamples) <= sampleBufferSize);

    const SINT numberOfFramesTotal =
            math_min(numberOfFrames, getMaxFrameIndex() - m_curFrameIndex);
    const SINT numberOfSamplesTotal = frames2samples(numberOfFramesTotal);

    CSAMPLE* outBuffer = sampleBuffer;
    SINT numberOfSamplesRemaining = numberOfSamplesTotal;
    while (0 < numberOfSamplesRemaining) {
        // If our buffer from libflac is empty (either because we explicitly cleared
        // it or because we've simply used all the samples), ask for a new buffer
        if (m_sampleBuffer.isEmpty()) {
            // Save the current frame index
            const SINT curFrameIndexBeforeProcessing = m_curFrameIndex;
            // Documentation of FLAC__stream_decoder_process_single():
            // "Depending on what was decoded, the metadata or write callback
            // will be called with the decoded metadata block or audio frame."
            // See also: https://xiph.org/flac/api/group__flac__stream__decoder.html#ga9d6df4a39892c05955122cf7f987f856
            if (!FLAC__stream_decoder_process_single(m_decoder)) {
                qWarning() << "Failed to decode FLAC file"
                        << m_file.fileName();
                break; // abort
            }
            // After seeking we might need to skip some samples if the decoder
            // complained that it has lost sync for some malformed(?) files
            if (curFrameIndexBeforeProcessing != m_curFrameIndex) {
                if (curFrameIndexBeforeProcessing > m_curFrameIndex) {
                    qWarning() << "Trying to adjust frame index"
                            << m_curFrameIndex << "<>" << curFrameIndexBeforeProcessing
                            << "while decoding FLAC file"
                            << m_file.fileName();
                    skipSampleFrames(curFrameIndexBeforeProcessing - m_curFrameIndex);
                } else {
                    qWarning() << "Unexpected frame index"
                            << m_curFrameIndex << "<>" << curFrameIndexBeforeProcessing
                            << "while decoding FLAC file"
                            << m_file.fileName();
                    break; // abort
                }
            }
            DEBUG_ASSERT(curFrameIndexBeforeProcessing == m_curFrameIndex);
        }
        if (m_sampleBuffer.isEmpty()) {
            break; // EOF
        }

        const SampleBuffer::ReadableChunk readableChunk(
                m_sampleBuffer.readFromHead(numberOfSamplesRemaining));
        const SINT framesToCopy = samples2frames(readableChunk.size());
        if (outBuffer) {
            if (readStereoSamples && (kChannelCountStereo != getChannelCount())) {
                if (kChannelCountMono == getChannelCount()) {
                    SampleUtil::copyMonoToDualMono(outBuffer,
                            readableChunk.data(),
                            framesToCopy);
                } else {
                    SampleUtil::copyMultiToStereo(outBuffer,
                            readableChunk.data(),
                            framesToCopy, getChannelCount());
                }
                outBuffer += framesToCopy * kChannelCountStereo;
            } else {
                SampleUtil::copy(outBuffer, readableChunk.data(), readableChunk.size());
                outBuffer += readableChunk.size();
            }
        }
        m_curFrameIndex += framesToCopy;
        numberOfSamplesRemaining -= readableChunk.size();
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfSamplesTotal >= numberOfSamplesRemaining);
    return samples2frames(numberOfSamplesTotal - numberOfSamplesRemaining);
}
コード例 #8
0
ファイル: soundsourceflac.cpp プロジェクト: demos/mixxx
SINT SoundSourceFLAC::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    // Seek to the new position
    SINT seekFrameIndex = frameIndex;
    int retryCount = 0;
    // NOTE(uklotzde): This loop avoids unnecessary seek operations.
    // If the file is decoded from the beginning to the end during
    // continuous playback no seek operations are necessary. This
    // may hide rare seek errors that we have observed in some "flaky"
    // FLAC files. The retry strategy implemented by this loop tries
    // to solve these issues when randomly seeking through such a file.
    while ((seekFrameIndex != m_curFrameIndex) &&
            (retryCount <= kSeekErrorMaxRetryCount)){
        // Discard decoded sample data before seeking
        m_sampleBuffer.reset();
        // Invalidate the current position
        m_curFrameIndex = getMaxFrameIndex();
        if (FLAC__stream_decoder_seek_absolute(m_decoder, seekFrameIndex)) {
            // Success: Set the new position
            m_curFrameIndex = seekFrameIndex;
            DEBUG_ASSERT(FLAC__STREAM_DECODER_SEEK_ERROR != FLAC__stream_decoder_get_state(m_decoder));
        } else {
            // Failure
            qWarning() << "Seek error at" << seekFrameIndex << "in" << m_file.fileName();
            if (FLAC__STREAM_DECODER_SEEK_ERROR == FLAC__stream_decoder_get_state(m_decoder)) {
                // Flush the input stream of the decoder according to the
                // documentation of FLAC__stream_decoder_seek_absolute()
                if (!FLAC__stream_decoder_flush(m_decoder)) {
                    qWarning() << "Failed to flush input buffer of the FLAC decoder after seek failure in"
                            << m_file.fileName();
                    // Invalidate the current position again...
                    m_curFrameIndex = getMaxFrameIndex();
                    // ...and abort
                    return m_curFrameIndex;
                }
            }
            if (getMinFrameIndex() < seekFrameIndex) {
                // The next seek position should start at a preceding sample block.
                // By subtracting max. blocksize from the current seek position it
                // is guaranteed that the targeted sample blocks of subsequent seek
                // operations will differ.
                DEBUG_ASSERT(0 < m_maxBlocksize);
                seekFrameIndex -= m_maxBlocksize;
                if (seekFrameIndex < getMinFrameIndex()) {
                    seekFrameIndex = getMinFrameIndex();
                }
            } else {
                // We have already reached the beginning of the file
                // and cannot move the seek position backward any
                // further!
                break; // exit loop
            }
        }
    } while (m_curFrameIndex != seekFrameIndex);
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));

    if (frameIndex > m_curFrameIndex) {
        // Adjust the current position
        skipSampleFrames(frameIndex - m_curFrameIndex);
    }

    return m_curFrameIndex;
}