int decoder_ogg_read( snd_stream_t *stream, int bytes, void *buffer )
{
	snd_ogg_stream_t *ogg_stream = (snd_ogg_stream_t *)stream->ptr;
	int bitstream, bytes_read, bytes_read_total = 0;
	int holes = 0;

	do
	{
#ifdef ENDIAN_BIG
		bytes_read = qov_read( &ogg_stream->vorbisfile, (char *)buffer+bytes_read_total, bytes-bytes_read_total, 1, 2, 1,
			&bitstream );
#elif defined (ENDIAN_LITTLE)
		bytes_read = qov_read( &ogg_stream->vorbisfile, (char *)buffer+bytes_read_total, bytes-bytes_read_total, 0, 2, 1,
			&bitstream );
#else
#error "runtime endianess detection support missing"
#endif
		if( bytes_read < 0 )
		{
			if( bytes_read == OV_HOLE )
			{
				if( holes++ == 3 )
					break;
				continue;
			}
			break;
		}
		bytes_read_total += bytes_read;
	}
	while( (bytes_read > 0 || bytes_read == OV_HOLE) && bytes_read_total < bytes );

	return bytes_read_total;
}
Exemple #2
0
/*
====================
OGG_LoadVorbisFile

Load an Ogg Vorbis file into memory
====================
*/
qboolean OGG_LoadVorbisFile(const char *filename, sfx_t *sfx)
{
	unsigned char *data;
	fs_offset_t filesize;
	ov_decode_t ov_decode;
	OggVorbis_File vf;
	vorbis_info *vi;
	vorbis_comment *vc;
	double peak, gaindb;

#ifndef LINK_TO_LIBVORBIS
	if (!vf_dll)
		return false;
#endif

	// Return if already loaded
	if (sfx->fetcher != NULL)
		return true;

	// Load the file completely
	data = FS_LoadFile(filename, snd_mempool, false, &filesize);
	if (data == NULL)
		return false;

	if (developer_loading.integer >= 2)
		Con_Printf("Loading Ogg Vorbis file \"%s\"\n", filename);

	// Open it with the VorbisFile API
	ov_decode.buffer = data;
	ov_decode.ind = 0;
	ov_decode.buffsize = filesize;
	if (qov_open_callbacks(&ov_decode, &vf, NULL, 0, callbacks) < 0)
	{
		Con_Printf("error while opening Ogg Vorbis file \"%s\"\n", filename);
		Mem_Free(data);
		return false;
	}

	// Get the stream information
	vi = qov_info(&vf, -1);
	if (vi->channels < 1 || vi->channels > 2)
	{
		Con_Printf("%s has an unsupported number of channels (%i)\n",
					sfx->name, vi->channels);
		qov_clear (&vf);
		Mem_Free(data);
		return false;
	}

	sfx->format.speed = vi->rate;
	sfx->format.channels = vi->channels;
	sfx->format.width = 2;  // We always work with 16 bits samples

	sfx->total_length = qov_pcm_total(&vf, -1);

	if (snd_streaming.integer && (snd_streaming.integer >= 2 || sfx->total_length > max(sizeof(ogg_stream_perchannel_t), snd_streaming_length.value * sfx->format.speed)))
	{
		// large sounds use the OGG fetcher to decode the file on demand (but the entire file is held in memory)
		ogg_stream_persfx_t* per_sfx;
		if (developer_loading.integer >= 2)
			Con_Printf("Ogg sound file \"%s\" will be streamed\n", filename);
		per_sfx = (ogg_stream_persfx_t *)Mem_Alloc(snd_mempool, sizeof(*per_sfx));
		sfx->memsize += sizeof (*per_sfx);
		per_sfx->file = data;
		per_sfx->filesize = filesize;
		sfx->memsize += filesize;
		sfx->fetcher_data = per_sfx;
		sfx->fetcher = &ogg_fetcher;
		sfx->flags |= SFXFLAG_STREAMED;
		vc = qov_comment(&vf, -1);
		OGG_DecodeTags(vc, &sfx->loopstart, &sfx->total_length, sfx->total_length, &peak, &gaindb);
		qov_clear(&vf);
	}
	else
	{
		// small sounds are entirely loaded and use the PCM fetcher
		char *buff;
		ogg_int64_t len;
		ogg_int64_t done;
		int bs;
		long ret;
		if (developer_loading.integer >= 2)
			Con_Printf ("Ogg sound file \"%s\" will be cached\n", filename);
		len = sfx->total_length * sfx->format.channels * sfx->format.width;
		sfx->flags &= ~SFXFLAG_STREAMED;
		sfx->memsize += len;
		sfx->fetcher = &wav_fetcher;
		sfx->fetcher_data = Mem_Alloc(snd_mempool, (size_t)len);
		buff = (char *)sfx->fetcher_data;
		done = 0;
		bs = 0;
		while ((ret = qov_read(&vf, &buff[done], (int)(len - done), mem_bigendian, 2, 1, &bs)) > 0)
			done += ret;
		vc = qov_comment(&vf, -1);
		OGG_DecodeTags(vc, &sfx->loopstart, &sfx->total_length, sfx->total_length, &peak, &gaindb);
		qov_clear(&vf);
		Mem_Free(data);
	}

	if(peak)
	{
		sfx->volume_mult = min(1.0f / peak, exp(gaindb * 0.05f * log(10.0f)));
		sfx->volume_peak = peak;
		if (developer_loading.integer >= 2)
			Con_Printf ("Ogg sound file \"%s\" uses ReplayGain (gain %f, peak %f)\n", filename, sfx->volume_mult, sfx->volume_peak);
	}
	else if(gaindb != 0)
	{
		sfx->volume_mult = min(1.0f / peak, exp(gaindb * 0.05f * log(10.0f)));
		sfx->volume_peak = 1.0; // if peak is not defined, we won't trust it
		if (developer_loading.integer >= 2)
			Con_Printf ("Ogg sound file \"%s\" uses ReplayGain (gain %f, peak not defined and assumed to be %f)\n", filename, sfx->volume_mult, sfx->volume_peak);
	}

	return true;
}
Exemple #3
0
/*
====================
OGG_GetSamplesFloat
====================
*/
static void OGG_GetSamplesFloat (channel_t *ch, sfx_t *sfx, int firstsampleframe, int numsampleframes, float *outsamplesfloat)
{
	ogg_stream_perchannel_t *per_ch = (ogg_stream_perchannel_t *)ch->fetcher_data;
	ogg_stream_persfx_t *per_sfx = (ogg_stream_persfx_t *)sfx->fetcher_data;
	int f = sfx->format.width * sfx->format.channels; // bytes per frame in the buffer
	short *buf;
	int i, len;
	int newlength, done, ret;

	// if this channel does not yet have a channel fetcher, make one
	if (per_ch == NULL)
	{
		// allocate a struct to keep track of our file position and buffer
		per_ch = (ogg_stream_perchannel_t *)Mem_Alloc(snd_mempool, sizeof(*per_ch));
		// begin decoding the file
		per_ch->ov_decode.buffer = per_sfx->file;
		per_ch->ov_decode.ind = 0;
		per_ch->ov_decode.buffsize = per_sfx->filesize;
		if (qov_open_callbacks(&per_ch->ov_decode, &per_ch->vf, NULL, 0, callbacks) < 0)
		{
			// this never happens - this function succeeded earlier on the same data
			Mem_Free(per_ch);
			return;
		}
		per_ch->bs = 0;
		per_ch->buffer_firstframe = 0;
		per_ch->buffer_numframes = 0;
		// attach the struct to our channel
		ch->fetcher_data = (void *)per_ch;
	}

	// if the request is too large for our buffer, loop...
	while (numsampleframes * f > (int)sizeof(per_ch->buffer))
	{
		done = sizeof(per_ch->buffer) / f;
		OGG_GetSamplesFloat(ch, sfx, firstsampleframe, done, outsamplesfloat);
		firstsampleframe += done;
		numsampleframes -= done;
		outsamplesfloat += done * sfx->format.channels;
	}

	// seek if the request is before the current buffer (loop back)
	// seek if the request starts beyond the current buffer by at least one frame (channel was zero volume for a while)
	// do not seek if the request overlaps the buffer end at all (expected behavior)
	if (per_ch->buffer_firstframe > firstsampleframe || per_ch->buffer_firstframe + per_ch->buffer_numframes < firstsampleframe)
	{
		// we expect to decode forward from here so this will be our new buffer start
		per_ch->buffer_firstframe = firstsampleframe;
		per_ch->buffer_numframes = 0;
		ret = qov_pcm_seek(&per_ch->vf, (ogg_int64_t)firstsampleframe);
		if (ret != 0)
		{
			// LordHavoc: we can't Con_Printf here, not thread safe...
			//Con_Printf("OGG_FetchSound: qov_pcm_seek(..., %d) returned %d\n", firstsampleframe, ret);
			return;
		}
	}

	// decompress the file as needed
	if (firstsampleframe + numsampleframes > per_ch->buffer_firstframe + per_ch->buffer_numframes)
	{
		// first slide the buffer back, discarding any data preceding the range we care about
		int offset = firstsampleframe - per_ch->buffer_firstframe;
		int keeplength = per_ch->buffer_numframes - offset;
		if (keeplength > 0)
			memmove(per_ch->buffer, per_ch->buffer + offset * sfx->format.width * sfx->format.channels, keeplength * sfx->format.width * sfx->format.channels);
		per_ch->buffer_firstframe = firstsampleframe;
		per_ch->buffer_numframes -= offset;
		// decompress as much as we can fit in the buffer
		newlength = sizeof(per_ch->buffer) - per_ch->buffer_numframes * f;
		done = 0;
		while (newlength > done && (ret = qov_read(&per_ch->vf, (char *)per_ch->buffer + per_ch->buffer_numframes * f + done, (int)(newlength - done), mem_bigendian, 2, 1, &per_ch->bs)) > 0)
			done += ret;
		// clear the missing space if any
		if (done < newlength)
			memset(per_ch->buffer + done, 0, newlength - done);
		// we now have more data in the buffer
		per_ch->buffer_numframes += done / f;
	}

	// convert the sample format for the caller
	buf = (short *)((char *)per_ch->buffer + (firstsampleframe - per_ch->buffer_firstframe) * f);
	len = numsampleframes * sfx->format.channels;
	for (i = 0;i < len;i++)
		outsamplesfloat[i] = buf[i] * (1.0f / 32768.0f);
}
Exemple #4
0
sfxcache_t *S_LoadSound(sfx_t *s)
{
	char namebuffer[MAX_OSPATH];
	char extionless[MAX_OSPATH];
	sfxcache_t *sc = NULL;
	int eof = 0;
	int current_section;
	vfsfile_t *f;
	OggVorbis_File oggfile;
	vorbis_info *ogginfo;
	ov_callbacks ovc = {
		vorbis_callback_read,
		vorbis_callback_seek,
		vorbis_callback_close,
		vorbis_callback_tell
	};
	static char pcmbuff[4096];
	
	//unsigned char *data;
	wavinfo_t info;
	int len;
	//int filesize;

	// see if still in memory
	if ((sc = (sfxcache_t *) Cache_Check (&s->cache)))
		return sc;

	if (!vorbis_CheckActive())
		vorbis_LoadLibrary();

	// load it in
	COM_StripExtension(s->name, extionless);
	snprintf (namebuffer, sizeof (namebuffer), "sound/%s.ogg", extionless);

/*	if (!(data = FS_LoadTempFile (namebuffer, &filesize))) {
		Com_Printf ("Couldn't load %s\n", namebuffer);
		return NULL;
	}*/

	if (!(f = FS_OpenVFS(namebuffer, "rb", FS_ANY))) {
		Com_Printf("Couldn't load %s\n", namebuffer);
		return NULL;
	}

	if (qov_open_callbacks(f, &oggfile, NULL, 0, ovc)) {
		Com_Printf("Invalid sound file %s\n", namebuffer);
		VFS_CLOSE(f);
		return NULL;
	}
	if (!(ogginfo = qov_info(&oggfile,-1))) {
		Com_Printf("Unable to retrieve information for %s\n", namebuffer);
		goto fail;
	}

	/* Print some information */
	fprintf(stderr, "%s\n", namebuffer);
	{
		char **ptr = qov_comment(&oggfile,-1)->user_comments;
		while(*ptr){
			fprintf(stderr,"%s\n",*ptr);
			++ptr;
		}
		fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",
				ogginfo->channels, ogginfo->rate);
		fprintf(stderr,"\nDecoded length: %ld samples\n",
				(long) qov_pcm_total(&oggfile,-1));
		fprintf(stderr,"Encoded by: %s\n\n", qov_comment(&oggfile,-1)->vendor);
	}

	info.rate = ogginfo->rate;			// Frequency rate
	info.width = 1;						// 8 bit samples
	info.channels = ogginfo->channels;	// 1 == mono, 2 == stereo
	info.loopstart = -1;				// NOOO idea...
	info.samples = qov_pcm_total(&oggfile,-1);	// Total samples
	info.dataofs = 0;					// offset

	
	//FMod_CheckModel(namebuffer, data, filesize);

	//info = GetWavinfo (s->name, data, filesize);

	// Stereo sounds are allowed (intended for music)
	if (info.channels < 1 || info.channels > 2) {
		Com_Printf("%s has an unsupported number of channels (%i)\n",s->name, info.channels);
		return NULL;
	}

	len = (int) ((double) info.samples * (double) shm->format.speed / (double) info.rate);
	len = len * info.width * info.channels;

	if (!(sc = (sfxcache_t *) Cache_Alloc (&s->cache, len + sizeof(sfxcache_t), s->name)))
		return NULL;

	/* Read the whole Ogg Vorbis file in */
	byte *output = sc->data;
	while (!eof) {
		// 0 == little endian, 1 = big endian
		// 1 == 8 bit samples, 2 = 16 bit samples
		// 1 == signed samples
		long ret = qov_read(&oggfile, pcmbuff, sizeof(pcmbuff), 
				0, info.width, 1, &current_section);
		if (ret == 0) {
			eof = 1;
		} else if (ret < 0) {
			/* error in the stream. Not a problem, just reporting it in
			 * case we (the app) cares. In this case, we don't. */
		} else {
			/* we don't bother dealing with sample rate changes, etc, but
			 * you'll have to */
			memcpy(output, pcmbuff, ret);
			output += ret;
		}
	}
	qov_clear(&oggfile);

	sc->total_length = (unsigned int) info.samples;
	sc->format.speed = info.rate;
	sc->format.width = info.width;
	sc->format.channels = info.channels;
	if (info.loopstart < 0)
		sc->loopstart = -1;
	else
		sc->loopstart = (int)((double)info.loopstart * (double)shm->format.speed / (double)sc->format.speed);

//	ResampleSfx (s, data + info.dataofs, info.samples, &sc->format, sc->data);
	return sc;

fail:
	qov_clear(&oggfile); // Only goto fail if ov_open* succeded
	if (f) {
		VFS_CLOSE(f);
	}
	return NULL;
}
void *decoder_ogg_load( const char *filename, snd_info_t *info )
{
	OggVorbis_File vorbisfile;
	int filenum, bitstream, bytes_read, bytes_read_total;
	char *buffer;
	ov_callbacks callbacks = { ovcb_read, ovcb_seek, ovcb_close, ovcb_tell };

	trap_FS_FOpenFile( filename, &filenum, FS_READ|FS_NOSIZE );
	if( !filenum )
		return NULL;

	if( trap_FS_IsUrl( filename ) )
	{
		callbacks.seek_func = NULL;
		callbacks.tell_func = NULL;
	}

	qov_open_callbacks( (void *) (qintptr) filenum, &vorbisfile, NULL, 0, callbacks );

	if( callbacks.seek_func && !qov_seekable( &vorbisfile ) )
	{
		Com_Printf( "Error unsupported .ogg file (not seekable): %s\n", filename );
		qov_clear( &vorbisfile ); // Does FS_FCloseFile
		return NULL;
	}

	if( qov_streams( &vorbisfile ) != 1 )
	{
		Com_Printf( "Error unsupported .ogg file (multiple logical bitstreams): %s\n", filename );
		qov_clear( &vorbisfile ); // Does FS_FCloseFile
		return NULL;
	}

	if( !read_ogg_header( vorbisfile, info ) )
	{
		Com_Printf( "Error reading .ogg file header: %s\n", filename );
		qov_clear( &vorbisfile ); // Does FS_FCloseFile
		return NULL;
	}

	buffer = S_Malloc( info->size );

	bytes_read_total = 0;
	do
	{
#ifdef ENDIAN_BIG
		bytes_read = qov_read( &vorbisfile, buffer+bytes_read_total, info->size-bytes_read_total, 1, 2, 1, &bitstream );
#elif defined (ENDIAN_LITTLE)
		bytes_read = qov_read( &vorbisfile, buffer+bytes_read_total, info->size-bytes_read_total, 0, 2, 1, &bitstream );
#else
#error "runtime endianess detection support missing"
#endif
		bytes_read_total += bytes_read;
	}
	while( bytes_read > 0 && bytes_read_total < info->size );

	qov_clear( &vorbisfile ); // Does FS_FCloseFile
	if( !bytes_read_total )
	{
		Com_Printf( "Error reading .ogg file: %s\n", filename );
		S_Free( buffer );
		return NULL;
	}

	return buffer;
}