/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... ------------------------------------------------------------------- The source of this file comes stright from http://www.devmaster.net Thanks a lot for the nice work, and the easy portability to our Project. */ #include #include "ogg_player.h" #include "sound_engine.h" #include "debug.h" /** * initializes an Ogg-player from a file * @param fileName the file to load */ OggPlayer::OggPlayer(const std::string& fileName) { this->setClassID(CL_SOUND_OGG_PLAYER, "OggPlayer"); this->state = None; this->source = 0; this->buffers[0] = 0; this->buffers[1] = 0; if (!fileName.empty()) { if (this->open(fileName)) this->setName(fileName); } } OggPlayer::~OggPlayer() { this->release(); } /** * opens a file for playback * @param fileName the file to open */ bool OggPlayer::open(const std::string& fileName) { // release old State if (state & FileOpened) release(); // allocating Buffers if (this->buffers[0] == 0) alGenBuffers(2, this->buffers); SoundEngine::checkError("Allocating Buffers", __LINE__); if (this->buffers[0] != 0 && this->buffers[1] != 0) state |= BuffersAllocated; else { PRINTF(2)("Unable to allocate al-Buffers\n"); this->release(); return false; } // allocating source if (this->source == 0) SoundEngine::getInstance()->popALSource(this->source); if (this->source != 0) state |= SourceAllocated; else { PRINTF(2)("No more Sources Availiable (maybe you should consider raising the source-count\n"); this->release(); return false; } // opening the FILE; int result; if(!(oggFile = fopen(fileName.c_str(), "rb"))) { PRINTF(2)("Could not open Ogg file."); this->release(); return false; } // reading the Stream. if((result = ov_open(oggFile, &oggStream, NULL, 0)) < 0) { PRINTF(2)("Could not open Ogg stream. %s", errorString(result)); fclose(oggFile); this->release(); return false; } // acquiring the vorbis-properties. vorbisInfo = ov_info(&oggStream, -1); vorbisComment = ov_comment(&oggStream, -1); this->state |= FileOpened; if(vorbisInfo->channels == 1) format = AL_FORMAT_MONO16; else format = AL_FORMAT_STEREO16; // setting the Source Properties. alSource3f(source, AL_POSITION, 0.0, 0.0, 0.0); alSource3f(source, AL_VELOCITY, 0.0, 0.0, 0.0); alSource3f(source, AL_DIRECTION, 0.0, 0.0, 0.0); alSourcef (source, AL_ROLLOFF_FACTOR, 0.0 ); alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE ); alSourcef (source, AL_GAIN, SoundEngine::getInstance()->getMusicVolume()); SoundEngine::checkError("OggPlayer::SetSourceProperties", __LINE__); return true; } /** * releases a stream */ void OggPlayer::release() { this->printState(); if (this->state & SourceAllocated) { assert(alIsSource(this->source)); if (this->state & Playing); { alSourceStop(source); SoundEngine::checkError("OggPlayer::release()::alSourceStop", __LINE__); this->state & !Playing; } empty(); SoundEngine::getInstance()->pushALSource(source); this->source = 0; this->state &= !SourceAllocated; } if (this->state & BuffersAllocated) { assert (this->buffers[0] != 0 && this->buffers[1] != 0); alDeleteBuffers(2, buffers); SoundEngine::checkError("OggPlayer::release()::alDeleteBuffers", __LINE__); this->buffers[0] = 0; this->buffers[1] = 0; this->state &= !BuffersAllocated; } if (this->state & FileOpened) { ov_clear(&oggStream); this->state &= ! FileOpened; } } /** * plays back the sound * @return true if running, false otherwise */ bool OggPlayer::playback() { if (!(this->state & FileOpened)) return false; if(playing()) return true; this->state |= Playing; if(!stream(buffers[0]) || !stream(buffers[1])) return false; alSourceQueueBuffers(this->source, 2, this->buffers); if (DEBUG >= 3) SoundEngine::checkError("OggPlayer::playback()::alSourceQueueBuffers", __LINE__); alSourcePlay(this->source); if (DEBUG >= 3) SoundEngine::checkError("OggPlayer::playback()::alSourcePlay", __LINE__); return true; } /** * * @returns true if the file is playing */ bool OggPlayer::playing() { if (!(this->state & FileOpened)) return false; ALenum state; alGetSourcei(this->source, AL_SOURCE_STATE, &state); return (state == AL_PLAYING); } /** * updates the stream, this has to be done every few parts of a second, for sound-consistency * @returns true, if the Sound is playing flawlessly */ bool OggPlayer::update() { if (unlikely(!(this->state & Playing))) return false; int processed; bool active = true; alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); if (DEBUG >= 3) SoundEngine::checkError("OggPlayer::update()::alGetSourceI", __LINE__); while(processed--) { ALuint buffer; alSourceUnqueueBuffers(source, 1, &buffer); if (DEBUG >= 3) SoundEngine::checkError("OggPlayer::update()::unqueue", __LINE__); active = stream(buffer); alSourceQueueBuffers(source, 1, &buffer); if (DEBUG >= 3) SoundEngine::checkError("OggPlayer::update()::queue", __LINE__); } return active; } /** * gets a new Stream from buffer * @param buffer the buffer to get the stream from * @return true, if everything worked as planed */ bool OggPlayer::stream(ALuint buffer) { if (unlikely(!(this->state & Playing))) return false; char pcm[BUFFER_SIZE]; int size = 0; int section; int result; while(size < BUFFER_SIZE) { result = ov_read(&oggStream, pcm + size, BUFFER_SIZE - size, 0, 2, 1, §ion); if(result > 0) size += result; else if(result < 0) throw errorString(result); else break; } if(size == 0) return false; alBufferData(buffer, format, pcm, size, vorbisInfo->rate); if (DEBUG >= 3) SoundEngine::checkError("OggPlayer::playback()::BUFFER", __LINE__); return true; } /** * empties the buffers */ void OggPlayer::empty() { int queued; alGetSourcei(source, AL_BUFFERS_QUEUED, &queued); while(queued--) { ALuint buffer; alSourceUnqueueBuffers(source, 1, &buffer); SoundEngine::checkError("OggPlayer::empty()::unqueue Buffers", __LINE__); } } /** * displays some info about the ogg-file */ void OggPlayer::debug() { cout << "version " << vorbisInfo->version << "\n" << "channels " << vorbisInfo->channels << "\n" << "rate (hz) " << vorbisInfo->rate << "\n" << "bitrate upper " << vorbisInfo->bitrate_upper << "\n" << "bitrate nominal " << vorbisInfo->bitrate_nominal << "\n" << "bitrate lower " << vorbisInfo->bitrate_lower << "\n" << "bitrate window " << vorbisInfo->bitrate_window << "\n" << "\n" << "vendor " << vorbisComment->vendor << "\n"; for(int i = 0; i < vorbisComment->comments; i++) cout << " " << vorbisComment->user_comments[i] << "\n"; cout << endl; } void OggPlayer::printState() { PRINTF(0)("OggPlayer is in the following States: "); if (this->state & FileOpened) PRINT(0)("FileOpened "); if (this->state & SourceAllocated) PRINT(0)("SourceAllocated "); if (this->state & BuffersAllocated) PRINT(0)("BuffersAllocated "); if (this->state & Stopped) PRINT(0)("Stopped "); if (this->state & Playing) PRINT(0)("Playing "); if (this->state & Paused) PRINT(0)("Paused "); if (this->state & Error) PRINT(0)("Error "); PRINT(0)("\n"); } /** * returns errors * @param code the error-code * @return the error as a String */ const char* OggPlayer::errorString(int code) { switch(code) { case OV_EREAD: return ("Read from media."); case OV_ENOTVORBIS: return ("Not Vorbis data."); case OV_EVERSION: return ("Vorbis version mismatch."); case OV_EBADHEADER: return ("Invalid Vorbis header."); case OV_EFAULT: return ("Internal logic fault (bug or heap/stack corruption."); default: return ("Unknown Ogg error."); } }