int OpusDecoder_init(decoders_OpusDecoder *self, PyObject *args, PyObject *kwds) { char *filename; int error; self->opus_file = NULL; self->audiotools_pcm = NULL; self->closed = 0; if (!PyArg_ParseTuple(args, "s", &filename)) return -1; if ((self->opus_file = op_open_file(filename, &error)) == NULL) { PyErr_SetString(PyExc_ValueError, "error opening Opus file"); return -1; } self->channel_count = op_channel_count(self->opus_file, -1); if ((self->audiotools_pcm = open_audiotools_pcm()) == NULL) return -1; return 0; }
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; }
OpusDynamicLoader::OpusDynamicLoader(const std::string &path) : DynamicLoader{path} { // open opus file auto op_file = open_opus_file(); // read channels from the opus file channels = op_channel_count(op_file.get(), -1); length = op_pcm_total(op_file.get(), -1) * 2; log::msg("Create dynamic opus loader: len=%d, chan=%d", length, channels); }
int CSound::DecodeOpus(int SampleID, const void *pData, unsigned DataSize) { if(SampleID == -1 || SampleID >= NUM_SAMPLES) return -1; CSample *pSample = &m_aSamples[SampleID]; OggOpusFile *OpusFile = op_open_memory((const unsigned char *) pData, DataSize, NULL); if (OpusFile) { int NumChannels = op_channel_count(OpusFile, -1); int NumSamples = op_pcm_total(OpusFile, -1); // per channel! pSample->m_Channels = NumChannels; if(pSample->m_Channels > 2) { dbg_msg("sound/opus", "file is not mono or stereo."); return -1; } pSample->m_pData = (short *)mem_alloc(NumSamples * sizeof(short) * NumChannels, 1); int Read; int Pos = 0; while (Pos < NumSamples) { Read = op_read(OpusFile, pSample->m_pData + Pos*NumChannels, NumSamples*NumChannels, NULL); Pos += Read; } pSample->m_NumFrames = NumSamples; // ? pSample->m_Rate = 48000; pSample->m_LoopStart = -1; pSample->m_LoopEnd = -1; pSample->m_PausedAt = 0; } else { dbg_msg("sound/opus", "failed to decode sample"); return -1; } return SampleID; }
pcm_data_t OpusInMemoryLoader::get_resource() { int op_err; // open the opus file opus_file_t op_file{op_open_file(path.c_str(), &op_err), opus_deleter}; if (op_err != 0) { throw util::Error{"Could not open: %s", path.c_str()}; } // determine number of channels and number of pcm samples auto op_channels = op_channel_count(op_file.get(), -1); auto pcm_length = op_pcm_total(op_file.get(), -1); log::dbg("Opus channels=%d, pcm_length=%u", op_channels, static_cast<uint32_t>(pcm_length)); // calculate pcm buffer size depending on the number of channels // if the opus file only had one channel, the pcm buffer size must be // doubled uint32_t length = static_cast<uint32_t>(pcm_length) * 2; pcm_data_t buffer(static_cast<size_t>(length), 0); // read data from opus file int position = 0; while (true) { int samples_read = op_read(op_file.get(), &buffer.front()+position, length-position, nullptr); if (samples_read < 0) { throw util::Error{"Failed to read from opus file: errorcode=%d", samples_read}; } else if(samples_read == 0) { break; } position += samples_read * op_channels; } // convert from mono to stereo if (op_channels == 1) { for(int i = pcm_length-1; i >= 0; i--) { auto value = buffer[i]; buffer[i*2+1] = value; buffer[i*2] = value; } } return std::move(buffer); }
static gulong xmms_opus_op_read (OggOpusFile *of, gchar *buf, gint len, gint bigendian, gint sampsize, gint signd, gint *outbuf) { gulong ret; int channels; channels = op_channel_count (of, -1); do { /* FIXME buffer len / sample size */ //ret = op_read_float (of, (float *)buf, len / sizeof(float), outbuf); ret = op_read (of, (opus_int16 *)buf, len / 2, outbuf); } while (ret == OP_HOLE); /* FIXME bytes from samples_read * channels * sample size */ //ret = ret * channels * sizeof(float); ret = ret * channels * 2; return ret; }
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); } } }
static prMALError SDKImportAudio7( imStdParms *stdParms, imFileRef SDKfileRef, imImportAudioRec7 *audioRec7) { prMALError result = malNoError; // privateData ImporterLocalRec8H ldataH = reinterpret_cast<ImporterLocalRec8H>(audioRec7->privateData); stdParms->piSuites->memFuncs->lockHandle(reinterpret_cast<char**>(ldataH)); ImporterLocalRec8Ptr localRecP = reinterpret_cast<ImporterLocalRec8Ptr>( *ldataH ); if(localRecP) { assert(audioRec7->position >= 0); // Do they really want contiguous samples? // for surround channels // Premiere uses Left, Right, Left Rear, Right Rear, Center, LFE // Ogg (and Opus) uses Left, Center, Right, Left Read, Right Rear, LFE // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 static const int surround_swizzle[] = {0, 2, 3, 4, 1, 5}; static const int stereo_swizzle[] = {0, 1, 2, 3, 4, 5}; // no swizzle, actually const int *swizzle = localRecP->numChannels > 2 ? surround_swizzle : stereo_swizzle; if(localRecP->fileType == Ogg_filetype && localRecP->vf != NULL) { OggVorbis_File &vf = *localRecP->vf; int seek_err = OV_OK; if(audioRec7->position >= 0) // otherwise contiguous, but we should be good at the current position seek_err = ov_pcm_seek(&vf, audioRec7->position); if(seek_err == OV_OK) { int num = 0; float **pcm_channels; long samples_needed = audioRec7->size; long pos = 0; while(samples_needed > 0 && result == malNoError) { int samples = samples_needed; if(samples > 1024) samples = 1024; // maximum size this call can read at once long samples_read = ov_read_float(&vf, &pcm_channels, samples, &num); if(samples_read >= 0) { if(samples_read == 0) { // EOF // Premiere will keep asking me for more and more samples, // even beyond what I told it I had in SDKFileInfo8->audDuration. // Just stop and everything will be fine. break; } for(int i=0; i < localRecP->numChannels; i++) { memcpy(&audioRec7->buffer[swizzle[i]][pos], pcm_channels[i], samples_read * sizeof(float)); } samples_needed -= samples_read; pos += samples_read; } else result = imDecompressionError; } } } else if(localRecP->fileType == Opus_filetype && localRecP->opus != NULL) { const int num_channels = op_channel_count(localRecP->opus, -1); assert(localRecP->numChannels == num_channels); int seek_err = OV_OK; if(audioRec7->position >= 0) // otherwise contiguous, but we should be good at the current position seek_err = op_pcm_seek(localRecP->opus, audioRec7->position); if(seek_err == OV_OK) { float *pcm_buf = (float *)malloc(sizeof(float) * audioRec7->size * num_channels); if(pcm_buf != NULL) { long samples_needed = audioRec7->size; long pos = 0; while(samples_needed > 0 && result == malNoError) { float *_pcm = &pcm_buf[pos * num_channels]; int samples_read = op_read_float(localRecP->opus, _pcm, samples_needed * num_channels, NULL); if(samples_read == 0) { // guess we're at the end of the stream break; } else if(samples_read < 0) { result = imDecompressionError; } else { for(int c=0; c < localRecP->numChannels; c++) { for(int i=0; i < samples_read; i++) { audioRec7->buffer[swizzle[c]][pos + i] = _pcm[(i * num_channels) + c]; } } samples_needed -= samples_read; pos += samples_read; } } free(pcm_buf); } } } else if(localRecP->fileType == FLAC_filetype && localRecP->flac != NULL) { try { //localRecP->flac->reset(); assert(audioRec7->position >= 0); // not handling Premiere's continuous reads assert(localRecP->flac->get_channels() == localRecP->numChannels); long samples_needed = audioRec7->size; localRecP->flac->set_buffers(audioRec7->buffer, samples_needed, audioRec7->position); // Calling seek will cause flac to "write" some audio, of course! bool sought = localRecP->flac->seek_absolute(audioRec7->position); bool eof = false; size_t buffer_position = 0; if(sought) { do{ size_t new_buffer_position = localRecP->flac->get_pos(); int samples_read = (new_buffer_position - buffer_position); if(samples_read > 0) { samples_needed -= samples_read; } else eof = true; buffer_position = new_buffer_position; if(samples_needed > 0 && !eof) { bool processed = localRecP->flac->process_single(); if(!processed) samples_needed = 0; } }while(samples_needed > 0 && !eof); } localRecP->flac->set_buffers(NULL, 0, 0); // don't trust libflac not to write at inopportune times } catch(...) { result = imDecompressionError; } } } stdParms->piSuites->memFuncs->unlockHandle(reinterpret_cast<char**>(ldataH)); assert(result == malNoError); return result; }
prMALError SDKGetInfo8( imStdParms *stdParms, imFileAccessRec8 *fileAccessInfo8, imFileInfoRec8 *SDKFileInfo8) { prMALError result = malNoError; SDKFileInfo8->hasDataRate = kPrFalse; // private data assert(SDKFileInfo8->privatedata); ImporterLocalRec8H ldataH = reinterpret_cast<ImporterLocalRec8H>(SDKFileInfo8->privatedata); stdParms->piSuites->memFuncs->lockHandle(reinterpret_cast<char**>(ldataH)); ImporterLocalRec8Ptr localRecP = reinterpret_cast<ImporterLocalRec8Ptr>( *ldataH ); SDKFileInfo8->hasVideo = kPrFalse; SDKFileInfo8->hasAudio = kPrFalse; if(localRecP) { if(localRecP->fileType == Ogg_filetype && localRecP->vf != NULL) { OggVorbis_File &vf = *localRecP->vf; vorbis_info *info = ov_info(&vf, 0); // Audio information SDKFileInfo8->hasAudio = kPrTrue; SDKFileInfo8->audInfo.numChannels = info->channels; SDKFileInfo8->audInfo.sampleRate = info->rate; SDKFileInfo8->audInfo.sampleType = kPrAudioSampleType_Compressed; SDKFileInfo8->audDuration = ov_pcm_total(&vf, 0); } else if(localRecP->fileType == Opus_filetype && localRecP->opus != NULL) { SDKFileInfo8->hasAudio = kPrTrue; SDKFileInfo8->audInfo.numChannels = op_channel_count(localRecP->opus, -1); SDKFileInfo8->audInfo.sampleRate = 48000; // Ogg Opus always uses 48 kHz SDKFileInfo8->audInfo.sampleType = kPrAudioSampleType_Compressed; SDKFileInfo8->audDuration = op_pcm_total(localRecP->opus, -1); } else if(localRecP->fileType == FLAC_filetype && localRecP->flac != NULL) { try { SDKFileInfo8->hasAudio = kPrTrue; SDKFileInfo8->audInfo.numChannels = localRecP->flac->get_channels(); SDKFileInfo8->audInfo.sampleRate = localRecP->flac->get_sample_rate(); int bitDepth = localRecP->flac->get_bits_per_sample(); SDKFileInfo8->audInfo.sampleType = bitDepth == 8 ? kPrAudioSampleType_8BitInt : bitDepth == 16 ? kPrAudioSampleType_16BitInt : bitDepth == 24 ? kPrAudioSampleType_24BitInt : bitDepth == 32 ? kPrAudioSampleType_32BitInt : bitDepth == 64 ? kPrAudioSampleType_64BitFloat : kPrAudioSampleType_Compressed; SDKFileInfo8->audDuration = localRecP->flac->get_total_samples(); } catch(...) { result = imBadFile; } } localRecP->audioSampleRate = SDKFileInfo8->audInfo.sampleRate; localRecP->numChannels = SDKFileInfo8->audInfo.numChannels; if(SDKFileInfo8->audInfo.numChannels > 2 && SDKFileInfo8->audInfo.numChannels != 6) { // Premiere can't handle anything but Mono, Stereo, and 5.1 result = imUnsupportedAudioFormat; } } stdParms->piSuites->memFuncs->unlockHandle(reinterpret_cast<char**>(ldataH)); return result; }
bool CSoundFile::ReadOpusSample(SAMPLEINDEX sample, FileReader &file) { file.Rewind(); #if defined(MPT_WITH_OPUSFILE) int rate = 0; int channels = 0; std::vector<int16> raw_sample_data; FileReader initial = file.GetChunk(65536); // 512 is recommended by libopusfile if(op_test(NULL, initial.GetRawData<unsigned char>(), initial.GetLength()) != 0) { return false; } OggOpusFile *of = op_open_memory(file.GetRawData<unsigned char>(), file.GetLength(), NULL); if(!of) { return false; } rate = 48000; channels = op_channel_count(of, -1); if(rate <= 0 || channels <= 0) { op_free(of); of = NULL; return false; } if(channels > 2 || op_link_count(of) != 1) { // We downmix multichannel to stereo as recommended by Opus specification in // case we are not able to handle > 2 channels. // We also decode chained files as stereo even if they start with a mono // stream, which simplifies handling of link boundaries for us. channels = 2; } std::vector<int16> decodeBuf(120 * 48000 / 1000); // 120ms (max Opus packet), 48kHz bool eof = false; while(!eof) { int framesRead = 0; if(channels == 2) { framesRead = op_read_stereo(of, &(decodeBuf[0]), static_cast<int>(decodeBuf.size())); } else if(channels == 1) { framesRead = op_read(of, &(decodeBuf[0]), static_cast<int>(decodeBuf.size()), NULL); } if(framesRead > 0) { raw_sample_data.insert(raw_sample_data.end(), decodeBuf.begin(), decodeBuf.begin() + (framesRead * channels)); } else if(framesRead == 0) { eof = true; } else if(framesRead == OP_HOLE) { // continue } else { // other errors are fatal, stop decoding eof = true; } } op_free(of); of = NULL; if(raw_sample_data.empty()) { return false; } DestroySampleThreadsafe(sample); strcpy(m_szNames[sample], ""); Samples[sample].Initialize(); Samples[sample].nC5Speed = rate; Samples[sample].nLength = raw_sample_data.size() / channels; Samples[sample].uFlags.set(CHN_16BIT); Samples[sample].uFlags.set(CHN_STEREO, channels == 2); Samples[sample].AllocateSample(); std::copy(raw_sample_data.begin(), raw_sample_data.end(), Samples[sample].pSample16); Samples[sample].Convert(MOD_TYPE_IT, GetType()); Samples[sample].PrecomputeLoops(*this, false); return Samples[sample].pSample != nullptr; #else // !MPT_WITH_OPUSFILE MPT_UNREFERENCED_PARAMETER(sample); MPT_UNREFERENCED_PARAMETER(file); return false; #endif // MPT_WITH_OPUSFILE }
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; }