/** 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; }
/** 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; }