/** Decodes an opened OggVorbis file into an OpenAL buffer
 *  \param psTrack pointer to object which will contain the final buffer
 *  \param PHYSFS_fileHandle file handle given by PhysicsFS to the opened file
 *  \return on success the psTrack pointer, otherwise it will be free'd and a NULL pointer is returned instead
 */
static inline TRACK* sound_DecodeOggVorbisTrack(TRACK *psTrack, PHYSFS_file* PHYSFS_fileHandle)
{
#ifndef WZ_NOSOUND
	ALenum		format;
	ALuint		buffer;
	struct OggVorbisDecoderState *decoder;
	soundDataBuffer	*soundBuffer;

	if ( !openal_initialized )
	{
		return NULL;
	}

	decoder = sound_CreateOggVorbisDecoder(PHYSFS_fileHandle, true);
	if (decoder == NULL)
	{
		debug(LOG_WARNING, "Failed to open audio file for decoding");
		free(psTrack);
		return NULL;
	}

	soundBuffer = sound_DecodeOggVorbis(decoder, 0);
	sound_DestroyOggVorbisDecoder(decoder);

	if (soundBuffer == NULL)
	{
		free(psTrack);
		return NULL;
	}

	if (soundBuffer->size == 0)
	{
		debug(LOG_WARNING, "sound_DecodeOggVorbisTrack: OggVorbis track is entirely empty after decoding");
// NOTE: I'm not entirely sure if a track that's empty after decoding should be
//       considered an error condition. Therefore I'll only error out on DEBUG
//       builds. (Returning NULL here __will__ result in a program termination.)
#ifdef DEBUG
		free(soundBuffer);
		free(psTrack);
		return NULL;
#endif
	}

	// Determine PCM data format
	format = (soundBuffer->channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;

	// Create an OpenAL buffer and fill it with the decoded data
	alGenBuffers(1, &buffer);
	sound_GetError();
	alBufferData(buffer, format, soundBuffer->data, soundBuffer->size, soundBuffer->frequency);
	sound_GetError();

	free(soundBuffer);

	// save buffer name in track
	psTrack->iBufferName = buffer;
#endif

	return psTrack;
}
Example #2
0
/** Destroy the given stream and release its associated resources. This function
 *  also handles calling of the \c onFinished callback function and closing of
 *  the PhysicsFS file handle.
 *  \param stream the stream to destroy
 */
static void sound_DestroyStream(AUDIO_STREAM *stream)
{
    ALint buffer_count;
    ALuint *buffers;
    ALint error;

    // Stop the OpenAL source from playing
    alSourceStop(stream->source);
    error = sound_GetError();

    if (error != AL_NO_ERROR)
    {
        // FIXME: We should really handle these errors.
    }

    // Retrieve the amount of buffers which were processed
    alGetSourcei(stream->source, AL_BUFFERS_PROCESSED, &buffer_count);
    error = sound_GetError();
    if (error != AL_NO_ERROR)
    {
        /* FIXME: We're leaking memory and resources here when bailing
         * out. But not doing so could cause stack overflows as a
         * result of the below alloca() call (due to buffer_count not
         * being properly initialised.
         */
        debug(LOG_SOUND, "alGetSourcei(AL_BUFFERS_PROCESSED) failed; bailing out...");
        return;
    }

    // Detach all buffers and retrieve their ID numbers
    buffers = (ALuint *)alloca(buffer_count * sizeof(ALuint));
    alSourceUnqueueBuffers(stream->source, buffer_count, buffers);
    sound_GetError();

    // Destroy all of these buffers
    alDeleteBuffers(buffer_count, buffers);
    sound_GetError();

    // Destroy the OpenAL source
    alDeleteSources(1, &stream->source);
    sound_GetError();

    // Destroy the sound decoder
    sound_DestroyOggVorbisDecoder(stream->decoder);

    // Now close the file
    PHYSFS_close(stream->fileHandle);

    // Now call the finished callback
    if (stream->onFinished)
    {
        stream->onFinished(stream->user_data);
    }

    // Free the memory used by this stream
    free(stream);
}
Example #3
0
/** Plays the audio data from the given file
 *  \param fileHandle,volume,onFinished,user_data see sound_PlayStream()
 *  \param streamBufferSize the size to use for the decoded audio buffers
 *  \param buffer_count the amount of audio buffers to use
 *  \see sound_PlayStream() for details about the rest of the function
 *       parameters and other details.
 */
AUDIO_STREAM *sound_PlayStreamWithBuf(PHYSFS_file *fileHandle, float volume, void (*onFinished)(void *), void *user_data, size_t streamBufferSize, unsigned int buffer_count)
{
    AUDIO_STREAM *stream;
    ALuint       *buffers = (ALuint *)alloca(sizeof(ALuint) * buffer_count);
    ALint error;
    unsigned int i;

    if (!openal_initialized)
    {
        debug(LOG_WARNING, "OpenAL isn't initialized, not creating an audio stream");
        return NULL;
    }

    stream = (AUDIO_STREAM *)malloc(sizeof(AUDIO_STREAM));
    if (stream == NULL)
    {
        debug(LOG_FATAL, "sound_PlayStream: Out of memory");
        abort();
        return NULL;
    }

    // Clear error codes
    alGetError();

    // Retrieve an OpenAL sound source
    alGenSources(1, &(stream->source));

    error = sound_GetError();
    if (error != AL_NO_ERROR)
    {
        // Failed to create OpenAL sound source, so bail out...
        debug(LOG_SOUND, "alGenSources failed, most likely out of sound sources");
        free(stream);
        return NULL;
    }

    stream->fileHandle = fileHandle;

    stream->decoder = sound_CreateOggVorbisDecoder(stream->fileHandle, false);
    if (stream->decoder == NULL)
    {
        debug(LOG_ERROR, "sound_PlayStream: Failed to open audio file for decoding");
        free(stream);
        return NULL;
    }

    stream->volume = volume;
    stream->bufferSize = streamBufferSize;

    alSourcef(stream->source, AL_GAIN, stream->volume);

    // HACK: this is a workaround for a bug in the 64bit implementation of OpenAL on GNU/Linux
    // The AL_PITCH value really should be 1.0.
    alSourcef(stream->source, AL_PITCH, 1.001f);

    // Create some OpenAL buffers to store the decoded data in
    alGenBuffers(buffer_count, buffers);
    sound_GetError();

    // Fill some buffers with audio data
    for (i = 0; i < buffer_count; ++i)
    {
        // Decode some audio data
        soundDataBuffer *soundBuffer = sound_DecodeOggVorbis(stream->decoder, stream->bufferSize);

        // If we actually decoded some data
        if (soundBuffer && soundBuffer->size > 0)
        {
            // Determine PCM data format
            ALenum format = (soundBuffer->channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;

            // Copy the audio data into one of OpenAL's own buffers
            alBufferData(buffers[i], format, soundBuffer->data, soundBuffer->size, soundBuffer->frequency);
            sound_GetError();

            // Clean up our memory
            free(soundBuffer);
        }
        else
        {
            // If no data has been decoded we're probably at the end of our
            // stream. So cleanup the excess stuff here.

            // First remove the data buffer itself
            free(soundBuffer);

            // Then remove OpenAL's buffers
            alDeleteBuffers(buffer_count - i, &buffers[i]);
            sound_GetError();

            break;
        }
    }

    // Bail out if we didn't fill any buffers
    if (i == 0)
    {
        debug(LOG_ERROR, "Failed to fill buffers with decoded audio data!");

        // Destroy the decoder
        sound_DestroyOggVorbisDecoder(stream->decoder);

        // Destroy the OpenAL source
        alDeleteSources(1, &stream->source);

        // Free allocated memory
        free(stream);

        return NULL;
    }

    // Attach the OpenAL buffers to our OpenAL source
    // (i = the amount of buffers we worked on in the above for-loop)
    alSourceQueueBuffers(stream->source, i, buffers);
    sound_GetError();

    // Start playing the source
    alSourcePlay(stream->source);

    sound_GetError();

    // Set callback info
    stream->onFinished = onFinished;
    stream->user_data = user_data;

    // Prepend this stream to the linked list
    stream->next = active_streams;
    active_streams = stream;

    return stream;
}
Example #4
0
/** Destroy the given stream and release its associated resources. This function
 *  also handles calling of the \c onFinished callback function and closing of
 *  the PhysicsFS file handle.
 *  \param stream the stream to destroy
 */
static void sound_DestroyStream(AUDIO_STREAM *stream)
{
	ALint buffer_count;
	ALuint *buffers;
	bool freeBuffers = false;
	ALint error;

	// Stop the OpenAL source from playing
	alSourceStop(stream->source);
	error = sound_GetError();

	if (error != AL_NO_ERROR)
	{
		// FIXME: We should really handle these errors.
	}

	// Retrieve the amount of buffers which were processed
	alGetSourcei(stream->source, AL_BUFFERS_PROCESSED, &buffer_count);
	error = sound_GetError();
	if (error != AL_NO_ERROR)
	{
		/* FIXME: We're leaking memory and resources here when bailing
		 * out. But not doing so could cause stack overflows as a
		 * result of the below alloca() call (due to buffer_count not
		 * being properly initialised.
		 */
		debug(LOG_SOUND, "alGetSourcei(AL_BUFFERS_PROCESSED) failed; bailing out...");
		return;
	}

	// Detach all buffers and retrieve their ID numbers
	if (buffer_count > 0)
	{
		if (buffer_count <= (1024 / sizeof(ALuint))) // See CMakeLists.txt for value of -Walloca-larger-than=<N>
		{
			buffers = (ALuint *)alloca(buffer_count * sizeof(ALuint));
		}
		else
		{
			// Too many buffers - don't allocate on the stack!
			buffers = (ALuint *)malloc(buffer_count * sizeof(ALuint));
			freeBuffers = true;
		}
		alSourceUnqueueBuffers(stream->source, buffer_count, buffers);
		sound_GetError();

		// Destroy all of these buffers
		alDeleteBuffers(buffer_count, buffers);
		sound_GetError();
		if(freeBuffers)
		{
			free(buffers);
		}
		buffers = nullptr;
	}
	else
	{
		// alGetSourcei(AL_BUFFERS_PROCESSED) returned a count <= 0?
		debug(LOG_SOUND, "alGetSourcei(AL_BUFFERS_PROCESSED) returned count: %d", buffer_count);
	}

	// Destroy the OpenAL source
	alDeleteSources(1, &stream->source);
	sound_GetError();

	// Destroy the sound decoder
	sound_DestroyOggVorbisDecoder(stream->decoder);

	// Now close the file
	PHYSFS_close(stream->fileHandle);

	// Now call the finished callback
	if (stream->onFinished)
	{
		stream->onFinished(stream->user_data);
	}

	// Free the memory used by this stream
	free(stream);
}