Пример #1
0
SINT SoundSourceOpus::readSampleFramesStereo(
        SINT numberOfFrames, CSAMPLE* sampleBuffer,
        SINT sampleBufferSize) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(getSampleBufferSize(numberOfFrames, true) <= sampleBufferSize);

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

    CSAMPLE* pSampleBuffer = sampleBuffer;
    SINT numberOfFramesRemaining = numberOfFramesTotal;
    while (0 < numberOfFramesRemaining) {
        int readResult = op_read_float_stereo(m_pOggOpusFile,
                pSampleBuffer,
                numberOfFramesRemaining * 2); // stereo
        if (0 < readResult) {
            m_curFrameIndex += readResult;
            pSampleBuffer += readResult * 2; // stereo
            numberOfFramesRemaining -= readResult;
        } else {
            qWarning() << "Failed to read sample data from OggOpus file:"
                    << readResult;
            break; // abort
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfFramesTotal >= numberOfFramesRemaining);
    return numberOfFramesTotal - numberOfFramesRemaining;
}
Пример #2
0
SINT SoundSourceOpus::readSampleFrames(
        SINT numberOfFrames, CSAMPLE* sampleBuffer) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));

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

    CSAMPLE* pSampleBuffer = sampleBuffer;
    SINT numberOfFramesRemaining = numberOfFramesTotal;
    while (0 < numberOfFramesRemaining) {
        int readResult = op_read_float(m_pOggOpusFile,
                pSampleBuffer,
                frames2samples(numberOfFramesRemaining), NULL);
        if (0 < readResult) {
            m_curFrameIndex += readResult;
            pSampleBuffer += frames2samples(readResult);
            numberOfFramesRemaining -= readResult;
        } else {
            qWarning() << "Failed to read sample data from OggOpus file:"
                    << readResult;
            break; // abort
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfFramesTotal >= numberOfFramesRemaining);
    return numberOfFramesTotal - numberOfFramesRemaining;
}
Пример #3
0
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;
}
Пример #4
0
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;
}
Пример #5
0
SINT SoundSourceOggVorbis::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);

    CSAMPLE* pSampleBuffer = sampleBuffer;
    SINT numberOfFramesRemaining = numberOfFramesTotal;
    while (0 < numberOfFramesRemaining) {
        float** pcmChannels;
        int currentSection;
        // Use 'long' here, because ov_read_float() returns this type.
        // This is an exception from the rule not to any types with
        // differing sizes on different platforms.
        // https://bugs.launchpad.net/mixxx/+bug/1094143
        const long readResult = ov_read_float(&m_vf, &pcmChannels,
                numberOfFramesRemaining, &currentSection);
        if (0 < readResult) {
            m_curFrameIndex += readResult;
            if (kChannelCountMono == getChannelCount()) {
                if (readStereoSamples) {
                    for (long i = 0; i < readResult; ++i) {
                        *pSampleBuffer++ = pcmChannels[0][i];
                        *pSampleBuffer++ = pcmChannels[0][i];
                    }
                } else {
                    for (long i = 0; i < readResult; ++i) {
                        *pSampleBuffer++ = pcmChannels[0][i];
                    }
                }
            } else if (readStereoSamples || (kChannelCountStereo == getChannelCount())) {
                for (long i = 0; i < readResult; ++i) {
                    *pSampleBuffer++ = pcmChannels[0][i];
                    *pSampleBuffer++ = pcmChannels[1][i];
                }
            } else {
                for (long i = 0; i < readResult; ++i) {
                    for (SINT j = 0; j < getChannelCount(); ++j) {
                        *pSampleBuffer++ = pcmChannels[j][i];
                    }
                }
            }
            numberOfFramesRemaining -= readResult;
        } else {
            qWarning() << "Failed to read from OggVorbis file:" << readResult;
            break; // abort
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfFramesTotal >= numberOfFramesRemaining);
    return numberOfFramesTotal - numberOfFramesRemaining;
}
Пример #6
0
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;
}
Пример #7
0
SINT SoundSourceFFmpeg::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    int ret = 0;
    qint64 i = 0;

    if (frameIndex < 0 || frameIndex < m_lCacheStartFrame) {
        ret = avformat_seek_file(m_pFormatCtx,
                                 m_iAudioStream,
                                 0,
                                 32767 * 2,
                                 32767 * 2,
                                 AVSEEK_FLAG_BACKWARD);


        if (ret < 0) {
            qDebug() << "SoundSourceFFmpeg::seek: Can't seek to 0 byte!";
            return -1;
        }

        clearCache();
        m_lCacheStartFrame = 0;
        m_lCacheEndFrame = 0;
        m_lCacheLastPos = 0;
        m_lCacheFramePos = 0;
        m_lStoredSeekPoint = -1;


        // Try to find some jump point near to
        // where we are located so we don't needed
        // to try guess it
        if (frameIndex >= AUDIOSOURCEFFMPEG_POSDISTANCE) {
            for (i = 0; i < m_SJumpPoints.size(); i ++) {
                if (m_SJumpPoints[i]->startFrame >= frameIndex && i > 2) {
                    m_lCacheFramePos = m_SJumpPoints[i - 2]->startFrame * 2;
                    m_lStoredSeekPoint = m_SJumpPoints[i - 2]->pos;
                    break;
                }
            }
        }

        if (frameIndex == 0) {
            readFramesToCache((AUDIOSOURCEFFMPEG_CACHESIZE - 50), -1);
        } else {
            readFramesToCache((AUDIOSOURCEFFMPEG_CACHESIZE / 2), frameIndex);
        }
    }


    if (m_lCacheEndFrame <= frameIndex) {
        readFramesToCache(100, frameIndex);
    }

    m_currentMixxxFrameIndex = frameIndex;

    m_bIsSeeked = TRUE;

    return frameIndex;
}
Пример #8
0
SINT SoundSourceWV::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));
    if (WavpackSeekSample(m_wpc, frameIndex) == true) {
        return frameIndex;
    } else {
        qDebug() << "SSWV::seek : could not seek to frame #" << frameIndex;
        return WavpackGetSampleIndex(m_wpc);
    }
}
Пример #9
0
SINT SoundSourceOpus::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    int seekResult = op_pcm_seek(m_pOggOpusFile, frameIndex);
    if (0 == seekResult) {
        m_curFrameIndex = frameIndex;
    } else {
        qWarning() << "Failed to seek OggOpus file:" << seekResult;
        const ogg_int64_t pcmOffset = op_pcm_tell(m_pOggOpusFile);
        if (0 <= pcmOffset) {
            m_curFrameIndex = pcmOffset;
        } else {
            // Reset to EOF
            m_curFrameIndex = getMaxFrameIndex();
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    return m_curFrameIndex;
}
Пример #10
0
SINT SoundSourceOggVorbis::seekSampleFrame(
        SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    const int seekResult = ov_pcm_seek(&m_vf, frameIndex);
    if (0 == seekResult) {
        m_curFrameIndex = frameIndex;
    } else {
        qWarning() << "Failed to seek OggVorbis file:" << seekResult;
        const ogg_int64_t pcmOffset = ov_pcm_tell(&m_vf);
        if (0 <= pcmOffset) {
            m_curFrameIndex = pcmOffset;
        } else {
            // Reset to EOF
            m_curFrameIndex = getFrameIndexMax();
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    return m_curFrameIndex;
}
Пример #11
0
SINT SoundSourceModPlug::readSampleFrames(
        SINT numberOfFrames, CSAMPLE* sampleBuffer) {
    DEBUG_ASSERT(0 <= numberOfFrames);
    DEBUG_ASSERT(isValidFrameIndex(m_seekPos));
    const SINT readFrames = math_min(getFrameCount() - m_seekPos, numberOfFrames);

    const SINT readSamples = frames2samples(readFrames);
    const SINT readOffset = frames2samples(m_seekPos);
    SampleUtil::convertS16ToFloat32(sampleBuffer, &m_sampleBuf[readOffset], readSamples);
    m_seekPos += readFrames;

    return readFrames;
}
Пример #12
0
SINT SoundSourceSndFile::seekSampleFrame(
        SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    const sf_count_t seekResult = sf_seek(m_pSndFile, frameIndex, SEEK_SET);
    if (0 <= seekResult) {
        return seekResult;
    } else {
        qWarning() << "Failed to seek libsnd file:" << seekResult
                << sf_strerror(m_pSndFile);
        return sf_seek(m_pSndFile, 0, SEEK_CUR);
    }
}
Пример #13
0
SINT SoundSourceMediaFoundation::seekSampleFrame(
        SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));
    if (sDebug) {
        qDebug() << "seekSampleFrame()" << frameIndex;
    }
    qint64 mfSeekTarget(mfFromFrame(frameIndex, getSamplingRate()) - 1);
    // minus 1 here seems to make our seeking work properly, otherwise we will
    // (more often than not, maybe always) seek a bit too far (although not
    // enough for our calculatedFrameFromMF <= nextFrame assertion in ::read).
    // Has something to do with 100ns MF units being much smaller than most
    // frame offsets (in seconds) -bkgood
    SINT result = m_iCurrentPosition;
    if (m_dead) {
        return result;
    }

    PROPVARIANT prop;
    HRESULT hr;

    // this doesn't fail, see MS's implementation
    hr = InitPropVariantFromInt64(mfSeekTarget < 0 ? 0 : mfSeekTarget, &prop);

    hr = m_pReader->Flush(MF_SOURCE_READER_FIRST_AUDIO_STREAM);
    if (FAILED(hr)) {
        qWarning() << "SSMF: failed to flush before seek";
    }

    // http://msdn.microsoft.com/en-us/library/dd374668(v=VS.85).aspx
    hr = m_pReader->SetCurrentPosition(GUID_NULL, prop);
    if (FAILED(hr)) {
        // nothing we can do here as we can't fail (no facility to other than
        // crashing mixxx)
        qWarning() << "SSMF: failed to seek"
                << (hr == MF_E_INVALIDREQUEST ?
                        "Sample requests still pending" : "");
    } else {
        result = frameIndex;
    }
    PropVariantClear(&prop);

    // record the next frame so that we can make sure we're there the next
    // time we get a buffer from MFSourceReader
    m_nextFrame = frameIndex;
    m_seeking = true;
    m_iCurrentPosition = result;
    return result;
}
Пример #14
0
SINT SoundSourceCoreAudio::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    // See comments above on kMp3StabilizationFrames.
    const SINT stabilization_frames = m_bFileIsMp3 ? math_min<SINT>(
            kMp3StabilizationFrames, frameIndex + m_headerFrames) : 0;
    OSStatus err = ExtAudioFileSeek(
            m_audioFile, frameIndex + m_headerFrames - stabilization_frames);
    if (stabilization_frames > 0) {
        readSampleFrames(stabilization_frames,
                         &kMp3StabilizationScratchBuffer[0]);
    }

    //_ThrowExceptionIfErr(@"ExtAudioFileSeek", err);
    //qDebug() << "SSCA: Seeking to" << frameIndex;
    if (err != noErr) {
        qDebug() << "SSCA: Error seeking to" << frameIndex; // << GetMacOSStatusErrorString(err) << GetMacOSStatusCommentString(err);
    }
    return frameIndex;
}
Пример #15
0
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;
}
Пример #16
0
SINT SoundSourceWV::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));

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

    if (frameIndex == m_curFrameIndex) {
        return m_curFrameIndex;
    }

    if (WavpackSeekSample(m_wpc, frameIndex) == true) {
        m_curFrameIndex = frameIndex;
        return frameIndex;
    } else {
        kLogger.debug() << "could not seek to frame #" << frameIndex;
        return WavpackGetSampleIndex(m_wpc);
    }
}
Пример #17
0
SINT SoundSourceModPlug::seekSampleFrame(
        SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    return m_seekPos = frameIndex;
}
Пример #18
0
ReadableSampleFrames SoundSourceFLAC::readSampleFramesClamped(
        WritableSampleFrames writableSampleFrames) {
    const SINT firstFrameIndex = writableSampleFrames.frameIndexRange().start();

    if (m_curFrameIndex != firstFrameIndex) {
        // Seek to the new position
        SINT seekFrameIndex = firstFrameIndex;
        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.clear();
            invalidateCurFrameIndex();
            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
                kLogger.warning()
                        << "Seek error at" << seekFrameIndex
                        << "in file" << 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)) {
                        kLogger.warning()
                                << "Failed to flush input buffer of the FLAC decoder after seek failure"
                                << "in file" << m_file.fileName();
                        invalidateCurFrameIndex();
                        // ...and abort
                        return ReadableSampleFrames(
                                IndexRange::between(
                                        m_curFrameIndex,
                                        m_curFrameIndex));
                    }
                }
                if (frameIndexMin() < 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 < frameIndexMin()) {
                        seekFrameIndex = frameIndexMin();
                    }
                } else {
                    // We have already reached the beginning of the file
                    // and cannot move the seek position backwards any
                    // further!
                    break; // exit loop
                }
            }
        }

        // Decoding starts before the actual target position
        DEBUG_ASSERT(m_curFrameIndex <= firstFrameIndex);
        const auto precedingFrames =
                IndexRange::between(m_curFrameIndex, firstFrameIndex);
        if (!precedingFrames.empty() &&
                (precedingFrames != readSampleFramesClamped(WritableSampleFrames(precedingFrames)).frameIndexRange())) {
            kLogger.warning()
                    << "Failed to skip preceding frames"
                    << precedingFrames;
            // Abort
            return ReadableSampleFrames(
                    IndexRange::between(
                            m_curFrameIndex,
                            m_curFrameIndex));
        }
    }
    DEBUG_ASSERT(m_curFrameIndex == firstFrameIndex);

    const SINT numberOfSamplesTotal = frames2samples(writableSampleFrames.frameLength());

    SINT numberOfSamplesRemaining = numberOfSamplesTotal;
    SINT outputSampleOffset = 0;
    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.empty()) {
            // 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)) {
                kLogger.warning()
                        << "Failed to decode FLAC file"
                        << m_file.fileName();
                break; // abort
            }
            // After decoding we might first need to skip some samples if the
            // decoder complained that it has lost sync for some malformed(?)
            // files
            if (m_curFrameIndex != curFrameIndexBeforeProcessing) {
                if (m_curFrameIndex < curFrameIndexBeforeProcessing) {
                    kLogger.warning()
                            << "Trying to adjust frame index"
                            << m_curFrameIndex << "<" << curFrameIndexBeforeProcessing
                            << "while decoding FLAC file"
                            << m_file.fileName();
                    const auto skipFrames =
                            IndexRange::between(m_curFrameIndex, curFrameIndexBeforeProcessing);
                    if (skipFrames != readSampleFramesClamped(WritableSampleFrames(skipFrames)).frameIndexRange()) {
                        kLogger.warning()
                                << "Failed to skip sample frames"
                                << skipFrames
                                << "while decoding FLAC file"
                                << m_file.fileName();
                        break; // abort
                    }
                } else {
                    kLogger.warning()
                            << "Unexpected frame index"
                            << m_curFrameIndex << ">" << curFrameIndexBeforeProcessing
                            << "while decoding FLAC file"
                            << m_file.fileName();
                    break; // abort
                }
            }
            DEBUG_ASSERT(curFrameIndexBeforeProcessing == m_curFrameIndex);
        }
        if (m_sampleBuffer.empty()) {
            break; // EOF
        }

        const SINT numberOfSamplesRead =
                std::min(m_sampleBuffer.readableLength(), numberOfSamplesRemaining);
        const SampleBuffer::ReadableSlice readableSlice(
                m_sampleBuffer.shrinkForReading(numberOfSamplesRead));
        DEBUG_ASSERT(readableSlice.length() == numberOfSamplesRead);
        if (writableSampleFrames.writableData()) {
            SampleUtil::copy(
                    writableSampleFrames.writableData(outputSampleOffset),
                    readableSlice.data(),
                    readableSlice.length());
            outputSampleOffset += numberOfSamplesRead;
        }
        m_curFrameIndex += samples2frames(numberOfSamplesRead);
        numberOfSamplesRemaining -= numberOfSamplesRead;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfSamplesTotal >= numberOfSamplesRemaining);
    const SINT numberOfSamples = numberOfSamplesTotal - numberOfSamplesRemaining;
    return ReadableSampleFrames(
            IndexRange::forward(firstFrameIndex, samples2frames(numberOfSamples)),
            SampleBuffer::ReadableSlice(
                    writableSampleFrames.writableData(),
                    std::min(writableSampleFrames.writableLength(), numberOfSamples)));
}
Пример #19
0
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;
}
Пример #20
0
SINT SoundSourceMp3::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);

    CSAMPLE* pSampleBuffer = sampleBuffer;
    SINT numberOfFramesRemaining = numberOfFramesTotal;
    while (0 < numberOfFramesRemaining) {
        if (0 >= m_madSynthCount) {
            // When all decoded output data has been consumed...
            DEBUG_ASSERT(0 == m_madSynthCount);
            // ...decode the next MP3 frame
            DEBUG_ASSERT(m_madStream.buffer);
            DEBUG_ASSERT(m_madStream.this_frame);

            // WARNING: Correctly evaluating and handling the result
            // of mad_frame_decode() has proven to be extremely tricky.
            // Don't change anything at the following lines of code
            // unless you know what you are doing!!!
            unsigned char const* pMadThisFrame = m_madStream.this_frame;
            if (mad_frame_decode(&m_madFrame, &m_madStream)) {
                // Something went wrong when decoding the frame...
                if (MAD_ERROR_BUFLEN == m_madStream.error) {
                    // Abort
                    break;
                }
                if (isUnrecoverableError(m_madStream)) {
                    qWarning() << "Unrecoverable MP3 frame decoding error:"
                            << mad_stream_errorstr(&m_madStream);
                    // Abort decoding
                    break;
                }
                if (isRecoverableError(m_madStream)) {
                    if (pMadThisFrame != m_madStream.this_frame) {
                        // Ignore all recoverable errors (and especially
                        // "lost synchronization" warnings) while skipping
                        // over prefetched frames after seeking.
                        if (pSampleBuffer) {
                            qWarning() << "Recoverable MP3 frame decoding error:"
                                    << mad_stream_errorstr(&m_madStream);
                        } else {
                            // Decoded samples will simply be discarded
                            qDebug() << "Recoverable MP3 frame decoding error while skipping:"
                                << mad_stream_errorstr(&m_madStream);
                        }
                    }
                    // Continue decoding
                }
            }
            if (pMadThisFrame == m_madStream.this_frame) {
                qDebug() << "Retry decoding MP3 frame @" << m_curFrameIndex;
                // Retry decoding
                continue;
            }

            DEBUG_ASSERT(isStreamValid(m_madStream));

#ifndef QT_NO_DEBUG_OUTPUT
            const SINT madFrameChannelCount = MAD_NCHANNELS(&m_madFrame.header);
            if (madFrameChannelCount != getChannelCount()) {
                qDebug() << "MP3 frame header with mismatching number of channels"
                        << madFrameChannelCount << "<>" << getChannelCount();
            }
#endif

            // Once decoded the frame is synthesized to PCM samples
            mad_synth_frame(&m_madSynth, &m_madFrame);
#ifndef QT_NO_DEBUG_OUTPUT
            const SINT madSynthSampleRate =  m_madSynth.pcm.samplerate;
            if (madSynthSampleRate != getSamplingRate()) {
                qDebug() << "Reading MP3 data with different sampling rate"
                        << madSynthSampleRate << "<>" << getSamplingRate();
            }
#endif
            m_madSynthCount = m_madSynth.pcm.length;
            DEBUG_ASSERT(0 < m_madSynthCount);
        }

        const SINT synthReadCount = math_min(
                m_madSynthCount, numberOfFramesRemaining);
        if (pSampleBuffer) {
            DEBUG_ASSERT(m_madSynthCount <= m_madSynth.pcm.length);
            const SINT madSynthOffset =
                    m_madSynth.pcm.length - m_madSynthCount;
            DEBUG_ASSERT(madSynthOffset < m_madSynth.pcm.length);
            const SINT madSynthChannelCount = m_madSynth.pcm.channels;
            DEBUG_ASSERT(0 < madSynthChannelCount);
            DEBUG_ASSERT(madSynthChannelCount <= getChannelCount());
#ifndef QT_NO_DEBUG_OUTPUT
            if (madSynthChannelCount != getChannelCount()) {
                qDebug() << "Reading MP3 data with different number of channels"
                        << madSynthChannelCount << "<>" << getChannelCount();
            }
#endif
            if (kChannelCountMono == madSynthChannelCount) {
                // MP3 frame contains a mono signal
                if (readStereoSamples || (kChannelCountStereo == getChannelCount())) {
                    // The reader explicitly requested a stereo signal
                    // or the AudioSource itself provides a stereo signal.
                    // Mono -> Stereo: Copy 1st channel twice
                    for (SINT i = 0; i < synthReadCount; ++i) {
                        const CSAMPLE sampleValue = madScaleSampleValue(
                                m_madSynth.pcm.samples[0][madSynthOffset + i]);
                        *pSampleBuffer++ = sampleValue;
                        *pSampleBuffer++ = sampleValue;
                    }
                } else {
                    // Mono -> Mono: Copy 1st channel
                    for (SINT i = 0; i < synthReadCount; ++i) {
                        const CSAMPLE sampleValue = madScaleSampleValue(
                                m_madSynth.pcm.samples[0][madSynthOffset + i]);
                        *pSampleBuffer++ = sampleValue;
                    }
                }
            } else {
                // MP3 frame contains a stereo signal
                DEBUG_ASSERT(kChannelCountStereo == madSynthChannelCount);
                // If the MP3 frame contains a stereo signal then the whole
                // AudioSource must also provide 2 channels, because the
                // maximum channel count of all MP3 frames is used.
                DEBUG_ASSERT(kChannelCountStereo == getChannelCount());
                // Stereo -> Stereo: Copy 1st + 2nd channel
                for (SINT i = 0; i < synthReadCount; ++i) {
                    *pSampleBuffer++ = madScaleSampleValue(
                            m_madSynth.pcm.samples[0][madSynthOffset + i]);
                    *pSampleBuffer++ = madScaleSampleValue(
                            m_madSynth.pcm.samples[1][madSynthOffset + i]);
                }
            }
        }
        // consume decoded output data
        m_madSynthCount -= synthReadCount;
        m_curFrameIndex += synthReadCount;
        numberOfFramesRemaining -= synthReadCount;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfFramesTotal >= numberOfFramesRemaining);
    return numberOfFramesTotal - numberOfFramesRemaining;
}
Пример #21
0
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;
}
Пример #22
0
SINT SoundSourceM4A::readSampleFrames(
        SINT numberOfFrames, CSAMPLE* sampleBuffer) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));

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

    CSAMPLE* pSampleBuffer = sampleBuffer;
    SINT numberOfSamplesRemaining = numberOfSamplesTotal;
    while (0 < numberOfSamplesRemaining) {

        if (!m_sampleBuffer.isEmpty()) {
            // Consume previously decoded sample data
            const SampleBuffer::ReadableChunk readableChunk(
                    m_sampleBuffer.readFromHead(numberOfSamplesRemaining));
            if (pSampleBuffer) {
                SampleUtil::copy(pSampleBuffer, readableChunk.data(), readableChunk.size());
                pSampleBuffer += readableChunk.size();
            }
            m_curFrameIndex += samples2frames(readableChunk.size());
            DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
            DEBUG_ASSERT(numberOfSamplesRemaining >= readableChunk.size());
            numberOfSamplesRemaining -= readableChunk.size();
            if (0 == numberOfSamplesRemaining) {
                break; // exit loop
            }
        }
        // All previously decoded sample data has been consumed now
        DEBUG_ASSERT(m_sampleBuffer.isEmpty());

        if (0 == m_inputBufferLength) {
            // Fill input buffer from file
            if (isValidSampleBlockId(m_curSampleBlockId)) {
                // Read data for next sample block into input buffer
                u_int8_t* pInputBuffer = &m_inputBuffer[0];
                u_int32_t inputBufferLength = m_inputBuffer.size(); // in/out parameter
                if (!MP4ReadSample(m_hFile, m_trackId, m_curSampleBlockId,
                        &pInputBuffer, &inputBufferLength,
                        NULL, NULL, NULL, NULL)) {
                    qWarning()
                            << "Failed to read MP4 input data for sample block"
                            << m_curSampleBlockId << "(" << "min ="
                            << kSampleBlockIdMin << "," << "max ="
                            << m_maxSampleBlockId << ")";
                    break; // abort
                }
                ++m_curSampleBlockId;
                m_inputBufferLength = inputBufferLength;
                m_inputBufferOffset = 0;
            }
        }
        DEBUG_ASSERT(0 <= m_inputBufferLength);
        if (0 == m_inputBufferLength) {
            break; // EOF
        }

        // NOTE(uklotzde): The sample buffer for NeAACDecDecode2 has to
        // be big enough for a whole block of decoded samples, which
        // contains up to kFramesPerSampleBlock frames. Otherwise
        // we need to use a temporary buffer.
        CSAMPLE* pDecodeBuffer; // in/out parameter
        SINT decodeBufferCapacity;
        const SINT decodeBufferCapacityMin = frames2samples(kFramesPerSampleBlock);
        if (pSampleBuffer && (decodeBufferCapacityMin <= numberOfSamplesRemaining)) {
            // Decode samples directly into sampleBuffer
            pDecodeBuffer = pSampleBuffer;
            decodeBufferCapacity = numberOfSamplesRemaining;
        } else {
            // Decode next sample block into temporary buffer
            const SINT writeToTailCount = math_max(
                    numberOfSamplesRemaining, decodeBufferCapacityMin);
            const SampleBuffer::WritableChunk writableChunk(
                    m_sampleBuffer.writeToTail(writeToTailCount));
            pDecodeBuffer = writableChunk.data();
            decodeBufferCapacity = writableChunk.size();
        }
        DEBUG_ASSERT(decodeBufferCapacityMin <= decodeBufferCapacity);

        NeAACDecFrameInfo decFrameInfo;
        void* pDecodeResult = NeAACDecDecode2(
                m_hDecoder, &decFrameInfo,
                &m_inputBuffer[m_inputBufferOffset],
                m_inputBufferLength,
                reinterpret_cast<void**>(&pDecodeBuffer),
                decodeBufferCapacity * sizeof(*pDecodeBuffer));
        // Verify the decoding result
        if (0 != decFrameInfo.error) {
            qWarning() << "AAC decoding error:"
                    << decFrameInfo.error
                    << NeAACDecGetErrorMessage(decFrameInfo.error)
                    << getUrlString();
            break; // abort
        }
        DEBUG_ASSERT(pDecodeResult == pDecodeBuffer); // verify the in/out parameter

        // Verify the decoded sample data for consistency
        if (getChannelCount() != decFrameInfo.channels) {
            qWarning() << "Corrupt or unsupported AAC file:"
                    << "Unexpected number of channels" << decFrameInfo.channels
                    << "<>" << getChannelCount();
            break; // abort
        }
        if (getFrameRate() != SINT(decFrameInfo.samplerate)) {
            qWarning() << "Corrupt or unsupported AAC file:"
                    << "Unexpected sample rate" << decFrameInfo.samplerate
                    << "<>" << getFrameRate();
            break; // abort
        }

        // Consume input data
        m_inputBufferLength -= decFrameInfo.bytesconsumed;
        m_inputBufferOffset += decFrameInfo.bytesconsumed;

        // Consume decoded output data
        const SINT numberOfSamplesDecoded = decFrameInfo.samples;
        DEBUG_ASSERT(numberOfSamplesDecoded <= decodeBufferCapacity);
        SINT numberOfSamplesRead;
        if (pDecodeBuffer == pSampleBuffer) {
            numberOfSamplesRead = math_min(numberOfSamplesDecoded, numberOfSamplesRemaining);
            pSampleBuffer += numberOfSamplesRead;
        } else {
            m_sampleBuffer.readFromTail(decodeBufferCapacity - numberOfSamplesDecoded);
            const SampleBuffer::ReadableChunk readableChunk(
                    m_sampleBuffer.readFromHead(numberOfSamplesRemaining));
            numberOfSamplesRead = readableChunk.size();
            if (pSampleBuffer) {
                SampleUtil::copy(pSampleBuffer, readableChunk.data(), numberOfSamplesRead);
                pSampleBuffer += numberOfSamplesRead;
            }
        }
        // The decoder might decode more samples than actually needed
        // at the end of the file! When the end of the file has been
        // reached decoding can be restarted by seeking to a new
        // position.
        DEBUG_ASSERT(numberOfSamplesDecoded >= numberOfSamplesRead);
        m_curFrameIndex += samples2frames(numberOfSamplesRead);
        DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
        DEBUG_ASSERT(numberOfSamplesRemaining >= numberOfSamplesRead);
        numberOfSamplesRemaining -= numberOfSamplesRead;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfSamplesTotal >= numberOfSamplesRemaining);
    return samples2frames(numberOfSamplesTotal - numberOfSamplesRemaining);
}
Пример #23
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;
}
Пример #24
0
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);
}
Пример #25
0
SINT SoundSourceFFmpeg::seekSampleFrame(SINT frameIndex) {
    DEBUG_ASSERT(isValidFrameIndex(frameIndex));

    int ret = 0;
    qint64 i = 0;
    struct ffmpegLocationObject *l_STestObj = nullptr;

    if (frameIndex < 0 || frameIndex < m_lCacheStartFrame) {
        // Seek to set (start of the stream which is FFmpeg frame 0)
        // because we are dealing with compressed audio FFmpeg takes
        // best of to seek that point (in this case 0 Is always there)
        // in every other case we should provide MIN and MAX tolerance
        // which we can take.
        // FFmpeg just just can't take zero as MAX tolerance so we try to
        // just make some tolerable (which is never used because zero point
        // should always be there) some number (which is 0xffff 65535)
        // that is chosen because in WMA frames can be that big and if it's
        // smaller than the frame we are seeking we can get into error
        ret = avformat_seek_file(m_pFormatCtx,
                                 m_iAudioStream,
                                 0,
                                 0,
                                 0xffff,
                                 AVSEEK_FLAG_BACKWARD);

        if (ret < 0) {
            qDebug() << "SoundSourceFFmpeg::seek: Can't seek to 0 byte!";
            return -1;
        }

        clearCache();
        m_lCacheStartFrame = 0;
        m_lCacheEndFrame = 0;
        m_lCacheLastPos = 0;
        m_lCacheFramePos = 0;
        m_lStoredSeekPoint = -1;


        // Try to find some jump point near to
        // where we are located so we don't needed
        // to try guess it
        if (m_SJumpPoints.size() > 0) {
            l_STestObj = m_SJumpPoints.first();

            if (frameIndex > l_STestObj->startFrame) {
                for (i = 0; i < m_SJumpPoints.size(); i++) {
                    if (m_SJumpPoints[i]->startFrame >= frameIndex) {
                        if (i > 0) {
                            i--;
                        }

                        m_lCacheFramePos = m_SJumpPoints[i]->startFrame;
                        m_lStoredSeekPoint = m_SJumpPoints[i]->pos;
                        m_SStoredJumpPoint = m_SJumpPoints[i];
                        break;
                    }
                }
            }
        }

        if (frameIndex == 0) {
            // Because we are in the beginning just read cache full
            // but leave 50 of just in case
            // -1 one means we are seeking from current position and
            // filling the cache
            readFramesToCache((AUDIOSOURCEFFMPEG_CACHESIZE - 50),
                              AUDIOSOURCEFFMPEG_FILL_FROM_CURRENTPOS);
        }
    }

    if (m_lCacheEndFrame <= frameIndex) {
        // Cache tries to read until it gets to frameIndex
        // after that we still read 100 FFmpeg frames to memory
        // so we have good cache to go forward (100) and backward (900)
        // from the point
        readFramesToCache(100, frameIndex);
    }

    m_currentMixxxFrameIndex = frameIndex;

    m_bIsSeeked = true;

    return frameIndex;
}
Пример #26
0
ReadableSampleFrames SoundSourceOggVorbis::readSampleFramesClamped(
        WritableSampleFrames writableSampleFrames) {

    const SINT firstFrameIndex = writableSampleFrames.frameIndexRange().start();

    if (m_curFrameIndex != firstFrameIndex) {
        const int seekResult = ov_pcm_seek(&m_vf, firstFrameIndex);
        if (seekResult == 0) {
            m_curFrameIndex = firstFrameIndex;
        } else {
            kLogger.warning() << "Failed to seek file:" << seekResult;
            const ogg_int64_t pcmOffset = ov_pcm_tell(&m_vf);
            if (0 <= pcmOffset) {
                m_curFrameIndex = pcmOffset;
            } else {
                // Reset to EOF
                m_curFrameIndex = frameIndexMax();
            }
            // Abort
            return ReadableSampleFrames(
                    IndexRange::between(
                            m_curFrameIndex,
                            m_curFrameIndex));
        }
    }
    DEBUG_ASSERT(m_curFrameIndex == firstFrameIndex);

    const SINT numberOfFramesTotal = writableSampleFrames.frameLength();

    CSAMPLE* pSampleBuffer = writableSampleFrames.writableData();
    SINT numberOfFramesRemaining = numberOfFramesTotal;
    while (0 < numberOfFramesRemaining) {
        float** pcmChannels;
        int currentSection;
        // Use 'long' here, because ov_read_float() returns this type.
        // This is an exception from the rule not to any types with
        // differing sizes on different platforms.
        // https://bugs.launchpad.net/mixxx/+bug/1094143
        const long readResult = ov_read_float(&m_vf, &pcmChannels,
                numberOfFramesRemaining, &currentSection);
        if (0 < readResult) {
            m_curFrameIndex += readResult;
            if (pSampleBuffer) {
                switch (channelCount()) {
                case 1:
                    for (long i = 0; i < readResult; ++i) {
                        *pSampleBuffer++ = pcmChannels[0][i];
                    }
                    break;
                case 2:
                    for (long i = 0; i < readResult; ++i) {
                        *pSampleBuffer++ = pcmChannels[0][i];
                        *pSampleBuffer++ = pcmChannels[1][i];
                    }
                    break;
                default:
                    for (long i = 0; i < readResult; ++i) {
                        for (SINT j = 0; j < channelCount(); ++j) {
                            *pSampleBuffer++ = pcmChannels[j][i];
                        }
                    }
                }
            }
            numberOfFramesRemaining -= readResult;
        } else {
            kLogger.warning() << "Failed to read from file:" << readResult;
            break; // abort
        }
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfFramesTotal >= numberOfFramesRemaining);
    const SINT numberOfFrames = numberOfFramesTotal - numberOfFramesRemaining;
    return ReadableSampleFrames(
            IndexRange::forward(firstFrameIndex, numberOfFrames),
            SampleBuffer::ReadableSlice(
                    writableSampleFrames.writableData(),
                    std::min(writableSampleFrames.writableLength(), frames2samples(numberOfFrames))));
}