void EventHeader::toStream( std::ostream &stream ) { stream.write( reinterpret_cast<const char*>(&m_mode), 1 ); // convert number of events to 24bit little endian representation uint32 numev = getNumEvents( ); uint8 tmp[3]; tmp[0] = numev % 256; tmp[1] = (numev / 256) % 256; tmp[2] = (numev / 65536) % 256; stream.write( reinterpret_cast<const char*>(tmp), 3 ); writeLittleEndian( stream, getSamplingRate( ) ); if( getMode() == 1 ) { Mode1Event e; // write event positions for( size_t i=0; i<getNumEvents(); i++) { getEvent( i, e ); writeLittleEndian( stream, e.position ); } // write event types for( size_t i=0; i<getNumEvents(); i++) { getEvent( i, e ); writeLittleEndian( stream, e.type ); } } else if( getMode() == 3 ) { Mode3Event e; // write event positions for( size_t i=0; i<getNumEvents(); i++) { getEvent( i, e ); writeLittleEndian( stream, e.position ); } // write event types for( size_t i=0; i<getNumEvents(); i++) { getEvent( i, e ); writeLittleEndian( stream, e.type ); } // write event channels for( size_t i=0; i<getNumEvents(); i++) { getEvent( i, e ); writeLittleEndian( stream, e.channel ); } // write event durations for( size_t i=0; i<getNumEvents(); i++) { getEvent( i, e ); writeLittleEndian( stream, e.duration ); } } }
status_t AudioSystem::getOutputSamplingRateForAttr(uint32_t* samplingRate, const audio_attributes_t *attr) { if (attr == NULL) { return BAD_VALUE; } audio_io_handle_t output = getOutputForAttr(attr); if (output == 0) { return PERMISSION_DENIED; } return getSamplingRate(output, samplingRate); }
bool SoundSourceMediaFoundation::readProperties() { PROPVARIANT prop; HRESULT hr = S_OK; //Get the duration, provided as a 64-bit integer of 100-nanosecond units hr = m_pReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &prop); if (FAILED(hr)) { qWarning() << "SSMF: error getting duration"; return false; } m_mfDuration = prop.hVal.QuadPart; setFrameCount(frameFromMF(m_mfDuration, getSamplingRate())); qDebug() << "SSMF: Frame count" << getFrameCount(); PropVariantClear(&prop); // presentation attribute MF_PD_AUDIO_ENCODING_BITRATE only exists for // presentation descriptors, one of which MFSourceReader is not. // Therefore, we calculate it ourselves. setBitrate(kBitsPerSample * frames2samples(getSamplingRate())); return true; }
status_t AudioSystem::getOutputSamplingRate(uint32_t* samplingRate, audio_stream_type_t streamType) { audio_io_handle_t output; if (streamType == AUDIO_STREAM_DEFAULT) { streamType = AUDIO_STREAM_MUSIC; } output = getOutput(streamType); if (output == 0) { return PERMISSION_DENIED; } return getSamplingRate(output, streamType, samplingRate); }
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 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 SoundSourceMediaFoundation::readSampleFrames( SINT numberOfFrames, CSAMPLE* sampleBuffer) { if (sDebug) { qDebug() << "read()" << numberOfFrames; } SINT framesNeeded(numberOfFrames); // first, copy frames from leftover buffer IF the leftover buffer is at // the correct frame if (m_leftoverBufferLength > 0 && m_leftoverBufferPosition == m_nextFrame) { copyFrames(sampleBuffer, &framesNeeded, m_leftoverBuffer, m_leftoverBufferLength); if (m_leftoverBufferLength > 0) { if (framesNeeded != 0) { qWarning() << __FILE__ << __LINE__ << "WARNING: Expected frames needed to be 0. Abandoning this file."; m_dead = true; } m_leftoverBufferPosition += numberOfFrames; } } else { // leftoverBuffer already empty or in the wrong position, clear it m_leftoverBufferLength = 0; } while (!m_dead && framesNeeded > 0) { HRESULT hr(S_OK); DWORD dwFlags(0); qint64 timestamp(0); IMFSample *pSample(nullptr); bool error(false); // set to true to break after releasing hr = m_pReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, // [in] DWORD dwStreamIndex, 0, // [in] DWORD dwControlFlags, nullptr, // [out] DWORD *pdwActualStreamIndex, &dwFlags, // [out] DWORD *pdwStreamFlags, ×tamp, // [out] LONGLONG *pllTimestamp, &pSample); // [out] IMFSample **ppSample if (FAILED(hr)) { qWarning() << "ReadSample failed!"; break; // abort } if (sDebug) { qDebug() << "ReadSample timestamp:" << timestamp << "frame:" << frameFromMF(timestamp, getSamplingRate()) << "dwflags:" << dwFlags; } if (dwFlags & MF_SOURCE_READERF_ERROR) { // our source reader is now dead, according to the docs qWarning() << "SSMF: ReadSample set ERROR, SourceReader is now dead"; m_dead = true; break; } else if (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM) { qDebug() << "SSMF: End of input file."; break; } else if (dwFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { qWarning() << "SSMF: Type change"; break; } else if (pSample == nullptr) { // generally this will happen when dwFlags contains ENDOFSTREAM, // so it'll be caught before now -bkgood qWarning() << "SSMF: No sample"; continue; } // we now own a ref to the instance at pSample IMFMediaBuffer *pMBuffer(nullptr); // I know this does at least a memcopy and maybe a malloc, if we have // xrun issues with this we might want to look into using // IMFSample::GetBufferByIndex (although MS doesn't recommend this) if (FAILED(hr = pSample->ConvertToContiguousBuffer(&pMBuffer))) { error = true; goto releaseSample; } CSAMPLE *buffer(nullptr); DWORD bufferLengthInBytes(0); hr = pMBuffer->Lock(reinterpret_cast<quint8**>(&buffer), nullptr, &bufferLengthInBytes); if (FAILED(hr)) { error = true; goto releaseMBuffer; } SINT bufferLength = samples2frames(bufferLengthInBytes / sizeof(buffer[0])); if (m_seeking) { qint64 bufferPosition(frameFromMF(timestamp, getSamplingRate())); if (sDebug) { qDebug() << "While seeking to " << m_nextFrame << "WMF put us at" << bufferPosition; } if (m_nextFrame < bufferPosition) { // Uh oh. We are farther forward than our seek target. Emit // silence? We can't seek backwards here. CSAMPLE* pBufferCurpos = sampleBuffer + frames2samples(numberOfFrames - framesNeeded); qint64 offshootFrames = bufferPosition - m_nextFrame; // If we can correct this immediately, write zeros and adjust // m_nextFrame to pretend it never happened. if (offshootFrames <= framesNeeded) { qWarning() << __FILE__ << __LINE__ << "Working around inaccurate seeking. Writing silence for" << offshootFrames << "frames"; // Set offshootFrames samples to zero. memset(pBufferCurpos, 0, sizeof(*pBufferCurpos) * frames2samples(offshootFrames)); // Now m_nextFrame == bufferPosition m_nextFrame += offshootFrames; framesNeeded -= offshootFrames; } else { // It's more complicated. The buffer we have just decoded is // more than framesNeeded frames away from us. It's too hard // for us to handle this correctly currently, so let's just // try to get on with our lives. m_seeking = false; m_nextFrame = bufferPosition; qWarning() << __FILE__ << __LINE__ << "Seek offshoot is too drastic. Cutting losses and pretending the current decoded audio buffer is the right seek point."; } } if (m_nextFrame >= bufferPosition && m_nextFrame < bufferPosition + bufferLength) { // m_nextFrame is in this buffer. buffer += frames2samples(m_nextFrame - bufferPosition); bufferLength -= m_nextFrame - bufferPosition; m_seeking = false; } else { // we need to keep going forward goto releaseRawBuffer; } } // If the bufferLength is larger than the leftover buffer, re-allocate // it with 2x the space. if (frames2samples(bufferLength) > m_leftoverBufferSize) { SINT newSize = m_leftoverBufferSize; while (newSize < frames2samples(bufferLength)) { newSize *= 2; } CSAMPLE* newBuffer = new CSAMPLE[newSize]; memcpy(newBuffer, m_leftoverBuffer, sizeof(m_leftoverBuffer[0]) * m_leftoverBufferSize); delete[] m_leftoverBuffer; m_leftoverBuffer = newBuffer; m_leftoverBufferSize = newSize; } copyFrames( sampleBuffer + frames2samples(numberOfFrames - framesNeeded), &framesNeeded, buffer, bufferLength); releaseRawBuffer: hr = pMBuffer->Unlock(); // I'm ignoring this, MSDN for IMFMediaBuffer::Unlock stipulates // nothing about the state of the instance if this fails so might as // well just let it be released. //if (FAILED(hr)) break; releaseMBuffer: safeRelease(&pMBuffer); releaseSample: safeRelease(&pSample); if (error) break; } SINT framesRead = numberOfFrames - framesNeeded; m_iCurrentPosition += framesRead; m_nextFrame += framesRead; if (m_leftoverBufferLength > 0) { if (framesNeeded != 0) { qWarning() << __FILE__ << __LINE__ << "WARNING: Expected frames needed to be 0. Abandoning this file."; m_dead = true; } m_leftoverBufferPosition = m_nextFrame; } if (sDebug) { qDebug() << "read()" << numberOfFrames << "returning" << framesRead; } return framesRead; }
SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { AVDictionary *l_iFormatOpts = nullptr; const QString localFileName(getLocalFileName()); qDebug() << "New SoundSourceFFmpeg :" << localFileName; DEBUG_ASSERT(!m_pFormatCtx); m_pFormatCtx = avformat_alloc_context(); if (m_pFormatCtx == nullptr) { qDebug() << "SoundSourceFFmpeg::tryOpen: Can't allocate memory"; return OpenResult::FAILED; } // TODO() why is this required, should't it be a runtime check #if LIBAVCODEC_VERSION_INT < 3622144 // 55.69.0 m_pFormatCtx->max_analyze_duration = 999999999; #endif // libav replaces open() with ff_win32_open() which accepts a // Utf8 path // see: avformat/os_support.h // The old method defining an URL_PROTOCOL is deprecated #if defined(_WIN32) && !defined(__MINGW32CE__) const QByteArray qBAFilename( avformat_version() >= ((52<<16)+(0<<8)+0) ? getLocalFileName().toUtf8() : getLocalFileName().toLocal8Bit()); #else const QByteArray qBAFilename(getLocalFileName().toLocal8Bit()); #endif // Open file and make m_pFormatCtx if (avformat_open_input(&m_pFormatCtx, qBAFilename.constData(), nullptr, &l_iFormatOpts) != 0) { qDebug() << "SoundSourceFFmpeg::tryOpen: cannot open" << localFileName; return OpenResult::FAILED; } // TODO() why is this required, should't it be a runtime check #if LIBAVCODEC_VERSION_INT > 3544932 // 54.23.100 av_dict_free(&l_iFormatOpts); #endif // Retrieve stream information if (avformat_find_stream_info(m_pFormatCtx, nullptr) < 0) { qDebug() << "SoundSourceFFmpeg::tryOpen: cannot open" << localFileName; return OpenResult::FAILED; } //debug only (Enable if needed) //av_dump_format(m_pFormatCtx, 0, qBAFilename.constData(), false); // Find the first audio stream m_iAudioStream = -1; for (unsigned int i = 0; i < m_pFormatCtx->nb_streams; i++) if (m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { m_iAudioStream = i; break; } if (m_iAudioStream == -1) { qDebug() << "SoundSourceFFmpeg::tryOpen: cannot find an audio stream: cannot open" << localFileName; return OpenResult::FAILED; } // Get a pointer to the codec context for the audio stream m_pCodecCtx = m_pFormatCtx->streams[m_iAudioStream]->codec; // Find the decoder for the audio stream if (!(m_pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id))) { qDebug() << "SoundSourceFFmpeg::tryOpen: cannot find a decoder for" << localFileName; return OpenResult::UNSUPPORTED_FORMAT; } if (avcodec_open2(m_pCodecCtx, m_pCodec, nullptr)<0) { qDebug() << "SoundSourceFFmpeg::tryOpen: cannot open" << localFileName; return OpenResult::FAILED; } m_pResample = std::make_unique<EncoderFfmpegResample>(m_pCodecCtx); m_pResample->openMixxx(m_pCodecCtx->sample_fmt, AV_SAMPLE_FMT_FLT); setChannelCount(m_pCodecCtx->channels); setSamplingRate(m_pCodecCtx->sample_rate); setFrameCount((qint64)round((double)((double)m_pFormatCtx->duration * (double)m_pCodecCtx->sample_rate) / (double)AV_TIME_BASE)); qDebug() << "SoundSourceFFmpeg::tryOpen: Sampling rate: " << getSamplingRate() << ", Channels: " << getChannelCount() << "\n"; if (getChannelCount() > 2) { qDebug() << "ffmpeg: No support for more than 2 channels!"; return OpenResult::FAILED; } return OpenResult::SUCCEEDED; }
static unsigned int get_length_in_samples(struct sppb_plugin_description *plugin, void *context) { MPSP_DPRINTF("parser: get_length_in_samples(): %u\n", (ModPlug_GetLength(self) + 500) / 1000 * getSamplingRate()); return (ModPlug_GetLength(self) + 500) / 1000 * get_sampling_rate(); }
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, nullptr, nullptr, nullptr, nullptr)) { 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 m_framesPerSampleBlock frames. Otherwise // we need to use a temporary buffer. CSAMPLE* pDecodeBuffer; // in/out parameter SINT decodeBufferCapacity; const SINT decodeBufferCapacityMin = frames2samples(m_framesPerSampleBlock); 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 (getSamplingRate() != SINT(decFrameInfo.samplerate)) { qWarning() << "Corrupt or unsupported AAC file:" << "Unexpected sampling rate" << decFrameInfo.samplerate << "<>" << getSamplingRate(); 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); }
void SoundSourceFLAC::flacMetadata(const FLAC__StreamMetadata* metadata) { // https://xiph.org/flac/api/group__flac__stream__decoder.html#ga43e2329c15731c002ac4182a47990f85 // "...one STREAMINFO block, followed by zero or more other metadata blocks." // "...by default the decoder only calls the metadata callback for the STREAMINFO block..." // "...always before the first audio frame (i.e. write callback)." switch (metadata->type) { case FLAC__METADATA_TYPE_STREAMINFO: { const SINT channelCount = metadata->data.stream_info.channels; if (isValidChannelCount(channelCount)) { if (hasChannelCount()) { // already set before -> check for consistency if (getChannelCount() != channelCount) { qWarning() << "Unexpected channel count:" << channelCount << " <> " << getChannelCount(); } } else { // not set before setChannelCount(channelCount); } } else { qWarning() << "Invalid channel count:" << channelCount; } const SINT samplingRate = metadata->data.stream_info.sample_rate; if (isValidSamplingRate(samplingRate)) { if (hasSamplingRate()) { // already set before -> check for consistency if (getSamplingRate() != samplingRate) { qWarning() << "Unexpected sampling rate:" << samplingRate << " <> " << getSamplingRate(); } } else { // not set before setSamplingRate(samplingRate); } } else { qWarning() << "Invalid sampling rate:" << samplingRate; } const SINT frameCount = metadata->data.stream_info.total_samples; DEBUG_ASSERT(isValidFrameCount(frameCount)); if (isEmpty()) { // not set before setFrameCount(frameCount); } else { // already set before -> check for consistency if (getFrameCount() != frameCount) { qWarning() << "Unexpected frame count:" << frameCount << " <> " << getFrameCount(); } } const unsigned bitsPerSample = metadata->data.stream_info.bits_per_sample; DEBUG_ASSERT(kBitsPerSampleDefault != bitsPerSample); if (kBitsPerSampleDefault == m_bitsPerSample) { // not set before m_bitsPerSample = bitsPerSample; m_sampleScaleFactor = CSAMPLE_PEAK / CSAMPLE(FLAC__int32(1) << bitsPerSample); } else { // already set before -> check for consistency if (bitsPerSample != m_bitsPerSample) { qWarning() << "Unexpected bits per sample:" << bitsPerSample << " <> " << m_bitsPerSample; } } m_maxBlocksize = metadata->data.stream_info.max_blocksize; if (0 >= m_maxBlocksize) { qWarning() << "Invalid max. blocksize" << m_maxBlocksize; } const SINT sampleBufferCapacity = m_maxBlocksize * getChannelCount(); m_sampleBuffer.resetCapacity(sampleBufferCapacity); break; } default: // Ignore all other metadata types break; } }
FLAC__StreamDecoderWriteStatus SoundSourceFLAC::flacWrite( const FLAC__Frame* frame, const FLAC__int32* const buffer[]) { const SINT numChannels = frame->header.channels; if (getChannelCount() > numChannels) { qWarning() << "Corrupt or unsupported FLAC file:" << "Invalid number of channels in FLAC frame header" << frame->header.channels << "<>" << getChannelCount(); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } if (getSamplingRate() != SINT(frame->header.sample_rate)) { qWarning() << "Corrupt or unsupported FLAC file:" << "Invalid sample rate in FLAC frame header" << frame->header.sample_rate << "<>" << getSamplingRate(); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } const SINT numReadableFrames = frame->header.blocksize; if (numReadableFrames > m_maxBlocksize) { qWarning() << "Corrupt or unsupported FLAC file:" << "Block size in FLAC frame header exceeds the maximum block size" << frame->header.blocksize << ">" << m_maxBlocksize; return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } // According to the API docs the decoder will always report the current // position in "FLAC samples" (= "Mixxx frames") for convenience DEBUG_ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); m_curFrameIndex = frame->header.number.sample_number; // Decode buffer should be empty before decoding the next frame DEBUG_ASSERT(m_sampleBuffer.isEmpty()); const SampleBuffer::WritableChunk writableChunk( m_sampleBuffer.writeToTail(frames2samples(numReadableFrames))); const SINT numWritableFrames = samples2frames(writableChunk.size()); DEBUG_ASSERT(numWritableFrames <= numReadableFrames); if (numWritableFrames < numReadableFrames) { qWarning() << "Sample buffer has not enough free space for all decoded FLAC samples:" << numWritableFrames << "<" << numReadableFrames; } CSAMPLE* pSampleBuffer = writableChunk.data(); DEBUG_ASSERT(getChannelCount() <= numChannels); switch (getChannelCount()) { case 1: { // optimized code for 1 channel (mono) for (SINT i = 0; i < numWritableFrames; ++i) { *pSampleBuffer++ = buffer[0][i] * m_sampleScaleFactor; } break; } case 2: { // optimized code for 2 channels (stereo) for (SINT i = 0; i < numWritableFrames; ++i) { *pSampleBuffer++ = buffer[0][i] * m_sampleScaleFactor; *pSampleBuffer++ = buffer[1][i] * m_sampleScaleFactor; } break; } default: { // generic code for multiple channels for (SINT i = 0; i < numWritableFrames; ++i) { for (SINT j = 0; j < getChannelCount(); ++j) { *pSampleBuffer++ = buffer[j][i] * m_sampleScaleFactor; } } } } return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; }