/* * et_opus_tag_read_file_tag: * @filename: file from which to read tags * @FileTag: File_Tag to read tag into * @error: a GError or %NULL * * Read file tags and store into File_Tag. * * Returns: %TRUE if successful otherwise %FALSE */ gboolean et_opus_tag_read_file_tag (GFile *gfile, File_Tag *FileTag, GError **error) { OggOpusFile *file; const OpusTags *tags; GFileInfo *info; g_return_val_if_fail (gfile != NULL && FileTag != 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; } tags = op_tags (file, 0); info = g_file_query_info (gfile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NONE, NULL, error); if (!info) { op_free (file); g_assert (error == NULL || *error != NULL); return FALSE; } /* The cast is safe according to the opusfile documentation. */ et_add_file_tags_from_vorbis_comments ((vorbis_comment *)tags, FileTag, g_file_info_get_display_name (info)); g_object_unref (info); op_free (file); g_assert (error == NULL || *error == NULL); return TRUE; }
static void xmms_opus_read_metadata (xmms_xform_t *xform, xmms_opus_data_t *data) { data->opushead = op_head (data->opusfile, -1); data->opustags = op_tags (data->opusfile, -1); data->channels = op_channel_count (data->opusfile, -1); gint i; if (!data->opustags) return; for (i = 0; i < data->opustags->comments; i++) { const gchar *ptr, *entry; gsize length; gchar key[64]; entry = data->opustags->user_comments[i]; length = data->opustags->comment_lengths[i]; if (entry == NULL || *entry == '\0') continue; /* check whether it's a valid comment */ ptr = memchr (entry, '=', length); if (ptr == NULL) continue; ptr++; g_strlcpy (key, entry, MIN (ptr - entry, sizeof (key))); if (!xmms_xform_metadata_mapper_match (xform, key, ptr, length - (ptr - entry))) { XMMS_DBG ("Unhandled tag '%s'", entry); } } }
/* 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; }
static int update_vorbis_comments (DB_playItem_t *it, OggOpusFile *opusfile, const int tracknum) { const OpusTags *vc = op_tags (opusfile, tracknum); if (!vc) { return -1; } deadbeef->pl_delete_all_meta (it); for (int i = 0; i < vc->comments; i++) { char *tag = strdup(vc->user_comments[i]); char *value; if (tag && (value = strchr(tag, '=')) #ifdef ANDROID && strlen (value) < 4000 #endif ) { // skip the ignored RG fields, and the picture if (_is_replaygain_tag (it, tag) || !strcasecmp (tag, "METADATA_BLOCK_PICTURE")) { free (tag); continue; } *value++ = '\0'; deadbeef->pl_append_meta(it, oggedit_map_tag(tag, "tag2meta"), value); } if (tag) { free(tag); } } const char *r128_trackgain = deadbeef->pl_find_meta (it, "R128_TRACK_GAIN"); if (r128_trackgain) { int trackgain = atoi (r128_trackgain) + op_head (opusfile, tracknum)->output_gain; if (trackgain != 0) { deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, trackgain / 256.0f + 5.0f); deadbeef->pl_delete_meta (it, "R128_TRACK_GAIN"); } } int albumgain = op_head (opusfile, tracknum)->output_gain; const char *r128_albumgain = deadbeef->pl_find_meta (it, "R128_ALBUM_GAIN"); if (r128_albumgain) { albumgain += atoi (r128_albumgain); deadbeef->pl_delete_meta (it, "R128_ALBUM_GAIN"); } if (albumgain != 0) { deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, albumgain / 256.0f + 5.0f); } char s[100]; int output_gain = op_head (opusfile, tracknum)->output_gain; snprintf (s, sizeof (s), "%0.2f dB", output_gain / 256.0f + 5.0f); deadbeef->pl_replace_meta (it, ":OPUS_HEADER_GAIN", s); deadbeef->pl_set_meta_int (it, ":SAMPLERATE_ORIGINAL", op_head (opusfile, tracknum)->input_sample_rate); deadbeef->pl_add_meta (it, "title", NULL); uint32_t f = deadbeef->pl_get_item_flags (it); f &= ~DDB_TAG_MASK; f |= DDB_TAG_VORBISCOMMENTS; deadbeef->pl_set_item_flags (it, f); ddb_playlist_t *plt = deadbeef->plt_get_curr (); if (plt) { deadbeef->plt_modified (plt); deadbeef->plt_unref (plt); } return 0; }
void VoiceMessagesLoader::onLoad(AudioData *audio) { bool started = false; int32 audioindex = -1; Loader *l = 0; Loaders::iterator j = _loaders.end(); { QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (!voice) return; for (int32 i = 0; i < AudioVoiceMsgSimultaneously; ++i) { VoiceMessages::Msg &m(voice->_data[i]); if (m.audio != audio || !m.loading) continue; audioindex = i; j = _loaders.find(audio); if (j != _loaders.end() && (j.value()->fname != m.fname || j.value()->data.size() != m.data.size())) { delete j.value(); _loaders.erase(j); j = _loaders.end(); } if (j == _loaders.end()) { l = (j = _loaders.insert(audio, new Loader())).value(); l->fname = m.fname; l->data = m.data; int ret; if (m.data.isEmpty()) { l->file = op_open_file(m.fname.toUtf8().constData(), &ret); } else { l->file = op_open_memory((const unsigned char*)m.data.constData(), m.data.size(), &ret); } if (!l->file) { LOG(("Audio Error: op_open_file failed for '%1', data size '%2', error code %3").arg(m.fname).arg(m.data.size()).arg(ret)); m.state = VoiceMessageStopped; return loadError(j); } ogg_int64_t duration = op_pcm_total(l->file, -1); if (duration < 0) { LOG(("Audio Error: op_pcm_total failed to get full duration for '%1', data size '%2', error code %3").arg(m.fname).arg(m.data.size()).arg(duration)); m.state = VoiceMessageStopped; return loadError(j); } m.duration = duration; m.skipStart = 0; m.skipEnd = duration; m.position = 0; m.started = 0; started = true; } else { if (!m.skipEnd) continue; l = j.value(); } break; } } if (j == _loaders.end()) { LOG(("Audio Error: trying to load part of audio, that is not playing at the moment")); emit error(audio); return; } if (started) { l->pcm_offset = op_pcm_tell(l->file); l->pcm_print_offset = l->pcm_offset - AudioVoiceMsgFrequency; } bool finished = false; DEBUG_LOG(("Audio Info: reading buffer for file '%1', data size '%2', current pcm_offset %3").arg(l->fname).arg(l->data.size()).arg(l->pcm_offset)); QByteArray result; int64 samplesAdded = 0; while (result.size() < AudioVoiceMsgBufferSize) { opus_int16 pcm[AudioVoiceMsgFrequency * AudioVoiceMsgChannels]; int ret = op_read_stereo(l->file, pcm, sizeof(pcm) / sizeof(*pcm)); if (ret < 0) { { QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (voice) { VoiceMessages::Msg &m(voice->_data[audioindex]); if (m.audio == audio) { m.state = VoiceMessageStopped; } } } LOG(("Audio Error: op_read_stereo failed, error code %1").arg(ret)); return loadError(j); } int li = op_current_link(l->file); if (li != l->prev_li) { const OpusHead *head = op_head(l->file, li); const OpusTags *tags = op_tags(l->file, li); for (int32 ci = 0; ci < tags->comments; ++ci) { const char *comment = tags->user_comments[ci]; if (opus_tagncompare("METADATA_BLOCK_PICTURE", 22, comment) == 0) { OpusPictureTag pic; int err = opus_picture_tag_parse(&pic, comment); if (err >= 0) { opus_picture_tag_clear(&pic); } } } if (!op_seekable(l->file)) { l->pcm_offset = op_pcm_tell(l->file) - ret; } } if (li != l->prev_li || l->pcm_offset >= l->pcm_print_offset + AudioVoiceMsgFrequency) { l->pcm_print_offset = l->pcm_offset; } l->pcm_offset = op_pcm_tell(l->file); if (!ret) { DEBUG_LOG(("Audio Info: read completed")); finished = true; break; } result.append((const char*)pcm, sizeof(*pcm) * ret * AudioVoiceMsgChannels); l->prev_li = li; samplesAdded += ret; { QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (!voice) return; VoiceMessages::Msg &m(voice->_data[audioindex]); if (m.audio != audio || !m.loading || m.fname != l->fname || m.data.size() != l->data.size()) { LOG(("Audio Error: playing changed while loading")); m.state = VoiceMessageStopped; return loadError(j); } } } QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (!voice) return; VoiceMessages::Msg &m(voice->_data[audioindex]); if (m.audio != audio || !m.loading || m.fname != l->fname || m.data.size() != l->data.size()) { LOG(("Audio Error: playing changed while loading")); m.state = VoiceMessageStopped; return loadError(j); } if (started) { if (m.source) { alSourceStop(m.source); for (int32 i = 0; i < 3; ++i) { if (m.samplesCount[i]) { alSourceUnqueueBuffers(m.source, 1, m.buffers + i); m.samplesCount[i] = 0; } } m.nextBuffer = 0; } } if (samplesAdded) { if (!m.source) { alGenSources(1, &m.source); alSourcef(m.source, AL_PITCH, 1.f); alSourcef(m.source, AL_GAIN, 1.f); alSource3f(m.source, AL_POSITION, 0, 0, 0); alSource3f(m.source, AL_VELOCITY, 0, 0, 0); alSourcei(m.source, AL_LOOPING, 0); } if (!m.buffers[m.nextBuffer]) alGenBuffers(3, m.buffers); if (!_checkALError()) { m.state = VoiceMessageStopped; return loadError(j); } if (m.samplesCount[m.nextBuffer]) { alSourceUnqueueBuffers(m.source, 1, m.buffers + m.nextBuffer); m.skipStart += m.samplesCount[m.nextBuffer]; } m.samplesCount[m.nextBuffer] = samplesAdded; alBufferData(m.buffers[m.nextBuffer], AL_FORMAT_STEREO16, result.constData(), result.size(), AudioVoiceMsgFrequency); alSourceQueueBuffers(m.source, 1, m.buffers + m.nextBuffer); m.skipEnd -= samplesAdded; m.nextBuffer = (m.nextBuffer + 1) % 3; if (!_checkALError()) { m.state = VoiceMessageStopped; return loadError(j); } } else { finished = true; } if (finished) { m.skipEnd = 0; m.duration = m.skipStart + m.samplesCount[0] + m.samplesCount[1] + m.samplesCount[2]; } m.loading = false; if (m.state == VoiceMessageResuming || m.state == VoiceMessagePlaying || m.state == VoiceMessageStarting) { ALint state = AL_INITIAL; alGetSourcei(m.source, AL_SOURCE_STATE, &state); if (_checkALError()) { if (state != AL_PLAYING) { alSourcePlay(m.source); emit needToCheck(); } } } }