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 }
prMALError SDKOpenFile8( imStdParms *stdParms, imFileRef *SDKfileRef, imFileOpenRec8 *SDKfileOpenRec8) { prMALError result = malNoError; ImporterLocalRec8H localRecH = NULL; ImporterLocalRec8Ptr localRecP = NULL; if(SDKfileOpenRec8->privatedata) { localRecH = (ImporterLocalRec8H)SDKfileOpenRec8->privatedata; stdParms->piSuites->memFuncs->lockHandle(reinterpret_cast<char**>(localRecH)); localRecP = reinterpret_cast<ImporterLocalRec8Ptr>( *localRecH ); } else { localRecH = (ImporterLocalRec8H)stdParms->piSuites->memFuncs->newHandle(sizeof(ImporterLocalRec8)); SDKfileOpenRec8->privatedata = (PrivateDataPtr)localRecH; stdParms->piSuites->memFuncs->lockHandle(reinterpret_cast<char**>(localRecH)); localRecP = reinterpret_cast<ImporterLocalRec8Ptr>( *localRecH ); localRecP->vf = NULL; localRecP->opus = NULL; localRecP->flac = NULL; localRecP->importerID = SDKfileOpenRec8->inImporterID; localRecP->fileType = SDKfileOpenRec8->fileinfo.filetype; } SDKfileOpenRec8->fileinfo.fileref = *SDKfileRef = reinterpret_cast<imFileRef>(imInvalidHandleValue); if(localRecP) { const prUTF16Char *path = SDKfileOpenRec8->fileinfo.filepath; #ifdef PRWIN_ENV HANDLE fileH = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(fileH != imInvalidHandleValue) { SDKfileOpenRec8->fileinfo.fileref = *SDKfileRef = fileH; } else result = imFileOpenFailed; #else FSIORefNum refNum = CAST_REFNUM(imInvalidHandleValue); CFStringRef filePathCFSR = CFStringCreateWithCharacters(NULL, path, prUTF16CharLength(path)); CFURLRef filePathURL = CFURLCreateWithFileSystemPath(NULL, filePathCFSR, kCFURLPOSIXPathStyle, false); if(filePathURL != NULL) { FSRef fileRef; Boolean success = CFURLGetFSRef(filePathURL, &fileRef); if(success) { HFSUniStr255 dataForkName; FSGetDataForkName(&dataForkName); OSErr err = FSOpenFork( &fileRef, dataForkName.length, dataForkName.unicode, fsRdWrPerm, &refNum); } CFRelease(filePathURL); } CFRelease(filePathCFSR); if(CAST_FILEREF(refNum) != imInvalidHandleValue) { SDKfileOpenRec8->fileinfo.fileref = *SDKfileRef = CAST_FILEREF(refNum); } else result = imFileOpenFailed; #endif } if(result == malNoError) { localRecP->fileType = SDKfileOpenRec8->fileinfo.filetype; assert(0 == ogg_tell_func(static_cast<void *>(*SDKfileRef))); if(localRecP->fileType == Ogg_filetype) { localRecP->vf = new OggVorbis_File; OggVorbis_File &vf = *localRecP->vf; int ogg_err = ov_open_callbacks(static_cast<void *>(*SDKfileRef), &vf, NULL, 0, g_ov_callbacks); if(ogg_err == OV_OK) { if( ov_streams(&vf) == 0 ) { result = imFileHasNoImportableStreams; ov_clear(&vf); } else if( !ov_seekable(&vf) ) { result = imBadFile; } } else result = imBadHeader; } else if(localRecP->fileType == Opus_filetype) { int _error = 0; localRecP->opus = op_open_callbacks(static_cast<void *>(*SDKfileRef), &g_opusfile_callbacks, NULL, 0, &_error); if(localRecP->opus != NULL && _error == 0) { assert(op_link_count(localRecP->opus) == 1); // we're not really handling multi-link scenarios } else result = imBadHeader; } else if(localRecP->fileType == FLAC_filetype) { try { localRecP->flac = new OurDecoder(*SDKfileRef); localRecP->flac->set_md5_checking(true); FLAC__StreamDecoderInitStatus init_status = localRecP->flac->init(); assert(init_status == FLAC__STREAM_DECODER_INIT_STATUS_OK && localRecP->flac->is_valid()); bool ok = localRecP->flac->process_until_end_of_metadata(); assert(ok); } catch(...) { result = imBadHeader; } } } // close file and delete private data if we got a bad file if(result != malNoError) { if(SDKfileOpenRec8->privatedata) { stdParms->piSuites->memFuncs->disposeHandle(reinterpret_cast<PrMemoryHandle>(SDKfileOpenRec8->privatedata)); SDKfileOpenRec8->privatedata = NULL; } } else { stdParms->piSuites->memFuncs->unlockHandle(reinterpret_cast<char**>(SDKfileOpenRec8->privatedata)); } return result; }
static DB_playItem_t * opusdec_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { return NULL; } int64_t fsize = deadbeef->fgetlength (fp); if (fp->vfs->is_streaming ()) { DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->plt_set_item_duration (plt, it, -1); deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); deadbeef->fclose (fp); return after; } OggOpusFile *opusfile = opus_file_open (fp); if (!opusfile) { deadbeef->fclose (fp); return NULL; } long nstreams = op_link_count (opusfile); int64_t currentsample = 0; for (int stream = 0; stream < nstreams; stream++) { const OpusHead *head = op_head (opusfile, stream); if (!head) { continue; } int64_t totalsamples = op_pcm_total (opusfile, stream); const float duration = totalsamples / 48000.f; DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->pl_set_meta_int (it, ":TRACKNUM", stream); deadbeef->plt_set_item_duration (plt, it, duration); if (nstreams > 1) { deadbeef->pl_item_set_startsample (it, currentsample); deadbeef->pl_item_set_endsample (it, currentsample + totalsamples - 1); deadbeef->pl_set_item_flags (it, DDB_IS_SUBTRACK); } if (update_vorbis_comments (it, opusfile, stream)) continue; int samplerate = 48000; int64_t startsample = deadbeef->pl_item_get_startsample (it); int64_t endsample = deadbeef->pl_item_get_endsample (it); const off_t start_offset = sample_offset(opusfile, startsample-1); const off_t end_offset = sample_offset(opusfile, endsample); char *filetype = NULL; const off_t stream_size = oggedit_opus_stream_info(deadbeef->fopen(fname), start_offset, end_offset, &filetype); if (filetype) { deadbeef->pl_replace_meta(it, ":FILETYPE", filetype); free(filetype); } if (stream_size > 0) { set_meta_ll(it, ":OPUS_STREAM_SIZE", stream_size); deadbeef->pl_set_meta_int(it, ":BITRATE", 8.f * samplerate * stream_size / totalsamples / 1000); } set_meta_ll (it, ":FILE_SIZE", fsize); deadbeef->pl_set_meta_int (it, ":CHANNELS", head->channel_count); deadbeef->pl_set_meta_int (it, ":SAMPLERATE", samplerate); if (nstreams == 1) { DB_playItem_t *cue = deadbeef->plt_process_cue (plt, after, it, totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); op_free(opusfile); deadbeef->fclose (fp); return cue; } } else { currentsample += totalsamples; } after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); } op_free(opusfile); deadbeef->fclose (fp); return after; }