SoundSource::OpenResult SoundSourceOpus::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { // From opus/opusfile.h // On Windows, this string must be UTF-8 (to allow access to // files whose names cannot be represented in the current // MBCS code page). // All other systems use the native character encoding. #ifdef _WIN32 QByteArray qBAFilename = getLocalFileName().toUtf8(); #else QByteArray qBAFilename = getLocalFileName().toLocal8Bit(); #endif int errorCode = 0; DEBUG_ASSERT(!m_pOggOpusFile); m_pOggOpusFile = op_open_file(qBAFilename.constData(), &errorCode); if (!m_pOggOpusFile) { qWarning() << "Failed to open OggOpus file:" << getUrlString() << "errorCode" << errorCode; return OpenResult::FAILED; } if (!op_seekable(m_pOggOpusFile)) { qWarning() << "SoundSourceOpus:" << "Stream in" << getUrlString() << "is not seekable"; return OpenResult::UNSUPPORTED_FORMAT; } const int channelCount = op_channel_count(m_pOggOpusFile, kCurrentStreamLink); if (0 < channelCount) { setChannelCount(channelCount); } else { qWarning() << "Failed to read channel configuration of OggOpus file:" << getUrlString(); return OpenResult::FAILED; } const ogg_int64_t pcmTotal = op_pcm_total(m_pOggOpusFile, kEntireStreamLink); if (0 <= pcmTotal) { setFrameCount(pcmTotal); } else { qWarning() << "Failed to read total length of OggOpus file:" << getUrlString(); return OpenResult::FAILED; } const opus_int32 bitrate = op_bitrate(m_pOggOpusFile, kEntireStreamLink); if (0 < bitrate) { setBitrate(bitrate / 1000); } else { qWarning() << "Failed to determine bitrate of OggOpus file:" << getUrlString(); return OpenResult::FAILED; } setSamplingRate(kSamplingRate); m_curFrameIndex = getMinFrameIndex(); return OpenResult::SUCCEEDED; }
Result SoundSourceWV::tryOpen(const AudioSourceConfig& audioSrcCfg) { DEBUG_ASSERT(!m_wpc); char msg[80]; // hold possible error message int openFlags = OPEN_WVC | OPEN_NORMALIZE; if ((kChannelCountMono == audioSrcCfg.channelCountHint) || (kChannelCountStereo == audioSrcCfg.channelCountHint)) { openFlags |= OPEN_2CH_MAX; } m_wpc = WavpackOpenFileInput( getLocalFileNameBytes().constData(), msg, openFlags, 0); if (!m_wpc) { qDebug() << "SSWV::open: failed to open file : " << msg; return ERR; } setChannelCount(WavpackGetReducedChannels(m_wpc)); setFrameRate(WavpackGetSampleRate(m_wpc)); setFrameCount(WavpackGetNumSamples(m_wpc)); if (WavpackGetMode(m_wpc) & MODE_FLOAT) { m_sampleScaleFactor = CSAMPLE_PEAK; } else { const int bitsPerSample = WavpackGetBitsPerSample(m_wpc); const uint32_t wavpackPeakSampleValue = uint32_t(1) << (bitsPerSample - 1); m_sampleScaleFactor = CSAMPLE_PEAK / CSAMPLE(wavpackPeakSampleValue); } return OK; }
VolumeFader::VolumeFader(SampleFormat theSampleFormat, unsigned int theChannelCount) : Effect(createEffectFunctor<VolumeFaderFunctor>(theSampleFormat)) { setChannelCount(theChannelCount); // _myCurrentVolume = _myBeginVolume = _myEndVolume = 0.0; _myCurrentFrame = _myFadeBeginFrame = _myFadeEndFrame = 0; }
MultiChannelBuffer::MultiChannelBuffer( int numChannels, int bufferSize ) : mBufferSize(bufferSize) , mChannels(NULL) , mChannelCount(0) { setChannelCount( numChannels ); }
Result SoundSourceSndFile::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { DEBUG_ASSERT(!m_pSndFile); SF_INFO sfInfo; #ifdef __WINDOWS__ // Pointer valid until string changed const QString fileName(getLocalFileName()); LPCWSTR lpcwFilename = (LPCWSTR) fileName.utf16(); m_pSndFile = sf_wchar_open(lpcwFilename, SFM_READ, &sfInfo); #else memset(&sfInfo, 0, sizeof(sfInfo)); m_pSndFile = sf_open(getLocalFileNameBytes().constData(), SFM_READ, &sfInfo); #endif if (!m_pSndFile) { // sf_format_check is only for writes qWarning() << "Error opening libsndfile file:" << getUrlString() << sf_strerror(m_pSndFile); return ERR; } if (sf_error(m_pSndFile) > 0) { qWarning() << "Error opening libsndfile file:" << getUrlString() << sf_strerror(m_pSndFile); return ERR; } setChannelCount(sfInfo.channels); setFrameRate(sfInfo.samplerate); setFrameCount(sfInfo.frames); return OK; }
Player& Player::init() { setChannelCount(kDefaultChannelCount); setFrameRate(kDefaultFrameRate); setFrameSize(kDefaultFrameSize); setSource(NULL); setTick(0); return *this; }
SoundSource::OpenResult SoundSourceWV::tryOpen( OpenMode /*mode*/, const OpenParams& params) { DEBUG_ASSERT(!m_wpc); char msg[80]; // hold possible error message int openFlags = OPEN_WVC | OPEN_NORMALIZE; if ((params.channelCount() == 1) || (params.channelCount() == 2)) { openFlags |= OPEN_2CH_MAX; } // We use WavpackOpenFileInputEx to support Unicode paths on windows // http://www.wavpack.com/lib_use.txt QString wavPackFileName = getLocalFileName(); m_pWVFile = new QFile(wavPackFileName); m_pWVFile->open(QFile::ReadOnly); QString correctionFileName(wavPackFileName + "c"); if (QFile::exists(correctionFileName)) { // If there is a correction file, open it as well m_pWVCFile = new QFile(correctionFileName); m_pWVCFile->open(QFile::ReadOnly); } m_wpc = WavpackOpenFileInputEx(&s_streamReader, m_pWVFile, m_pWVCFile, msg, openFlags, 0); if (!m_wpc) { kLogger.warning() << "failed to open file : " << msg; return OpenResult::Failed; } setChannelCount(WavpackGetReducedChannels(m_wpc)); setSampleRate(WavpackGetSampleRate(m_wpc)); initFrameIndexRangeOnce( mixxx::IndexRange::forward( 0, WavpackGetNumSamples(m_wpc))); if (WavpackGetMode(m_wpc) & MODE_FLOAT) { m_sampleScaleFactor = CSAMPLE_PEAK; } else { const int bitsPerSample = WavpackGetBitsPerSample(m_wpc); if ((bitsPerSample >= 8) && (bitsPerSample <= 32)) { // Range of signed sample values: [-2 ^ (bitsPerSample - 1), 2 ^ (bitsPerSample - 1) - 1] const uint32_t absSamplePeak = 1u << (bitsPerSample - 1); DEBUG_ASSERT(absSamplePeak > 0); // Scaled range of sample values: [-CSAMPLE_PEAK, CSAMPLE_PEAK) m_sampleScaleFactor = CSAMPLE_PEAK / absSamplePeak; } else { kLogger.warning() << "Invalid bits per sample:" << bitsPerSample; return OpenResult::Aborted; } } m_curFrameIndex = frameIndexMin(); return OpenResult::Succeeded; }
MultiChannelBuffer::MultiChannelBuffer( const MultiChannelBuffer & other ) : mBufferSize(other.mBufferSize) , mChannels(NULL) , mChannelCount(0) { setChannelCount( other.mChannelCount ); for(int i = 0; i < mChannelCount; i++) { memcpy(mChannels[i], other.mChannels[i], sizeof(float)*mBufferSize); } }
void FloatLatch::menuExecuted() { Component::menuExecuted(); // Delayed connector count setting. if (tempConnCountOwner == this) { tempConnCountOwner = (FloatLatch *)0; if (tempConnCount != getChannelCount()) { setChannelCount(tempConnCount); } } }
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: { setChannelCount(metadata->data.stream_info.channels); setSampleRate(metadata->data.stream_info.sample_rate); initFrameIndexRangeOnce( IndexRange::forward( 0, metadata->data.stream_info.total_samples)); const unsigned bitsPerSample = metadata->data.stream_info.bits_per_sample; DEBUG_ASSERT(kBitsPerSampleDefault != bitsPerSample); if (kBitsPerSampleDefault == m_bitsPerSample) { // not set before if ((bitsPerSample >= 4) && (bitsPerSample <= 32)) { m_bitsPerSample = bitsPerSample; } else { kLogger.warning() << "Invalid bits per sample:" << bitsPerSample; } } else { // already set before -> check for consistency if (bitsPerSample != m_bitsPerSample) { kLogger.warning() << "Unexpected bits per sample:" << bitsPerSample << " <> " << m_bitsPerSample; } } m_maxBlocksize = metadata->data.stream_info.max_blocksize; if (0 >= m_maxBlocksize) { kLogger.warning() << "Invalid max. blocksize" << m_maxBlocksize; } const SINT sampleBufferCapacity = m_maxBlocksize * channelCount(); if (m_sampleBuffer.capacity() < sampleBufferCapacity) { m_sampleBuffer.adjustCapacity(sampleBufferCapacity); } break; } default: // Ignore all other metadata types break; } }
SoundSource::OpenResult SoundSourceSndFile::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { DEBUG_ASSERT(!m_pSndFile); SF_INFO sfInfo; memset(&sfInfo, 0, sizeof(sfInfo)); #ifdef __WINDOWS__ // Note: we cannot use QString::toStdWString since QT 4 is compiled with // '/Zc:wchar_t-' flag and QT 5 not const QString localFileName(QDir::toNativeSeparators(getLocalFileName())); const ushort* const fileNameUtf16 = localFileName.utf16(); static_assert(sizeof(wchar_t) == sizeof(ushort), "QString::utf16(): wchar_t and ushort have different sizes"); m_pSndFile = sf_wchar_open( reinterpret_cast<wchar_t*>(const_cast<ushort*>(fileNameUtf16)), SFM_READ, &sfInfo); #else m_pSndFile = sf_open(getLocalFileName().toLocal8Bit(), SFM_READ, &sfInfo); #endif switch (sf_error(m_pSndFile)) { case SF_ERR_NO_ERROR: DEBUG_ASSERT(m_pSndFile != nullptr); break; // continue case SF_ERR_UNRECOGNISED_FORMAT: return OpenResult::UNSUPPORTED_FORMAT; default: const QString errorMsg(sf_strerror(m_pSndFile)); if (errorMsg.toLower().indexOf("unknown format") != -1) { // NOTE(uklotzde 2016-05-11): This actually happens when // trying to open a file with a supported file extension // that contains data in an unsupported format! return OpenResult::UNSUPPORTED_FORMAT; } else { qWarning() << "Error opening libsndfile file:" << getUrlString() << errorMsg; return OpenResult::FAILED; } } setChannelCount(sfInfo.channels); setSamplingRate(sfInfo.samplerate); setFrameCount(sfInfo.frames); return OpenResult::SUCCEEDED; }
MultiChannelBuffer & MultiChannelBuffer::operator=( const MultiChannelBuffer & other ) { if ( mBufferSize != other.mBufferSize ) { setBufferSize(other.mBufferSize); } if ( mChannelCount != other.mChannelCount ) { setChannelCount(other.mChannelCount); } for(int i = 0; i < mChannelCount; i++) { memcpy(mChannels[i], other.mChannels[i], sizeof(float)*mBufferSize); } return *this; }
SoundSource::OpenResult SoundSourceWV::tryOpen(const AudioSourceConfig& audioSrcCfg) { DEBUG_ASSERT(!m_wpc); char msg[80]; // hold possible error message int openFlags = OPEN_WVC | OPEN_NORMALIZE; if ((kChannelCountMono == audioSrcCfg.getChannelCount()) || (kChannelCountStereo == audioSrcCfg.getChannelCount())) { openFlags |= OPEN_2CH_MAX; } // We use WavpackOpenFileInputEx to support Unicode paths on windows // http://www.wavpack.com/lib_use.txt QString wavPackFileName = getLocalFileName(); m_pWVFile = new QFile(wavPackFileName); m_pWVFile->open(QFile::ReadOnly); QString correctionFileName(wavPackFileName + "c"); if (QFile::exists(correctionFileName)) { // If there is a correction file, open it as well m_pWVCFile = new QFile(correctionFileName); m_pWVCFile->open(QFile::ReadOnly); } m_wpc = WavpackOpenFileInputEx(&s_streamReader, m_pWVFile, m_pWVCFile, msg, openFlags, 0); if (!m_wpc) { qDebug() << "SSWV::open: failed to open file : " << msg; return OpenResult::FAILED; } setChannelCount(WavpackGetReducedChannels(m_wpc)); setSamplingRate(WavpackGetSampleRate(m_wpc)); setFrameCount(WavpackGetNumSamples(m_wpc)); if (WavpackGetMode(m_wpc) & MODE_FLOAT) { m_sampleScaleFactor = CSAMPLE_PEAK; } else { const int bitsPerSample = WavpackGetBitsPerSample(m_wpc); const uint32_t wavpackPeakSampleValue = 1u << (bitsPerSample - 1); m_sampleScaleFactor = CSAMPLE_PEAK / wavpackPeakSampleValue; } return OpenResult::SUCCEEDED; }
Result SoundSourceOggVorbis::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { m_pFile = new QFile(getLocalFileName()); if(!m_pFile->open(QFile::ReadOnly)) { qWarning() << "Failed to open OggVorbis file:" << getUrlString(); return ERR; } if (ov_open_callbacks(m_pFile, &m_vf, NULL, 0, s_callbacks) < 0) { qDebug() << "oggvorbis: Input does not appear to be an Ogg bitstream."; return ERR; } if (!ov_seekable(&m_vf)) { qWarning() << "OggVorbis file is not seekable:" << getUrlString(); return ERR; } // lookup the ogg's channels and sample rate const vorbis_info* vi = ov_info(&m_vf, kCurrentBitstreamLink); if (!vi) { qWarning() << "Failed to read OggVorbis file:" << getUrlString(); return ERR; } setChannelCount(vi->channels); setSamplingRate(vi->rate); if (0 < vi->bitrate_nominal) { setBitrate(vi->bitrate_nominal / 1000); } else { if ((0 < vi->bitrate_lower) && (vi->bitrate_lower == vi->bitrate_upper)) { setBitrate(vi->bitrate_lower / 1000); } } ogg_int64_t pcmTotal = ov_pcm_total(&m_vf, kEntireBitstreamLink); if (0 <= pcmTotal) { setFrameCount(pcmTotal); } else { qWarning() << "Failed to read total length of OggVorbis file:" << getUrlString(); return ERR; } return OK; }
Result SoundSourceOggVorbis::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { const QByteArray qbaFilename(getLocalFileNameBytes()); if (0 != ov_fopen(qbaFilename.constData(), &m_vf)) { qWarning() << "Failed to open OggVorbis file:" << getUrlString(); return ERR; } if (!ov_seekable(&m_vf)) { qWarning() << "OggVorbis file is not seekable:" << getUrlString(); return ERR; } // lookup the ogg's channels and sample rate const vorbis_info* vi = ov_info(&m_vf, kCurrentBitstreamLink); if (!vi) { qWarning() << "Failed to read OggVorbis file:" << getUrlString(); return ERR; } setChannelCount(vi->channels); setFrameRate(vi->rate); if (0 < vi->bitrate_nominal) { setBitrate(vi->bitrate_nominal / 1000); } else { if ((0 < vi->bitrate_lower) && (vi->bitrate_lower == vi->bitrate_upper)) { setBitrate(vi->bitrate_lower / 1000); } } ogg_int64_t pcmTotal = ov_pcm_total(&m_vf, kEntireBitstreamLink); if (0 <= pcmTotal) { setFrameCount(pcmTotal); } else { qWarning() << "Failed to read total length of OggVorbis file:" << getUrlString(); return ERR; } return OK; }
bool MzSpectrogramFFTW::initialise(size_t channels, size_t stepsize, size_t blocksize) { if (channels < getMinChannelCount() || channels > getMaxChannelCount()) { return false; } // step size and block size should never be zero if (stepsize <= 0 || blocksize <= 0) { return false; } setChannelCount(channels); setBlockSize(blocksize); setStepSize(stepsize); mz_minbin = getParameterInt("minbin"); mz_maxbin = getParameterInt("maxbin"); if (mz_minbin >= getBlockSize()/2) { mz_minbin = getBlockSize()/2-1; } if (mz_maxbin >= getBlockSize()/2) { mz_maxbin = getBlockSize()/2-1; } if (mz_maxbin < 0) { mz_maxbin = getBlockSize()/2-1; } if (mz_maxbin < mz_minbin) { std::swap(mz_minbin, mz_maxbin); } // The signal size/transform size are equivalent for this // plugin but the FFTW can handle any size transform. // If the size of the transform is a multiple of small // prime numbers the FFT will be used, otherwise it will // be slow (when block size=1021 for example). mz_transformer.setSize(getBlockSize()); delete [] mz_wind_buff; mz_wind_buff = new double[getBlockSize()]; makeHannWindow(mz_wind_buff, getBlockSize()); return true; }
Result SoundSourceSndFile::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { DEBUG_ASSERT(!m_pSndFile); SF_INFO sfInfo; memset(&sfInfo, 0, sizeof(sfInfo)); #ifdef __WINDOWS__ // Note: we cannot use QString::toStdWString since QT 4 is compiled with // '/Zc:wchar_t-' flag and QT 5 not const QString localFileName(QDir::toNativeSeparators(getLocalFileName())); const ushort* const fileNameUtf16 = localFileName.utf16(); static_assert(sizeof(wchar_t) == sizeof(ushort), "QString::utf16(): wchar_t and ushort have different sizes"); m_pSndFile = sf_wchar_open( reinterpret_cast<wchar_t*>(const_cast<ushort*>(fileNameUtf16)), SFM_READ, &sfInfo); #else m_pSndFile = sf_open(getLocalFileName().toLocal8Bit(), SFM_READ, &sfInfo); #endif if (!m_pSndFile) { // sf_format_check is only for writes qWarning() << "Error opening libsndfile file:" << getUrlString() << sf_strerror(m_pSndFile); return ERR; } if (sf_error(m_pSndFile) > 0) { qWarning() << "Error opening libsndfile file:" << getUrlString() << sf_strerror(m_pSndFile); return ERR; } setChannelCount(sfInfo.channels); setSamplingRate(sfInfo.samplerate); setFrameCount(sfInfo.frames); return OK; }
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; }
SoundSource::OpenResult SoundSourceMp3::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { DEBUG_ASSERT(!hasValidChannelCount()); DEBUG_ASSERT(!hasValidSamplingRate()); DEBUG_ASSERT(!m_file.isOpen()); if (!m_file.open(QIODevice::ReadOnly)) { qWarning() << "Failed to open file:" << m_file.fileName(); return OpenResult::FAILED; } // Get a pointer to the file using memory mapped IO m_fileSize = m_file.size(); m_pFileData = m_file.map(0, m_fileSize); // NOTE(uklotzde): If the file disappears unexpectedly while mapped // a SIGBUS error might occur that is not handled and will terminate // Mixxx immediately. This behavior is documented in the manpage of // mmap(). It has already appeared due to hardware errors and is // described in the following bug report: // https://bugs.launchpad.net/mixxx/+bug/1452005 // Transfer it to the mad stream-buffer: mad_stream_options(&m_madStream, MAD_OPTION_IGNORECRC); mad_stream_buffer(&m_madStream, m_pFileData, m_fileSize); DEBUG_ASSERT(m_pFileData == m_madStream.this_frame); DEBUG_ASSERT(m_seekFrameList.empty()); m_avgSeekFrameCount = 0; m_curFrameIndex = getMinFrameIndex(); int headerPerSamplingRate[kSamplingRateCount]; for (int i = 0; i < kSamplingRateCount; ++i) { headerPerSamplingRate[i] = 0; } // Decode all the headers and calculate audio properties unsigned long sumBitrate = 0; mad_header madHeader; mad_header_init(&madHeader); SINT maxChannelCount = getChannelCount(); do { if (!decodeFrameHeader(&madHeader, &m_madStream, true)) { if (isStreamValid(m_madStream)) { // Skip frame continue; } else { // Abort decoding break; } } // Grab data from madHeader const unsigned int madSampleRate = madHeader.samplerate; // TODO(XXX): Replace DEBUG_ASSERT with static_assert // MAD must not change its enum values! DEBUG_ASSERT(MAD_UNITS_8000_HZ == 8000); const mad_units madUnits = static_cast<mad_units>(madSampleRate); const long madFrameLength = mad_timer_count(madHeader.duration, madUnits); if (0 >= madFrameLength) { qWarning() << "Skipping MP3 frame with invalid length" << madFrameLength << "in:" << m_file.fileName(); // Skip frame continue; } const SINT madChannelCount = MAD_NCHANNELS(&madHeader); if (isValidChannelCount(maxChannelCount) && (madChannelCount != maxChannelCount)) { qWarning() << "Differing number of channels" << madChannelCount << "<>" << maxChannelCount << "in some MP3 frame headers:" << m_file.fileName(); } maxChannelCount = math_max(madChannelCount, maxChannelCount); const int samplingRateIndex = getIndexBySamplingRate(madSampleRate); if (samplingRateIndex >= kSamplingRateCount) { qWarning() << "Invalid sample rate:" << m_file.fileName() << madSampleRate; // Abort mad_header_finish(&madHeader); return OpenResult::FAILED; } // Count valid frames separated by its sampling rate headerPerSamplingRate[samplingRateIndex]++; addSeekFrame(m_curFrameIndex, m_madStream.this_frame); // Accumulate data from the header sumBitrate += madHeader.bitrate; // Update current stream position m_curFrameIndex += madFrameLength; DEBUG_ASSERT(m_madStream.this_frame); DEBUG_ASSERT(0 <= (m_madStream.this_frame - m_pFileData)); } while (quint64(m_madStream.this_frame - m_pFileData) < m_fileSize); mad_header_finish(&madHeader); if (MAD_ERROR_NONE != m_madStream.error) { // Unreachable code for recoverable errors DEBUG_ASSERT(!MAD_RECOVERABLE(m_madStream.error)); if (MAD_ERROR_BUFLEN != m_madStream.error) { qWarning() << "Unrecoverable MP3 header error:" << mad_stream_errorstr(&m_madStream); // Abort return OpenResult::FAILED; } } if (m_seekFrameList.empty()) { // This is not a working MP3 file. qWarning() << "SSMP3: This is not a working MP3 file:" << m_file.fileName(); // Abort return OpenResult::FAILED; } int mostCommonSamplingRateIndex = kSamplingRateCount; // invalid int mostCommonSamplingRateCount = 0; int differentRates = 0; for (int i = 0; i < kSamplingRateCount; ++i) { // Find most common sampling rate if (mostCommonSamplingRateCount < headerPerSamplingRate[i]) { mostCommonSamplingRateCount = headerPerSamplingRate[i]; mostCommonSamplingRateIndex = i; differentRates++; } } if (differentRates > 1) { qWarning() << "Differing sampling rate in some headers:" << m_file.fileName(); for (int i = 0; i < kSamplingRateCount; ++i) { if (0 < headerPerSamplingRate[i]) { qWarning() << headerPerSamplingRate[i] << "MP3 headers with sampling rate" << getSamplingRateByIndex(i); } } qWarning() << "MP3 files with varying sample rate are not supported!"; qWarning() << "Since this happens most likely due to a corrupt file"; qWarning() << "Mixxx tries to plays it with the most common sample rate for this file"; } if (mostCommonSamplingRateIndex < kSamplingRateCount) { setSamplingRate(getSamplingRateByIndex(mostCommonSamplingRateIndex)); } else { qWarning() << "No single valid sampling rate in header"; // Abort return OpenResult::FAILED; } // Initialize the AudioSource setChannelCount(maxChannelCount); setFrameCount(m_curFrameIndex); // Calculate average values m_avgSeekFrameCount = getFrameCount() / m_seekFrameList.size(); const unsigned long avgBitrate = sumBitrate / m_seekFrameList.size(); setBitrate(avgBitrate / 1000); // Terminate m_seekFrameList addSeekFrame(m_curFrameIndex, 0); // Reset positions m_curFrameIndex = getMinFrameIndex(); // Restart decoding at the beginning of the audio stream m_curFrameIndex = restartDecoding(m_seekFrameList.front()); if (m_curFrameIndex != m_seekFrameList.front().frameIndex) { qWarning() << "Failed to start decoding:" << m_file.fileName(); // Abort return OpenResult::FAILED; } return OpenResult::SUCCEEDED; }
bool SoundSourceM4A::openDecoder() { DEBUG_ASSERT(m_hDecoder == nullptr); // not already opened m_hDecoder = NeAACDecOpen(); if (m_hDecoder == nullptr) { qWarning() << "Failed to open the AAC decoder!"; return false; } NeAACDecConfigurationPtr pDecoderConfig = NeAACDecGetCurrentConfiguration( m_hDecoder); pDecoderConfig->outputFormat = FAAD_FMT_FLOAT; if ((kChannelCountMono == m_audioSrcCfg.getChannelCount()) || (kChannelCountStereo == m_audioSrcCfg.getChannelCount())) { pDecoderConfig->downMatrix = 1; } else { pDecoderConfig->downMatrix = 0; } pDecoderConfig->defObjectType = LC; if (!NeAACDecSetConfiguration(m_hDecoder, pDecoderConfig)) { qWarning() << "Failed to configure AAC decoder!"; return false; } u_int8_t* configBuffer = nullptr; u_int32_t configBufferSize = 0; if (!MP4GetTrackESConfiguration(m_hFile, m_trackId, &configBuffer, &configBufferSize)) { // Failed to get mpeg-4 audio config... this is ok. // NeAACDecInit2() will simply use default values instead. qWarning() << "Failed to read the MP4 audio configuration." << "Continuing with default values."; } SAMPLERATE_TYPE samplingRate; unsigned char channelCount; if (0 > NeAACDecInit2(m_hDecoder, configBuffer, configBufferSize, &samplingRate, &channelCount)) { free(configBuffer); qWarning() << "Failed to initialize the AAC decoder!"; return false; } else { free(configBuffer); } // Calculate how many sample blocks we need to decode in advance // of a random seek in order to get the recommended number of // prefetch frames m_numberOfPrefetchSampleBlocks = (kNumberOfPrefetchFrames + (m_framesPerSampleBlock - 1)) / m_framesPerSampleBlock; setChannelCount(channelCount); setSamplingRate(samplingRate); setFrameCount(((m_maxSampleBlockId - kSampleBlockIdMin) + 1) * m_framesPerSampleBlock); // Resize temporary buffer for decoded sample data const SINT sampleBufferCapacity = frames2samples(m_framesPerSampleBlock); m_sampleBuffer.resetCapacity(sampleBufferCapacity); // Discard all buffered samples m_inputBufferLength = 0; // Invalidate current position(s) for the following seek operation m_curSampleBlockId = MP4_INVALID_SAMPLE_ID; m_curFrameIndex = getMaxFrameIndex(); // (Re-)Start decoding at the beginning of the file seekSampleFrame(getMinFrameIndex()); return m_curFrameIndex == getMinFrameIndex(); }
// soundsource overrides Result SoundSourceCoreAudio::tryOpen(const AudioSourceConfig& audioSrcCfg) { const QString fileName(getLocalFileName()); //Open the audio file. OSStatus err; /** This code blocks works with OS X 10.5+ only. DO NOT DELETE IT for now. */ CFStringRef urlStr = CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar *>(fileName.unicode()), fileName.size()); CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, urlStr, kCFURLPOSIXPathStyle, false); err = ExtAudioFileOpenURL(urlRef, &m_audioFile); CFRelease(urlStr); CFRelease(urlRef); /** TODO: Use FSRef for compatibility with 10.4 Tiger. Note that ExtAudioFileOpen() is deprecated above Tiger, so we must maintain both code paths if someone finishes this part of the code. FSRef fsRef; CFURLGetFSRef(reinterpret_cast<CFURLRef>(url.get()), &fsRef); err = ExtAudioFileOpen(&fsRef, &m_audioFile); */ if (err != noErr) { qDebug() << "SSCA: Error opening file " << fileName; return ERR; } // get the input file format UInt32 inputFormatSize = sizeof(m_inputFormat); err = ExtAudioFileGetProperty(m_audioFile, kExtAudioFileProperty_FileDataFormat, &inputFormatSize, &m_inputFormat); if (err != noErr) { qDebug() << "SSCA: Error getting file format (" << fileName << ")"; return ERR; } m_bFileIsMp3 = m_inputFormat.mFormatID == kAudioFormatMPEGLayer3; // create the output format const UInt32 numChannels = (kChannelCountZero < audioSrcCfg.channelCountHint) ? audioSrcCfg.channelCountHint : 2; m_outputFormat = CAStreamBasicDescription(m_inputFormat.mSampleRate, numChannels, CAStreamBasicDescription::kPCMFormatFloat32, true); // set the client format err = ExtAudioFileSetProperty(m_audioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(m_outputFormat), &m_outputFormat); if (err != noErr) { qDebug() << "SSCA: Error setting file property"; return ERR; } //get the total length in frames of the audio file - copypasta: http://discussions.apple.com/thread.jspa?threadID=2364583&tstart=47 SInt64 totalFrameCount; UInt32 totalFrameCountSize = sizeof(totalFrameCount); err = ExtAudioFileGetProperty(m_audioFile, kExtAudioFileProperty_FileLengthFrames, &totalFrameCountSize, &totalFrameCount); if (err != noErr) { qDebug() << "SSCA: Error getting number of frames"; return ERR; } // // WORKAROUND for bug in ExtFileAudio // AudioConverterRef acRef; UInt32 acrsize = sizeof(AudioConverterRef); err = ExtAudioFileGetProperty(m_audioFile, kExtAudioFileProperty_AudioConverter, &acrsize, &acRef); //_ThrowExceptionIfErr(@"kExtAudioFileProperty_AudioConverter", err); AudioConverterPrimeInfo primeInfo; UInt32 piSize = sizeof(AudioConverterPrimeInfo); memset(&primeInfo, 0, piSize); err = AudioConverterGetProperty(acRef, kAudioConverterPrimeInfo, &piSize, &primeInfo); if (err != kAudioConverterErr_PropertyNotSupported) { // Only if decompressing //_ThrowExceptionIfErr(@"kAudioConverterPrimeInfo", err); m_headerFrames = primeInfo.leadingFrames; } else { m_headerFrames = 0; } setChannelCount(m_outputFormat.NumberChannels()); setFrameRate(m_inputFormat.mSampleRate); // NOTE(uklotzde): This is what I found when migrating // the code from SoundSource (sample-oriented) to the new // AudioSource (frame-oriented) API. It is not documented // when m_headerFrames > 0 and what the consequences are. setFrameCount(totalFrameCount/* - m_headerFrames*/); //Seek to position 0, which forces us to skip over all the header frames. //This makes sure we're ready to just let the Analyser rip and it'll //get the number of samples it expects (ie. no header frames). seekSampleFrame(0); return OK; }
Result SoundSourceM4A::tryOpen(const AudioSourceConfig& audioSrcCfg) { DEBUG_ASSERT(MP4_INVALID_FILE_HANDLE == m_hFile); /* open MP4 file, check for >= ver 1.9.1 */ #if MP4V2_PROJECT_version_hex <= 0x00010901 m_hFile = MP4Read(getLocalFileNameBytes().constData(), 0); #else m_hFile = MP4Read(getLocalFileNameBytes().constData()); #endif if (MP4_INVALID_FILE_HANDLE == m_hFile) { qWarning() << "Failed to open file for reading:" << getUrlString(); return ERR; } m_trackId = findFirstAudioTrackId(m_hFile); if (MP4_INVALID_TRACK_ID == m_trackId) { qWarning() << "No AAC track found:" << getUrlString(); return ERR; } const MP4SampleId numberOfSamples = MP4GetTrackNumberOfSamples(m_hFile, m_trackId); if (0 >= numberOfSamples) { qWarning() << "Failed to read number of samples from file:" << getUrlString(); return ERR; } m_maxSampleBlockId = kSampleBlockIdMin + (numberOfSamples - 1); // Determine the maximum input size (in bytes) of a // sample block for the selected track. const u_int32_t maxSampleBlockInputSize = MP4GetTrackMaxSampleSize(m_hFile, m_trackId); m_inputBuffer.resize(maxSampleBlockInputSize, 0); DEBUG_ASSERT(NULL == m_hDecoder); // not already opened m_hDecoder = NeAACDecOpen(); if (!m_hDecoder) { qWarning() << "Failed to open the AAC decoder!"; return ERR; } NeAACDecConfigurationPtr pDecoderConfig = NeAACDecGetCurrentConfiguration( m_hDecoder); pDecoderConfig->outputFormat = FAAD_FMT_FLOAT; if ((kChannelCountMono == audioSrcCfg.channelCountHint) || (kChannelCountStereo == audioSrcCfg.channelCountHint)) { pDecoderConfig->downMatrix = 1; } else { pDecoderConfig->downMatrix = 0; } pDecoderConfig->defObjectType = LC; if (!NeAACDecSetConfiguration(m_hDecoder, pDecoderConfig)) { qWarning() << "Failed to configure AAC decoder!"; return ERR; } u_int8_t* configBuffer = NULL; u_int32_t configBufferSize = 0; if (!MP4GetTrackESConfiguration(m_hFile, m_trackId, &configBuffer, &configBufferSize)) { /* failed to get mpeg-4 audio config... this is ok. * NeAACDecInit2() will simply use default values instead. */ qWarning() << "Failed to read the MP4 audio configuration." << "Continuing with default values."; } SAMPLERATE_TYPE sampleRate; unsigned char channelCount; if (0 > NeAACDecInit2(m_hDecoder, configBuffer, configBufferSize, &sampleRate, &channelCount)) { free(configBuffer); qWarning() << "Failed to initialize the AAC decoder!"; return ERR; } else { free(configBuffer); } setChannelCount(channelCount); setFrameRate(sampleRate); setFrameCount(getFrameCountForSampleBlockId(m_maxSampleBlockId)); // Resize temporary buffer for decoded sample data const SINT sampleBufferCapacity = frames2samples(kFramesPerSampleBlock); m_sampleBuffer.resetCapacity(sampleBufferCapacity); // Invalidate current position to enforce the following // seek operation m_curFrameIndex = getMaxFrameIndex(); // (Re-)Start decoding at the beginning of the file seekSampleFrame(getMinFrameIndex()); return OK; }
SoundSource::OpenResult SoundSourceOggVorbis::tryOpen( OpenMode /*mode*/, const OpenParams& /*config*/) { m_pFile = std::make_unique<QFile>(getLocalFileName()); if (!m_pFile->open(QFile::ReadOnly)) { kLogger.warning() << "Failed to open file for" << getUrlString(); return OpenResult::Failed; } const int initDecoderResult = ov_open_callbacks(m_pFile.get(), &m_vf, nullptr, 0, s_callbacks); switch (initDecoderResult) { case 0: // success -> continue break; case OV_ENOTVORBIS: case OV_EVERSION: kLogger.warning() << "Unsupported format in" << getUrlString(); return OpenResult::Aborted; default: kLogger.warning() << "Failed to initialize decoder for" << getUrlString(); return OpenResult::Failed; } if (!ov_seekable(&m_vf)) { kLogger.warning() << "Stream in" << getUrlString() << "is not seekable"; return OpenResult::Aborted; } // lookup the ogg's channels and sample rate const vorbis_info* vi = ov_info(&m_vf, kCurrentBitstreamLink); if (!vi) { kLogger.warning() << "Failed to read stream info from" << getUrlString(); return OpenResult::Failed; } setChannelCount(vi->channels); setSampleRate(vi->rate); if (0 < vi->bitrate_nominal) { initBitrateOnce(vi->bitrate_nominal / 1000); } else { if ((0 < vi->bitrate_lower) && (vi->bitrate_lower == vi->bitrate_upper)) { initBitrateOnce(vi->bitrate_lower / 1000); } } ogg_int64_t pcmTotal = ov_pcm_total(&m_vf, kEntireBitstreamLink); if (0 <= pcmTotal) { initFrameIndexRangeOnce(IndexRange::forward(0, pcmTotal)); } else { kLogger.warning() << "Failed to read read total length of" << getUrlString(); return OpenResult::Failed; } return OpenResult::Succeeded; }
/** Cobbled together from: http://msdn.microsoft.com/en-us/library/dd757929(v=vs.85).aspx and http://msdn.microsoft.com/en-us/library/dd317928(VS.85).aspx -- Albert If anything in here fails, just bail. I'm not going to decode HRESULTS. -- Bill */ bool SoundSourceMediaFoundation::configureAudioStream(const AudioSourceConfig& audioSrcCfg) { HRESULT hr; // deselect all streams, we only want the first hr = m_pSourceReader->SetStreamSelection( MF_SOURCE_READER_ALL_STREAMS, false); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to deselect all streams"; return false; } hr = m_pSourceReader->SetStreamSelection( kStreamIndex, true); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to select first audio stream"; return false; } IMFMediaType* pAudioType = nullptr; hr = m_pSourceReader->GetCurrentMediaType( kStreamIndex, &pAudioType); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to get current media type from stream"; return false; } //------ Get bitrate from the file, before we change it to get uncompressed audio UINT32 avgBytesPerSecond; hr = pAudioType->GetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &avgBytesPerSecond); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "error getting MF_MT_AUDIO_AVG_BYTES_PER_SECOND"; return false; } setBitrate( (avgBytesPerSecond * 8) / 1000); //------ hr = pAudioType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set major type to audio"; safeRelease(&pAudioType); return false; } hr = pAudioType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set subtype format to float"; safeRelease(&pAudioType); return false; } hr = pAudioType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, true); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set all samples independent"; safeRelease(&pAudioType); return false; } hr = pAudioType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, true); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set fixed size samples"; safeRelease(&pAudioType); return false; } hr = pAudioType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, kBitsPerSample); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set bits per sample:" << kBitsPerSample; safeRelease(&pAudioType); return false; } const UINT sampleSize = kLeftoverSize * kBytesPerSample; hr = pAudioType->SetUINT32( MF_MT_SAMPLE_SIZE, sampleSize); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set sample size:" << sampleSize; safeRelease(&pAudioType); return false; } UINT32 numChannels; hr = pAudioType->GetUINT32( MF_MT_AUDIO_NUM_CHANNELS, &numChannels); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to get actual number of channels"; return false; } else { qDebug() << "Number of channels in input stream" << numChannels; } if (audioSrcCfg.hasValidChannelCount()) { numChannels = audioSrcCfg.getChannelCount(); hr = pAudioType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, numChannels); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set number of channels:" << numChannels; safeRelease(&pAudioType); return false; } qDebug() << "Requested number of channels" << numChannels; } UINT32 samplesPerSecond; hr = pAudioType->GetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, &samplesPerSecond); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to get samples per second"; return false; } else { qDebug() << "Samples per second in input stream" << samplesPerSecond; } if (audioSrcCfg.hasValidSamplingRate()) { samplesPerSecond = audioSrcCfg.getSamplingRate(); hr = pAudioType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSecond); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set samples per second:" << samplesPerSecond; safeRelease(&pAudioType); return false; } qDebug() << "Requested samples per second" << samplesPerSecond; } // Set this type on the source reader. The source reader will // load the necessary decoder. hr = m_pSourceReader->SetCurrentMediaType( kStreamIndex, nullptr, pAudioType); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to set media type"; safeRelease(&pAudioType); return false; } // Finally release the reference before reusing the pointer safeRelease(&pAudioType); // Get the resulting output format. hr = m_pSourceReader->GetCurrentMediaType( kStreamIndex, &pAudioType); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to retrieve completed media type"; return false; } // Ensure the stream is selected. hr = m_pSourceReader->SetStreamSelection( kStreamIndex, true); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to select first audio stream (again)"; return false; } hr = pAudioType->GetUINT32( MF_MT_AUDIO_NUM_CHANNELS, &numChannels); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to get actual number of channels"; return false; } setChannelCount(numChannels); hr = pAudioType->GetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, &samplesPerSecond); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to get the actual sample rate"; return false; } setSamplingRate(samplesPerSecond); UINT32 leftoverBufferSizeInBytes = 0; hr = pAudioType->GetUINT32(MF_MT_SAMPLE_SIZE, &leftoverBufferSizeInBytes); if (FAILED(hr)) { qWarning() << kLogPreamble << hr << "failed to get sample buffer size (in bytes)"; return false; } DEBUG_ASSERT((leftoverBufferSizeInBytes % kBytesPerSample) == 0); m_sampleBuffer.resetCapacity(leftoverBufferSizeInBytes / kBytesPerSample); DEBUG_ASSERT(m_sampleBuffer.getCapacity() > 0); qDebug() << kLogPreamble << "Sample buffer capacity" << m_sampleBuffer.getCapacity(); // Finally release the reference safeRelease(&pAudioType); return true; }
Result SoundSourceM4A::tryOpen(const AudioSourceConfig& audioSrcCfg) { DEBUG_ASSERT(MP4_INVALID_FILE_HANDLE == m_hFile); // open MP4 file, check for >= ver 1.9.1 // From mp4v2/file.h: // * On Windows, this should be a UTF-8 encoded string. // * On other platforms, it should be an 8-bit encoding that is // * appropriate for the platform, locale, file system, etc. // * (prefer to use UTF-8 when possible). #if MP4V2_PROJECT_version_hex <= 0x00010901 m_hFile = MP4Read(getLocalFileName().toUtf8().constData(), 0); #else m_hFile = MP4Read(getLocalFileName().toUtf8().constData()); #endif if (MP4_INVALID_FILE_HANDLE == m_hFile) { qWarning() << "Failed to open file for reading:" << getUrlString(); return ERR; } m_trackId = findFirstAudioTrackId(m_hFile); if (MP4_INVALID_TRACK_ID == m_trackId) { qWarning() << "No AAC track found:" << getUrlString(); return ERR; } // Read fixed sample duration. If the sample duration is not // fixed (that is, if the number of frames per sample block varies // through the file), the call returns MP4_INVALID_DURATION. We // can't currently handle these. m_framesPerSampleBlock = MP4GetTrackFixedSampleDuration(m_hFile, m_trackId); if (MP4_INVALID_DURATION == m_framesPerSampleBlock) { qWarning() << "Unable to decode tracks with non-fixed sample durations: " << getUrlString(); return ERR; } const MP4SampleId numberOfSamples = MP4GetTrackNumberOfSamples(m_hFile, m_trackId); if (0 >= numberOfSamples) { qWarning() << "Failed to read number of samples from file:" << getUrlString(); return ERR; } m_maxSampleBlockId = kSampleBlockIdMin + (numberOfSamples - 1); // Determine the maximum input size (in bytes) of a // sample block for the selected track. const u_int32_t maxSampleBlockInputSize = MP4GetTrackMaxSampleSize(m_hFile, m_trackId); m_inputBuffer.resize(maxSampleBlockInputSize, 0); DEBUG_ASSERT(nullptr == m_hDecoder); // not already opened m_hDecoder = NeAACDecOpen(); if (!m_hDecoder) { qWarning() << "Failed to open the AAC decoder!"; return ERR; } NeAACDecConfigurationPtr pDecoderConfig = NeAACDecGetCurrentConfiguration( m_hDecoder); pDecoderConfig->outputFormat = FAAD_FMT_FLOAT; if ((kChannelCountMono == audioSrcCfg.channelCountHint) || (kChannelCountStereo == audioSrcCfg.channelCountHint)) { pDecoderConfig->downMatrix = 1; } else { pDecoderConfig->downMatrix = 0; } pDecoderConfig->defObjectType = LC; if (!NeAACDecSetConfiguration(m_hDecoder, pDecoderConfig)) { qWarning() << "Failed to configure AAC decoder!"; return ERR; } u_int8_t* configBuffer = nullptr; u_int32_t configBufferSize = 0; if (!MP4GetTrackESConfiguration(m_hFile, m_trackId, &configBuffer, &configBufferSize)) { /* failed to get mpeg-4 audio config... this is ok. * NeAACDecInit2() will simply use default values instead. */ qWarning() << "Failed to read the MP4 audio configuration." << "Continuing with default values."; } SAMPLERATE_TYPE sampleRate; unsigned char channelCount; if (0 > NeAACDecInit2(m_hDecoder, configBuffer, configBufferSize, &sampleRate, &channelCount)) { free(configBuffer); qWarning() << "Failed to initialize the AAC decoder!"; return ERR; } else { free(configBuffer); } // Calculate how many sample blocks we need to decode in advance // of a random seek in order to get the recommended number of // prefetch frames m_numberOfPrefetchSampleBlocks = (kNumberOfPrefetchFrames + (m_framesPerSampleBlock - 1)) / m_framesPerSampleBlock; setChannelCount(channelCount); setFrameRate(sampleRate); setFrameCount(((m_maxSampleBlockId - kSampleBlockIdMin) + 1) * m_framesPerSampleBlock); // Resize temporary buffer for decoded sample data const SINT sampleBufferCapacity = frames2samples(m_framesPerSampleBlock); m_sampleBuffer.resetCapacity(sampleBufferCapacity); // Invalidate current position to enforce the following // seek operation m_curFrameIndex = getMaxFrameIndex(); // (Re-)Start decoding at the beginning of the file seekSampleFrame(getMinFrameIndex()); return OK; }
Result SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { unsigned int i; AVDictionary *l_iFormatOpts = NULL; const QByteArray qBAFilename(getLocalFileNameBytes()); qDebug() << "New SoundSourceFFmpeg :" << qBAFilename; DEBUG_ASSERT(!m_pFormatCtx); m_pFormatCtx = avformat_alloc_context(); #if LIBAVCODEC_VERSION_INT < 3622144 m_pFormatCtx->max_analyze_duration = 999999999; #endif // Open file and make m_pFormatCtx if (avformat_open_input(&m_pFormatCtx, qBAFilename.constData(), NULL, &l_iFormatOpts)!=0) { qDebug() << "av_open_input_file: cannot open" << qBAFilename; return ERR; } #if LIBAVCODEC_VERSION_INT > 3544932 av_dict_free(&l_iFormatOpts); #endif // Retrieve stream information if (avformat_find_stream_info(m_pFormatCtx, NULL)<0) { qDebug() << "av_find_stream_info: cannot open" << qBAFilename; return ERR; } //debug only (Enable if needed) //av_dump_format(m_pFormatCtx, 0, qBAFilename.constData(), false); // Find the first audio stream m_iAudioStream=-1; for (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() << "ffmpeg: cannot find an audio stream: cannot open" << qBAFilename; return ERR; } // 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() << "ffmpeg: cannot find a decoder for" << qBAFilename; return ERR; } if (avcodec_open2(m_pCodecCtx, m_pCodec, NULL)<0) { qDebug() << "ffmpeg: cannot open" << qBAFilename; return ERR; } m_pResample = new EncoderFfmpegResample(m_pCodecCtx); m_pResample->open(m_pCodecCtx->sample_fmt, AV_SAMPLE_FMT_FLT); setChannelCount(m_pCodecCtx->channels); setFrameRate(m_pCodecCtx->sample_rate); setFrameCount((m_pFormatCtx->duration * m_pCodecCtx->sample_rate) / AV_TIME_BASE); qDebug() << "ffmpeg: Samplerate: " << getFrameRate() << ", Channels: " << getChannelCount() << "\n"; if (getChannelCount() > 2) { qDebug() << "ffmpeg: No support for more than 2 channels!"; return ERR; } return OK; }
Result SoundSourceModPlug::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) { ScopedTimer t("SoundSourceModPlug::open()"); // read module file to byte array const QString fileName(getLocalFileName()); QFile modFile(fileName); qDebug() << "[ModPlug] Loading ModPlug module " << modFile.fileName(); modFile.open(QIODevice::ReadOnly); m_fileBuf = modFile.readAll(); modFile.close(); // get ModPlugFile descriptor for later access m_pModFile = ModPlug::ModPlug_Load(m_fileBuf.constData(), m_fileBuf.length()); if (m_pModFile == NULL) { // an error occured t.cancel(); qDebug() << "[ModPlug] Could not load module file: " << fileName; return ERR; } DEBUG_ASSERT(0 == (kChunkSizeInBytes % sizeof(m_sampleBuf[0]))); const SINT chunkSizeInSamples = kChunkSizeInBytes / sizeof(m_sampleBuf[0]); const SampleBuffer::size_type bufferSizeLimitInSamples = s_bufferSizeLimit / sizeof(m_sampleBuf[0]); // Estimate size of sample buffer (for better performance) aligned // with the chunk size. Beware: Module length estimation is unreliable // due to loops! const SampleBuffer::size_type estimateMilliseconds = ModPlug::ModPlug_GetLength(m_pModFile); const SampleBuffer::size_type estimateSamples = estimateMilliseconds * kChannelCount * kSamplingRate; const SampleBuffer::size_type estimateChunks = (estimateSamples + (chunkSizeInSamples - 1)) / chunkSizeInSamples; const SampleBuffer::size_type sampleBufferCapacity = math_min( estimateChunks * chunkSizeInSamples, bufferSizeLimitInSamples); m_sampleBuf.reserve(sampleBufferCapacity); qDebug() << "[ModPlug] Reserved " << m_sampleBuf.capacity() << " #samples"; // decode samples into sample buffer while (m_sampleBuf.size() < bufferSizeLimitInSamples) { // reserve enough space in sample buffer const SampleBuffer::size_type currentSize = m_sampleBuf.size(); m_sampleBuf.resize(currentSize + chunkSizeInSamples); const int bytesRead = ModPlug::ModPlug_Read(m_pModFile, &m_sampleBuf[currentSize], kChunkSizeInBytes); // adjust size of sample buffer after reading if (0 < bytesRead) { DEBUG_ASSERT(0 == (bytesRead % sizeof(m_sampleBuf[0]))); const SampleBuffer::size_type samplesRead = bytesRead / sizeof(m_sampleBuf[0]); m_sampleBuf.resize(currentSize + samplesRead); } else { // nothing read -> EOF m_sampleBuf.resize(currentSize); break; // exit loop } } qDebug() << "[ModPlug] Filled Sample buffer with " << m_sampleBuf.size() << " samples."; qDebug() << "[ModPlug] Sample buffer has " << m_sampleBuf.capacity() - m_sampleBuf.size() << " samples unused capacity."; setChannelCount(kChannelCount); setSamplingRate(kSamplingRate); setFrameCount(samples2frames(m_sampleBuf.size())); m_seekPos = 0; return OK; }
/** Cobbled together from: http://msdn.microsoft.com/en-us/library/dd757929(v=vs.85).aspx and http://msdn.microsoft.com/en-us/library/dd317928(VS.85).aspx -- Albert If anything in here fails, just bail. I'm not going to decode HRESULTS. -- Bill */ bool SoundSourceMediaFoundation::configureAudioStream(const Mixxx::AudioSourceConfig& audioSrcCfg) { HRESULT hr(S_OK); // deselect all streams, we only want the first hr = m_pReader->SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false); if (FAILED(hr)) { qWarning() << "SSMF: failed to deselect all streams"; return false; } hr = m_pReader->SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, true); if (FAILED(hr)) { qWarning() << "SSMF: failed to select first audio stream"; return false; } hr = MFCreateMediaType(&m_pAudioType); if (FAILED(hr)) { qWarning() << "SSMF: failed to create media type"; return false; } hr = m_pAudioType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); if (FAILED(hr)) { qWarning() << "SSMF: failed to set major type"; return false; } hr = m_pAudioType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float); if (FAILED(hr)) { qWarning() << "SSMF: failed to set subtype"; return false; } hr = m_pAudioType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, true); if (FAILED(hr)) { qWarning() << "SSMF: failed to set samples independent"; return false; } hr = m_pAudioType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, true); if (FAILED(hr)) { qWarning() << "SSMF: failed to set fixed size samples"; return false; } hr = m_pAudioType->SetUINT32(MF_MT_SAMPLE_SIZE, kLeftoverSize); if (FAILED(hr)) { qWarning() << "SSMF: failed to set sample size"; return false; } hr = m_pAudioType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, kSampleRate); if (FAILED(hr)) { qWarning() << "SSMF: failed to set sample rate"; return false; } // "Number of bits per audio sample in an audio media type." hr = m_pAudioType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, kBitsPerSample); if (FAILED(hr)) { qWarning() << "SSMF: failed to set bits per sample"; return false; } if (isValidChannelCount(audioSrcCfg.channelCountHint)) { hr = m_pAudioType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, audioSrcCfg.channelCountHint); if (FAILED(hr)) { qWarning() << "SSMF: failed to set number of channels"; return false; } setChannelCount(audioSrcCfg.channelCountHint); } else { UINT32 numChannels = 0; hr = m_pAudioType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &numChannels); if (FAILED(hr) || (0 >= numChannels)) { qWarning() << "SSMF: failed to get number of channels"; return false; } setChannelCount(numChannels); } // "...the block alignment is equal to the number of audio channels // multiplied by the number of bytes per audio sample." hr = m_pAudioType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, frames2samples(sizeof(m_leftoverBuffer[0]))); if (FAILED(hr)) { qWarning() << "SSMF: failed to set block alignment"; return false; } // Set this type on the source reader. The source reader will // load the necessary decoder. hr = m_pReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, m_pAudioType); // the reader has the media type now, free our reference so we can use our // pointer for other purposes. Do this before checking for failure so we // don't dangle. safeRelease(&m_pAudioType); if (FAILED(hr)) { qWarning() << "SSMF: failed to set media type"; return false; } // Get the complete uncompressed format. hr = m_pReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &m_pAudioType); if (FAILED(hr)) { qWarning() << "SSMF: failed to retrieve completed media type"; return false; } // Ensure the stream is selected. hr = m_pReader->SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, true); if (FAILED(hr)) { qWarning() << "SSMF: failed to select first audio stream (again)"; return false; } UINT32 leftoverBufferSize = 0; hr = m_pAudioType->GetUINT32(MF_MT_SAMPLE_SIZE, &leftoverBufferSize); if (FAILED(hr)) { qWarning() << "SSMF: failed to get buffer size"; return false; } m_leftoverBufferSize = leftoverBufferSize; m_leftoverBufferSize /= sizeof(CSAMPLE); // convert size in bytes to sizeof(CSAMPLE) m_leftoverBuffer = new CSAMPLE[m_leftoverBufferSize]; return true; }
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; } }