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; }
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; }
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; }
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; }
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, ¤tSection); 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; }
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; }
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; }
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); } }
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; }
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; }
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; }
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); } }
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; }
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; }
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; }
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); } }
SINT SoundSourceModPlug::seekSampleFrame( SINT frameIndex) { DEBUG_ASSERT(isValidFrameIndex(frameIndex)); return m_seekPos = frameIndex; }
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))); }
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; }
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; }
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; }
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); }
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; }
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); }
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; }
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, ¤tSection); 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)))); }