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; }
/* * et_opus_read_file_info: * @file: file to read info from * @ETFileInfo: ET_File_Info to put information into * @error: a GError or %NULL * * Read header information of an Opus file. * * Returns: %TRUE if successful otherwise %FALSE */ gboolean et_opus_read_file_info (GFile *gfile, ET_File_Info *ETFileInfo, GError **error) { OggOpusFile *file; const OpusHead* head; GFileInfo *info; g_return_val_if_fail (gfile != NULL && ETFileInfo != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); file = et_opus_open_file (gfile, error); if (!file) { g_assert (error == NULL || *error != NULL); return FALSE; } /* FIXME: Improve error-checking. */ head = op_head (file, -1); /* TODO: Read the vendor string from the Vorbis comment? */ ETFileInfo->version = head->version; ETFileInfo->bitrate = op_bitrate (file, -1) / 1000; ETFileInfo->mode = head->channel_count; /* All Opus audio is encoded at 48 kHz, but the input sample rate can * differ, and then input_sample_rate will be set. */ if (head->input_sample_rate != 0) { ETFileInfo->samplerate = head->input_sample_rate; } else { ETFileInfo->samplerate = 48000; } ETFileInfo->duration = op_pcm_total (file, -1) / 48000; op_free (file); info = g_file_query_info (gfile, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (info) { ETFileInfo->size = g_file_info_get_size (info); g_object_unref (info); } else { ETFileInfo->size = 0; } g_assert (error == NULL || *error == NULL); return TRUE; }
/* Parse the the file to get metadata */ Result SoundSourceOpus::parseHeader() { int error = 0; QByteArray qBAFilename = m_qFilename.toLocal8Bit(); OggOpusFile *l_ptrOpusFile = op_open_file(qBAFilename.constData(), &error); this->setBitrate((int)op_bitrate(l_ptrOpusFile, -1) / 1000); this->setSampleRate(48000); this->setChannels(2); qint64 l_lLength = op_pcm_total(l_ptrOpusFile, -1) * 2; this->setDuration(l_lLength / (48000 * 2)); this->setType("opus"); // If we don't have new enough Taglib we use libopusfile parser! #if (TAGLIB_MAJOR_VERSION >= 1) && (TAGLIB_MINOR_VERSION >= 9) TagLib::Ogg::Opus::File f(qBAFilename.constData()); // Takes care of all the default metadata bool result = processTaglibFile(f); TagLib::Ogg::XiphComment *tag = f.tag(); if (tag) { processXiphComment(tag); } #else // From Taglib 1.9.x Opus is supported // Before that we have parse tags by this code int i = 0; const OpusTags *l_ptrOpusTags = op_tags(l_ptrOpusFile, -1); // This is left for debug reasons !! // qDebug() << "opus: We have " << l_ptrOpusTags->comments; for( i = 0; i < l_ptrOpusTags->comments; i ++){ QString l_SWholeTag = QString(l_ptrOpusTags->user_comments[i]); QString l_STag = l_SWholeTag.left(l_SWholeTag.indexOf("=")); QString l_SPayload = l_SWholeTag.right((l_SWholeTag.length() - l_SWholeTag.indexOf("=")) - 1); if (!l_STag.compare("ARTIST") ) { this->setArtist(l_SPayload); } else if (!l_STag.compare("ALBUM")) { this->setAlbum(l_SPayload); } else if (!l_STag.compare("BPM")) { this->setBPM(l_SPayload.toFloat()); } else if (!l_STag.compare("YEAR") || !l_STag.compare("DATE")) { this->setYear(l_SPayload); } else if (!l_STag.compare("GENRE")) { this->setGenre(l_SPayload); } else if (!l_STag.compare("TRACKNUMBER")) { this->setTrackNumber(l_SPayload); } else if (!l_STag.compare("COMPOSER")) { this->setComposer(l_SPayload); } else if (!l_STag.compare("ALBUMARTIST")) { this->setAlbumArtist(l_SPayload); } else if (!l_STag.compare("TITLE")) { this->setTitle(l_SPayload); } else if (!l_STag.compare("REPLAYGAIN_TRACK_PEAK")) { } else if (!l_STag.compare("REPLAYGAIN_TRACK_GAIN")) { this->parseReplayGainString (l_SPayload); } else if (!l_STag.compare("REPLAYGAIN_ALBUM_PEAK")) { } else if (!l_STag.compare("REPLAYGAIN_ALBUM_GAIN")) { } // This is left fot debug reasons!! //qDebug() << "Comment" << i << l_ptrOpusTags->comment_lengths[i] << //" (" << l_ptrOpusTags->user_comments[i] << ")" << l_STag << "*" << l_SPayload; } op_free(l_ptrOpusFile); return OK; #endif #if TAGLIB_MAJOR_VERSION >= 1 && TAGLIB_MINOR_VERSION >= 9 return result ? OK : ERR; #endif }
Result SoundSourceOpus::parseTrackMetadataAndCoverArt( TrackMetadata* pTrackMetadata, QImage* pCoverArt) const { if (OK == SoundSource::parseTrackMetadataAndCoverArt( pTrackMetadata, pCoverArt)) { // Done if the default implementation in the base class // supports Opus files. return OK; } // Beginning with version 1.9.0 TagLib supports the Opus format. // Until this becomes the minimum version required by Mixxx tags // in .opus files must also be parsed using opusfile. The following // code should removed as soon as it is no longer needed! // // NOTE(uklotzde): The following code has been found in SoundSourceOpus // and will not be improved. We are aware of its shortcomings like // the lack of proper error handling. // 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 error = 0; OggOpusFileOwner l_ptrOpusFile( op_open_file(qBAFilename.constData(), &error)); int i = 0; const OpusTags *l_ptrOpusTags = op_tags(l_ptrOpusFile, -1); pTrackMetadata->setChannels(op_channel_count(l_ptrOpusFile, -1)); pTrackMetadata->setSampleRate(Mixxx::SoundSourceOpus::kSamplingRate); pTrackMetadata->setBitrate(op_bitrate(l_ptrOpusFile, -1) / 1000); pTrackMetadata->setDuration( op_pcm_total(l_ptrOpusFile, -1) / pTrackMetadata->getSampleRate()); bool hasDate = false; for (i = 0; i < l_ptrOpusTags->comments; ++i) { QString l_SWholeTag = QString(l_ptrOpusTags->user_comments[i]); QString l_STag = l_SWholeTag.left(l_SWholeTag.indexOf("=")); QString l_SPayload = l_SWholeTag.right((l_SWholeTag.length() - l_SWholeTag.indexOf("=")) - 1); if (!l_STag.compare("ARTIST")) { pTrackMetadata->setArtist(l_SPayload); } else if (!l_STag.compare("ALBUM")) { pTrackMetadata->setAlbum(l_SPayload); } else if (!l_STag.compare("BPM")) { pTrackMetadata->setBpm(l_SPayload.toFloat()); } else if (!l_STag.compare("DATE")) { // Prefer "DATE" over "YEAR" pTrackMetadata->setYear(l_SPayload.trimmed()); // Avoid to overwrite "DATE" with "YEAR" hasDate |= !pTrackMetadata->getYear().isEmpty(); } else if (!hasDate && !l_STag.compare("YEAR")) { pTrackMetadata->setYear(l_SPayload.trimmed()); } else if (!l_STag.compare("GENRE")) { pTrackMetadata->setGenre(l_SPayload); } else if (!l_STag.compare("TRACKNUMBER")) { pTrackMetadata->setTrackNumber(l_SPayload); } else if (!l_STag.compare("COMPOSER")) { pTrackMetadata->setComposer(l_SPayload); } else if (!l_STag.compare("ALBUMARTIST")) { pTrackMetadata->setAlbumArtist(l_SPayload); } else if (!l_STag.compare("TITLE")) { pTrackMetadata->setTitle(l_SPayload); } else if (!l_STag.compare("REPLAYGAIN_TRACK_GAIN")) { bool trackGainRatioValid = false; double trackGainRatio = ReplayGain::parseGain2Ratio(l_SPayload, &trackGainRatioValid); if (trackGainRatioValid) { ReplayGain trackGain(pTrackMetadata->getReplayGain()); trackGain.setRatio(trackGainRatio); pTrackMetadata->setReplayGain(trackGain); } } // This is left fot debug reasons!! //qDebug() << "Comment" << i << l_ptrOpusTags->comment_lengths[i] << //" (" << l_ptrOpusTags->user_comments[i] << ")" << l_STag << "*" << l_SPayload; } return OK; }