long SoundSourceOpus::seek(long filepos) {
    // In our speak, filepos is a sample in the file abstraction (i.e. it's
    // stereo no matter what). filepos/2 is the frame we want to seek to.
    if (filepos % 2 != 0) {
        qDebug() << "SoundSourceOpus got non-even seek target.";
        filepos--;
    }

    if (op_seekable(m_ptrOpusFile)) {
        // I can't say why filepos have to divide by two
        // Have no idea.. probably seek point is mono..
        if (op_pcm_seek(m_ptrOpusFile, filepos / 2) != 0) {
            // This is totally common (i.e. you're at EOF). Let's not leave this
            // qDebug on.

            qDebug() << "opus: Seek ERR on seekable.";
        }

        // qDebug() << "Wanted:" << filepos << "GOT:" << op_pcm_tell(m_ptrOpusFile);


        //return op_pcm_tell(m_ptrOpusFile);
        // We are here allways!
        return filepos;
    } else {
        qDebug() << "opus: Seek ERR at file " << m_qFilename;
        return 0;
    }
    return filepos;
}
Beispiel #2
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;
}
/*
=================
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;
}
Beispiel #4
0
static int
opusdec_read (DB_fileinfo_t *_info, char *bytes, int size) {
    opusdec_info_t *info = (opusdec_info_t *)_info;

    // Work round some streamer limitations and infobar issue #22
    if (info->new_track && is_playing_track(info->new_track)) {
        info->new_track = NULL;
        send_event(info->it, DB_EV_TRACKINFOCHANGED);
        info->next_update = -2;
    }

    // Don't read past the end of a sub-track
    int samples_to_read = size / sizeof(float) / _info->fmt.channels;
    int64_t endsample = deadbeef->pl_item_get_endsample (info->it);
    if (endsample > 0) {
        opus_int64 samples_left = endsample - op_pcm_tell (info->opusfile);
        if (samples_left < samples_to_read) {
            samples_to_read = (int)samples_left;
            size = samples_to_read * sizeof(float) * _info->fmt.channels;
        }
    }

    // Read until we have enough bytes to satisfy streamer, or there are none left
    int bytes_read = 0;
    int ret = OP_HOLE;

    int samples_read = 0;
    while (samples_read < samples_to_read && (ret > 0 || ret == OP_HOLE))
    {
        int nframes = samples_to_read-samples_read;
        float pcm[nframes * _info->fmt.channels];
        int new_link = -1;
        ret = op_read_float(info->opusfile, pcm, nframes * _info->fmt.channels, &new_link);

        if (ret < 0) {
        }
        else if (new_link != info->cur_bit_stream && !op_seekable (info->opusfile) && new_streaming_link(info, new_link)) {
            samples_read = samples_to_read;
            break;
        }
        else if (ret > 0) {
            for (int channel = 0; channel < _info->fmt.channels; channel++) {
                const float *pcm_channel = &pcm[info->channelmap ? info->channelmap[channel] : channel];
                float *ptr = ((float *)bytes + samples_read*_info->fmt.channels) + channel;
                for (int sample = 0; sample < ret; sample ++, pcm_channel += _info->fmt.channels) {
                    *ptr = *pcm_channel;
                    ptr += _info->fmt.channels;
                }
            }
            samples_read += ret;
        }
    }
    bytes_read = samples_read * sizeof(float) * _info->fmt.channels;
    info->currentsample += bytes_read / (sizeof (float) * _info->fmt.channels);


    int64_t startsample = deadbeef->pl_item_get_startsample (info->it);
    _info->readpos = (float)(op_pcm_tell(info->opusfile) - startsample) / _info->fmt.samplerate;
    if (info->set_bitrate && _info->readpos > info->next_update) {
        const int rate = (int)op_bitrate_instant(info->opusfile) / 1000;
        if (rate > 0) {
            deadbeef->streamer_set_bitrate(rate);
            info->next_update = info->next_update <= 0 ? info->next_update + 1 : _info->readpos + 5;
        }
    }

    return bytes_read;
}
Beispiel #5
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();
			}
		}
	}
}
Beispiel #6
0
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;
}