OggOpusReader::OggOpusReader(Stream *fp) : fw(fp) { OpusFileCallbacks cb; int error = 0; memset(&cb, 0, sizeof(cb)); cb.read_func = iop_read_func; cb.seek_func = iop_seek_func; cb.close_func = iop_close_func; cb.tell_func = iop_tell_func; fp->seek(0, SEEK_SET); if(!(opfile = op_open_callbacks((void*)fp, &cb, NULL, 0, &error))) { switch(error) { default: throw MDFN_Error(0, _("opusfile: error code: %d(%s)", error, op_errstring(error))); break; case OP_ENOTFORMAT: throw(0); break; } } }
Error AudioStreamPlaybackOpus::set_file(const String &p_file) { file=p_file; stream_valid=false; Error err; f=FileAccess::open(file,FileAccess::READ,&err); if (err) { ERR_FAIL_COND_V( err, err ); } int _err; opus_file = op_open_callbacks(f,&_op_callbacks,NULL,0,&_err); switch (_err) { case OP_EREAD: { // - Can't read the file. memdelete(f); f=NULL; ERR_FAIL_V( ERR_FILE_CANT_READ ); } break; case OP_EVERSION: // - Unrecognized version number. case OP_ENOTFORMAT: // - Stream is not Opus data. case OP_EIMPL : { // - Stream used non-implemented feature. memdelete(f); f=NULL; ERR_FAIL_V( ERR_FILE_UNRECOGNIZED ); } break; case OP_EBADLINK: // - Failed to find old data after seeking. case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks. case OP_EBADHEADER: { // - Invalid or mising Opus bitstream header. memdelete(f); f=NULL; ERR_FAIL_V( ERR_FILE_CORRUPT ); } break; case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption. memdelete(f); f=NULL; ERR_FAIL_V( ERR_BUG ); } break; } const OpusHead *oinfo = op_head(opus_file,-1); stream_channels=oinfo->channel_count; pre_skip=oinfo->pre_skip; frames_mixed=pre_skip; ogg_int64_t len = op_pcm_total(opus_file,-1); if(len < 0) { length = 0; } else { length=(len/osrate); } op_free(opus_file); memdelete(f); f=NULL; stream_valid=true; return OK; }
int AudioStreamPlaybackOpus::mix(int16_t* p_bufer,int p_frames) { if (!playing) return 0; int total=p_frames; while (true) { int todo = p_frames; if (todo==0 || todo<MIN_MIX) { break; } int ret=op_read(opus_file,(opus_int16*)p_bufer,todo*stream_channels,¤t_section); if (ret<0) { playing = false; ERR_EXPLAIN("Error reading Opus File: "+file); ERR_BREAK(ret<0); } else if (ret==0) { // end of song, reload? op_free(opus_file); _close_file(); f=FileAccess::open(file,FileAccess::READ); int errv = 0; opus_file = op_open_callbacks(f,&_op_callbacks,NULL,0,&errv); if (errv!=0) { playing=false; break; // :( } if (!has_loop()) { playing=false; repeats=1; break; } if (loop_restart_time) { bool ok = op_pcm_seek(opus_file, (loop_restart_time*osrate)+pre_skip)==0; if (!ok) { playing=false; ERR_PRINT("loop restart time rejected") } frames_mixed=(loop_restart_time*osrate)+pre_skip; } else {
bool OpusDecoder::Open(FILE* file) { finished = false; int res; OpusFileCallbacks callbacks = {custom_read, custom_seek, custom_tell, custom_close}; oof = op_open_callbacks(file, &callbacks, nullptr, 0, &res); if (res != 0) { error_message = "Opus: Error reading file"; op_free(oof); fclose(file); return false; } return true; }
Error AudioStreamPlaybackOpus::_load_stream() { ERR_FAIL_COND_V(!stream_valid,ERR_UNCONFIGURED); _clear_stream(); if (file=="") return ERR_INVALID_DATA; Error err; f=FileAccess::open(file,FileAccess::READ,&err); if (err) { ERR_FAIL_COND_V( err, err ); } int _err = 0; opus_file = op_open_callbacks(f,&_op_callbacks,NULL,0,&_err); switch (_err) { case OP_EREAD: { // - Can't read the file. memdelete(f); f=NULL; ERR_FAIL_V( ERR_FILE_CANT_READ ); } break; case OP_EVERSION: // - Unrecognized version number. case OP_ENOTFORMAT: // - Stream is not Opus data. case OP_EIMPL : { // - Stream used non-implemented feature. memdelete(f); f=NULL; ERR_FAIL_V( ERR_FILE_UNRECOGNIZED ); } break; case OP_EBADLINK: // - Failed to find old data after seeking. case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks. case OP_EBADHEADER: { // - Invalid or mising Opus bitstream header. memdelete(f); f=NULL; ERR_FAIL_V( ERR_FILE_CORRUPT ); } break; case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption. memdelete(f); f=NULL; ERR_FAIL_V( ERR_BUG ); } break; } repeats=0; stream_loaded=true; return OK; }
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; }
/* ================= S_OggOpus_CodecOpenStream ================= */ snd_stream_t *S_OggOpus_CodecOpenStream( const char *filename ) { snd_stream_t *stream; // Opus codec control structure OggOpusFile *of; // some variables used to get informations about the file const OpusHead *opusInfo; ogg_int64_t numSamples; // check if input is valid if ( !filename ) { return NULL; } // Open the stream stream = S_CodecUtilOpen( filename, &opus_codec ); if ( !stream ) { return NULL; } // open the codec with our callbacks and stream as the generic pointer of = op_open_callbacks( stream, &S_OggOpus_Callbacks, NULL, 0, NULL ); if ( !of ) { S_CodecUtilClose( &stream ); return NULL; } // the stream must be seekable if ( !op_seekable( of ) ) { op_free( of ); S_CodecUtilClose( &stream ); return NULL; } // get the info about channels and rate opusInfo = op_head( of, -1 ); if ( !opusInfo ) { op_free( of ); S_CodecUtilClose( &stream ); return NULL; } if ( opusInfo->stream_count != 1 ) { op_free( of ); S_CodecUtilClose( &stream ); Com_Printf( "Only Ogg Opus files with one stream are support\n" ); return NULL; } if ( opusInfo->channel_count != 1 && opusInfo->channel_count != 2 ) { op_free( of ); S_CodecUtilClose( &stream ); Com_Printf( "Only mono and stereo Ogg Opus files are supported\n" ); return NULL; } // get the number of sample-frames in the file numSamples = op_pcm_total( of, -1 ); // fill in the info-structure in the stream stream->info.rate = 48000; stream->info.width = OPUS_SAMPLEWIDTH; stream->info.channels = opusInfo->channel_count; stream->info.samples = numSamples; stream->info.size = stream->info.samples * stream->info.channels * stream->info.width; stream->info.dataofs = 0; // We use stream->pos for the file pointer in the compressed ogg file stream->pos = 0; // We use the generic pointer in stream for the opus codec control structure stream->ptr = of; return stream; }
opus_file_t open_opus_file(const util::Path &path) { if (not path.is_file()) { throw audio::Error{ ERR << "Audio file not found: " << path }; } opus_file_t op_file; int op_err = 0; //asm("int $3"); // check if the file can be opened directly auto native_path = path.resolve_native_path(); if (false && native_path.size() > 0) { op_file.handle = { op_open_file(native_path.c_str(), &op_err), opus_deleter }; } else { // open the file and move the handle to the heap op_file.file = std::make_unique<util::File>(); *op_file.file = path.open_r(); op_file.handle = { op_open_callbacks(op_file.file.get(), &opus_access_funcs, nullptr, 0, &op_err), opus_deleter }; } if (op_err != 0) { const char *reason; switch (op_err) { case OP_EREAD: reason = "read/seek/tell failed or data has changed"; break; case OP_EFAULT: reason = "opus failed to allocate memory " "or something else bad happened internally"; break; case OP_EIMPL: reason = "Stream used an unsupported feature"; break; case OP_EINVAL: reason = "seek() worked, but tell() did not, " "or initial_bytes != start seek pos"; break; case OP_ENOTFORMAT: reason = "Data didn't contain opus stream"; break; case OP_EBADHEADER: reason = "Header packet was invalid or missing"; break; case OP_EVERSION: reason = "ID header has unrecognized version"; break; case OP_EBADLINK: reason = "Data we already saw before seeking not found"; break; case OP_EBADTIMESTAMP: reason = "Validity check for first/last timestamp failed"; break; default: reason = "Unknown other error in opusfile"; break; } throw audio::Error{ ERR << "Could not open opus file: " << path << " = '" << native_path << "': " << reason }; } return op_file; }
static gboolean xmms_opus_init (xmms_xform_t *xform) { xmms_opus_data_t *data; gint ret; guint playtime; const gchar *metakey; g_return_val_if_fail (xform, FALSE); data = g_new0 (xmms_opus_data_t, 1), data->callbacks.read = opus_callback_read; data->callbacks.close = opus_callback_close; data->callbacks.tell = opus_callback_tell; data->callbacks.seek = opus_callback_seek; data->current = -1; xmms_xform_private_data_set (xform, data); data->opusfile = op_open_callbacks (xform, &data->callbacks, NULL, 0, &ret); if (ret) { return FALSE; } playtime = op_pcm_total (data->opusfile, -1) / 48000; if (playtime != OP_EINVAL) { gint filesize; metakey = XMMS_MEDIALIB_ENTRY_PROPERTY_SIZE; if (xmms_xform_metadata_get_int (xform, metakey, &filesize)) { xmms_opus_set_duration (xform, playtime); } } xmms_opus_read_metadata (xform, data); /* xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "audio/pcm", XMMS_STREAM_TYPE_FMT_FORMAT, XMMS_SAMPLE_FORMAT_FLOAT, XMMS_STREAM_TYPE_FMT_CHANNELS, data->channels, XMMS_STREAM_TYPE_FMT_SAMPLERATE, 48000, XMMS_STREAM_TYPE_END); */ xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "audio/pcm", XMMS_STREAM_TYPE_FMT_FORMAT, XMMS_SAMPLE_FORMAT_S16, XMMS_STREAM_TYPE_FMT_CHANNELS, data->channels, XMMS_STREAM_TYPE_FMT_SAMPLERATE, 48000, XMMS_STREAM_TYPE_END); return TRUE; }
AudioData LoadOpusCodec(std::string filename) { std::string audioFile; try { audioFile = FS::PakPath::ReadFile(filename); } catch (std::system_error& err) { audioLogs.Warn("Failed to open %s: %s", filename, err.what()); return AudioData(); } OpusDataSource dataSource = {&audioFile, 0}; OggOpusFile* opusFile = op_open_callbacks(&dataSource, &Opus_Callbacks, nullptr, 0, nullptr); if (!opusFile) { audioLogs.Warn("Error while reading %s", filename); return AudioData(); } const OpusHead* opusInfo = op_head(opusFile, -1); if (!opusInfo) { op_free(opusFile); audioLogs.Warn("Could not read OpusHead in %s", filename); return AudioData(); } if (opusInfo->stream_count != 1) { op_free(opusFile); audioLogs.Warn("Only one stream is supported in Opus files: %s", filename); return AudioData(); } if (opusInfo->channel_count != 1 && opusInfo->channel_count != 2) { op_free(opusFile); audioLogs.Warn("Only mono and stereo Opus files are supported: %s", filename); return AudioData(); } const int sampleWidth = 2; int sampleRate = 48000; int numberOfChannels = opusInfo->channel_count; // The buffer is big enough to hold 120ms worth of samples per channel opus_int16* buffer = new opus_int16[numberOfChannels * 5760]; int samplesPerChannelRead = 0; std::vector<opus_int16> samples; while ((samplesPerChannelRead = op_read(opusFile, buffer, sampleWidth * numberOfChannels * 5760, nullptr)) > 0) { std::copy_n(buffer, samplesPerChannelRead * numberOfChannels, std::back_inserter(samples)); } op_free(opusFile); char* rawSamples = new char[sampleWidth * samples.size()]; std::copy_n(reinterpret_cast<char*>(samples.data()), sampleWidth * samples.size(), rawSamples); return AudioData(sampleRate, sampleWidth, numberOfChannels, samples.size() * sampleWidth, rawSamples); }
static OggOpusFile * opus_file_open(DB_FILE *fp) { int res = 0; return op_open_callbacks(fp, &opcb, NULL, 0, &res); }
static qboolean S_OPUS_CodecOpenStream (snd_stream_t *stream) { OggOpusFile *opFile; const OpusHead *op_info; long numstreams; int res; opFile = op_open_callbacks(&stream->fh, &opc_qfs, NULL, 0, &res); if (!opFile) { Con_Printf("%s is not a valid Opus file (error %i).\n", stream->name, res); goto _fail; } stream->priv = opFile; if (!op_seekable(opFile)) { Con_Printf("Opus stream %s not seekable.\n", stream->name); goto _fail; } op_info = op_head(opFile, -1); if (!op_info) { Con_Printf("Unable to get stream information for %s.\n", stream->name); goto _fail; } /* FIXME: handle section changes */ numstreams = op_info->stream_count; if (numstreams != 1) { Con_Printf("More than one (%ld) stream in %s\n", (long)op_info->stream_count, stream->name); goto _fail; } if (op_info->channel_count != 1 && op_info->channel_count != 2) { Con_Printf("Unsupported number of channels %d in %s\n", op_info->channel_count, stream->name); goto _fail; } /* All Opus audio is coded at 48 kHz, and should also be decoded * at 48 kHz for playback: info->input_sample_rate only tells us * the sampling rate of the original input before opus encoding. * S_RawSamples() shall already downsample this, as necessary. */ stream->info.rate = 48000; stream->info.channels = op_info->channel_count; /* op_read() yields 16-bit output using native endian ordering: */ stream->info.bits = 16; stream->info.width = 2; return true; _fail: if (opFile) op_free(opFile); return false; }