示例#1
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;
}
示例#2
0
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;
}
示例#3
0
/*
 * 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;
}
示例#4
0
void cmdMusic(const std::string& args) {
	// terrible hack for testing audio
	// decode the entire file into a big buffer all at once

	OggOpusFile* opusFile;
	if (args.length() == 0) {
		opusFile = op_open_memory(
		    static_cast<const uint8_t*>(EMBED_DATA(Who_Likes_to_Party_Kevin_MacLeod_incompetech_opus)),
		    EMBED_SIZE(Who_Likes_to_Party_Kevin_MacLeod_incompetech_opus),
		    NULL);
	} else {
		opusFile = op_open_file(args.c_str(), NULL);
	}

	if (!opusFile) {
		narf::console->println("Failed to open music file " + args);
		return;
	}

	auto newMusicSize = op_pcm_total(opusFile, -1) * 2; // stereo
	auto newMusicSamples = new float[newMusicSize];

	size_t decoded = 0;
	while (decoded < newMusicSize) {
		auto rc = op_read_float_stereo(opusFile, newMusicSamples + decoded, newMusicSize - decoded);
		if (rc < 0) {
			narf::console->println("opusfile decode failed");
			decoded = 0;
			break;
		}

		decoded += rc * 2; // return code is number of samples per channel, and we are decoding in stereo
	}

	if (decoded != newMusicSize) {
		narf::console->println("opusfile decode returned wrong number of samples (got " + std::to_string(decoded) + ", expected " + std::to_string(newMusicSize) + ")");
		delete[] newMusicSamples;
		newMusicSamples = nullptr;
		newMusicSize = 0;
	}

	SDL_LockMutex(musicMutex);

	if (musicSamples) {
		delete[] musicSamples;
	}

	musicSamples = newMusicSamples;
	musicSize = newMusicSize;
	musicCursor = 0;

	SDL_UnlockMutex(musicMutex);

	op_free(opusFile);
}
示例#5
0
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);
}
示例#6
0
static off_t
sample_offset(OggOpusFile *opusfile, const opus_int64 sample)
{
    if (sample <= 0 || sample == op_pcm_total(opusfile, -1))
        return 0;

    if (op_pcm_seek(opusfile, sample)) {
        return -1;
    }

    return op_raw_tell(opusfile);
}
示例#7
0
Result SoundSourceOpus::open() {
    int error = 0;
    QByteArray qBAFilename = m_qFilename.toLocal8Bit();

    m_ptrOpusFile = op_open_file(qBAFilename.constData(), &error);
    if ( m_ptrOpusFile == NULL ) {
        qDebug() << "opus: Input does not appear to be an Opus bitstream.";
        m_lFilelength = 0;
        return ERR;
    }

    // opusfile lib all ways gives you 48000 samplerate and stereo 16 bit sample
    m_iChannels = 2;
    this->setBitrate((int)op_bitrate_instant(m_ptrOpusFile));
    this->setSampleRate(48000);
    this->setChannels(m_iChannels);


    if (m_iChannels > 2) {
        qDebug() << "opus: No support for more than 2 m_iChannels!";
        op_free(m_ptrOpusFile);
        m_lFilelength = 0;
        return ERR;
    }

    // op_pcm_total returns the total number of frames in the ogg file. The
    // frame is the channel-independent measure of samples. The total samples in
    // the file is m_iChannels * ov_pcm_total. rryan 7/2009 I verified this by
    // hand. a 30 second long 48khz mono ogg and a 48khz stereo ogg both report
    // 1440000 for op_pcm_total.
    qint64 ret = op_pcm_total(m_ptrOpusFile, -1) * 2;

    // qDebug() << m_qFilename << "chan:" << m_iChannels << "sample:" << m_iSampleRate << "LEN:" << ret;


    if (ret >= 0) {
        // We pretend that the file is stereo to the rest of the world.
        m_lFilelength = ret;
    } else { //error
        if (ret == OP_EINVAL) {
            //The file is not seekable. Not sure if any action is needed.
            qDebug() << "opus: file is not seekable " << m_qFilename;
        }
    }

    return OK;
}
示例#8
0
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;
}
示例#9
0
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);
}
示例#10
0
static gint64
xmms_opus_seek (xmms_xform_t *xform, gint64 samples,
                  xmms_xform_seek_mode_t whence, xmms_error_t *err)
{
	xmms_opus_data_t *data;

	g_return_val_if_fail (whence == XMMS_XFORM_SEEK_SET, -1);
	g_return_val_if_fail (xform, -1);

	data = xmms_xform_private_data_get (xform);
	g_return_val_if_fail (data, FALSE);

	if (samples > op_pcm_total (data->opusfile, -1)) {
		xmms_log_error ("Trying to seek past end of stream");
		return -1;
	}

	op_pcm_seek (data->opusfile, samples);

	return samples;
}
示例#11
0
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;
}
示例#12
0
/*
=================
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;
}
示例#13
0
/*
   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

}
示例#14
0
static int
opusdec_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
    opusdec_info_t *info = (opusdec_info_t *)_info;

    if (!info->info.file) {
        deadbeef->pl_lock ();
	const char *uri = strdupa (deadbeef->pl_find_meta (it, ":URI"));
        deadbeef->pl_unlock ();
        DB_FILE *fp = deadbeef->fopen (uri);

        if (!fp) {
            return -1;
        }
        info->info.file = fp;
        info->it = it;
        deadbeef->pl_item_ref (it);
    }

    info->opusfile = opus_file_open (info->info.file);
    if (!info->opusfile) {
        return -1;
    }

    const OpusHead *head = op_head (info->opusfile, 0);

    if (head->channel_count > 8) {
        trace ("opus: the track has %d channels, but 8 is max supported.\n");
        return -1;
    }

    // take this parameters from your input file
    // we set constants for clarity sake
    _info->fmt.bps = 32;
    _info->fmt.is_float = 1;
    _info->fmt.channels = head->channel_count;
    _info->fmt.samplerate  = 48000;
    if (head->mapping_family == 1) {
        info->channelmap = oggedit_vorbis_channel_map (head->channel_count);
    }

    for (int i = 0; i < (_info->fmt.channels&0x1f); i++) {
        _info->fmt.channelmask |= 1 << i;
    }
    _info->readpos = 0;
    _info->plugin = &plugin;

    // set all gain adjustment to 0, because deadbeef is performing that.
    op_set_gain_offset (info->opusfile, OP_ABSOLUTE_GAIN, 0);

    if (info->info.file->vfs->is_streaming ()) {
        deadbeef->pl_item_set_startsample (it, 0);
        if (deadbeef->pl_get_item_duration (it) < 0) {
            deadbeef->pl_item_set_endsample (it, -1);
        }
        else {
            deadbeef->pl_item_set_endsample (it, op_pcm_total (info->opusfile, -1) - 1);
        }

        if (update_vorbis_comments (it, info->opusfile, -1))
            return -1;

        deadbeef->pl_set_meta_int(it, ":TRACKNUM", 0);
    }
    else {
        opusdec_seek_sample (_info, 0);
    }

    deadbeef->pl_replace_meta (it, "!FILETYPE", "Ogg Opus");
    deadbeef->pl_set_meta_int (it, ":CHANNELS", head->channel_count);

    info->cur_bit_stream = -1;

    return 0;
}
示例#15
0
int64 OggOpusReader::FrameCount(void)
{
 return(op_pcm_total(pvfile, -1));
}
示例#16
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();
			}
		}
	}
}
示例#17
0
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;
}
示例#18
0
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;
}
示例#19
0
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;
}