From e20f93ffdc077479cef94dc54b85f258b996a120 Mon Sep 17 00:00:00 2001 From: janwas Date: Sun, 1 Aug 2010 10:52:12 +0000 Subject: [PATCH] add proper OGG support, a near-total rewrite of a patch by Kyniker/Heron. not yet integrated into snd_mgr. also remove the no longer used CMusicPlayer and CPlayList. This was SVN commit r7834. --- source/lib/external_libraries/vorbis.h | 40 ++++ source/lib/file/file.h | 1 - source/lib/lib_errors.h | 1 + source/lib/res/sound/ogg.cpp | 201 ++++++++++++++++++ source/lib/res/sound/ogg.h | 18 ++ source/main.cpp | 9 - source/sound/CMusicPlayer.cpp | 283 ------------------------- source/sound/CMusicPlayer.h | 85 -------- source/sound/CPlayList.cpp | 68 ------ source/sound/CPlayList.h | 40 ---- 10 files changed, 260 insertions(+), 486 deletions(-) create mode 100644 source/lib/external_libraries/vorbis.h create mode 100644 source/lib/res/sound/ogg.cpp create mode 100644 source/lib/res/sound/ogg.h delete mode 100644 source/sound/CMusicPlayer.cpp delete mode 100644 source/sound/CMusicPlayer.h delete mode 100644 source/sound/CPlayList.cpp delete mode 100644 source/sound/CPlayList.h diff --git a/source/lib/external_libraries/vorbis.h b/source/lib/external_libraries/vorbis.h new file mode 100644 index 0000000000..c8b287de7a --- /dev/null +++ b/source/lib/external_libraries/vorbis.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2010 Wildfire Games + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * bring in OGG Vorbis header+library + */ + +#ifndef INCLUDED_VORBIS +#define INCLUDED_VORBIS + +#include + +#if MSC_VERSION +# ifdef NDEBUG +# pragma comment(lib, "vorbisfile.lib") +# else +# pragma comment(lib, "vorbisfile_d.lib") +# endif +#endif + +#endif // #ifndef INCLUDED_VORBIS diff --git a/source/lib/file/file.h b/source/lib/file/file.h index 4a6d07c005..68c171cd03 100644 --- a/source/lib/file/file.h +++ b/source/lib/file/file.h @@ -45,7 +45,6 @@ namespace FileImpl class File { - NONCOPYABLE(File); public: File() : m_pathname(), m_fd(0) diff --git a/source/lib/lib_errors.h b/source/lib/lib_errors.h index e6183b0136..5ee5ad91ec 100644 --- a/source/lib/lib_errors.h +++ b/source/lib/lib_errors.h @@ -430,6 +430,7 @@ const LibError LOGIC = -100010; const LibError TIMED_OUT = -100011; const LibError REENTERED = -100012; const LibError CORRUPTED = -100013; +const LibError VERSION = -100014; // function arguments const LibError INVALID_PARAM = -100020; diff --git a/source/lib/res/sound/ogg.cpp b/source/lib/res/sound/ogg.cpp new file mode 100644 index 0000000000..c68018a0b0 --- /dev/null +++ b/source/lib/res/sound/ogg.cpp @@ -0,0 +1,201 @@ +#include "precompiled.h" +#include "ogg.h" + +#include "lib/external_libraries/openal.h" +#include "lib/external_libraries/vorbis.h" + +#include "lib/byte_order.h" +#include "lib/file/file.h" + + +static LibError LibErrorFromVorbis(int err) +{ + switch(err) + { + case 0: + return INFO::OK; + case OV_HOLE: + return ERR::AGAIN; + case OV_EREAD: + return ERR::IO; + case OV_EFAULT: + return ERR::LOGIC; + case OV_EIMPL: + return ERR::NOT_IMPLEMENTED; + case OV_EINVAL: + return ERR::INVALID_PARAM; + case OV_ENOTVORBIS: + return ERR::NOT_SUPPORTED; + case OV_EBADHEADER: + return ERR::CORRUPTED; + case OV_EVERSION: + return ERR::VERSION; + case OV_ENOTAUDIO: + return ERR::_1; + case OV_EBADPACKET: + return ERR::_2; + case OV_EBADLINK: + return ERR::_3; + case OV_ENOSEEK: + return ERR::_4; + default: + return ERR::FAIL; + } +} + + +//----------------------------------------------------------------------------- + +class VorbisFileAdapter +{ +public: + VorbisFileAdapter(const File& openedFile) + : file(openedFile) + , size(fs::file_size(openedFile.Pathname())) + , offset(0) + { + } + + static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context) + { + VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + const off_t sizeRequested = numItems*itemSize; + const off_t sizeRemaining = adapter->size - adapter->offset; + const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining); + + if(adapter->file.Read(adapter->offset, (u8*)bufferToFill, sizeToRead) == INFO::OK) + { + adapter->offset += sizeToRead; + return sizeToRead; + } + + errno = EIO; + return 0; + } + + static int Seek(void* context, ogg_int64_t offset, int whence) + { + VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + + off_t origin = 0; + switch(whence) + { + case SEEK_SET: + origin = 0; + break; + case SEEK_CUR: + origin = adapter->offset; + break; + case SEEK_END: + origin = adapter->size+1; + break; + NODEFAULT; + } + + adapter->offset = Clamp(origin+offset, off_t(0), adapter->size); + return 0; + } + + static int Close(void* context) + { + VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + adapter->file.Close(); + return 0; // return value is ignored + } + + static long Tell(void* context) + { + VorbisFileAdapter* adapter = (VorbisFileAdapter*)context; + return adapter->offset; + } + +private: + File file; + off_t size; + off_t offset; +}; + + +//----------------------------------------------------------------------------- + +class OggStreamImpl : public OggStream +{ +public: + OggStreamImpl(const File& openedFile) + : adapter(openedFile) + { + } + + LibError Open() + { + ov_callbacks callbacks; + callbacks.read_func = VorbisFileAdapter::Read; + callbacks.close_func = VorbisFileAdapter::Close; + callbacks.seek_func = VorbisFileAdapter::Seek; + callbacks.tell_func = VorbisFileAdapter::Tell; + const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks); + if(ret != 0) + WARN_RETURN(LibErrorFromVorbis(ret)); + + const int link = -1; // retrieve info for current bitstream + info = ov_info(&vf, link); + if(!info) + WARN_RETURN(ERR::INVALID_HANDLE); + + return INFO::OK; + } + + virtual ALenum Format() + { + return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + } + + virtual ALsizei SamplingRate() + { + return info->rate; + } + + virtual LibError GetNextChunk(u8* buffer, size_t size) + { + // we may have to call ov_read multiple times because it + // treats the buffer size "as a limit and not a request" + size_t bytesRead = 0; + for(;;) + { + const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN); + const int wordSize = sizeof(i16); + const int isSigned = 1; + int bitstream; // unused + const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream); + if(ret == 0) // EOF + return (LibError)bytesRead; + else if(ret < 0) + WARN_RETURN(LibErrorFromVorbis(ret)); + else // success + { + bytesRead += ret; + if(bytesRead == size) + return INFO::OK; + } + } + } + +private: + VorbisFileAdapter adapter; + OggVorbis_File vf; + vorbis_info* info; +}; + + +//----------------------------------------------------------------------------- + +LibError OpenOggStream(const fs::wpath& pathname, OggStreamPtr& stream) +{ + File file; + RETURN_ERR(file.Open(pathname, L'r')); + + shared_ptr tmp(new OggStreamImpl(file)); + RETURN_ERR(tmp->Open()); + stream = tmp; + return INFO::OK; +} diff --git a/source/lib/res/sound/ogg.h b/source/lib/res/sound/ogg.h new file mode 100644 index 0000000000..b070bfe2d6 --- /dev/null +++ b/source/lib/res/sound/ogg.h @@ -0,0 +1,18 @@ +#ifndef INCLUDED_OGG +#define INCLUDED_OGG + +#include "lib/external_libraries/openal.h" + +class OggStream +{ +public: + virtual ALenum Format() = 0; + virtual ALsizei SamplingRate() = 0; + virtual LibError GetNextChunk(u8* buffer, size_t size) = 0; +}; + +typedef shared_ptr OggStreamPtr; + +extern LibError OpenOggStream(const fs::wpath& pathname, OggStreamPtr& stream); + +#endif // INCLUDED_OGG diff --git a/source/main.cpp b/source/main.cpp index 3d51b8588f..8dbbb5a1a4 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -63,7 +63,6 @@ that of Atlas depending on commandline parameters. #include "graphics/GameView.h" #include "scripting/ScriptingHost.h" #include "simulation2/Simulation2.h" -#include "sound/CMusicPlayer.h" #include "gui/GUIManager.h" #define LOG_CATEGORY L"main" @@ -199,8 +198,6 @@ static int ProgressiveLoad() } -CMusicPlayer music_player; - static void Frame() { MICROLOG(L"Frame"); @@ -241,11 +238,6 @@ static void Frame() // this is mostly relevant for "inactive" state, so that other windows // get enough CPU time, but it's always nice for power+thermal management. - - PROFILE_START( "update music" ); - music_player.Update(); - PROFILE_END( "update music" ); - bool is_building_archive; // must come before PROFILE_START's { PROFILE_START("build archive"); MICROLOG(L"build archive"); @@ -375,7 +367,6 @@ static void MainControllerInit() static void MainControllerShutdown() { - music_player.Release(); } diff --git a/source/sound/CMusicPlayer.cpp b/source/sound/CMusicPlayer.cpp deleted file mode 100644 index 667bf5ab7d..0000000000 --- a/source/sound/CMusicPlayer.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 of the License, or - * (at your option) any later version. - * - * 0 A.D. is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with 0 A.D. If not, see . - */ - -#include "precompiled.h" - -#include "CMusicPlayer.h" -#include "ps/CLogger.h" - -#include -#include - -#define LOG_CATEGORY L"audio" - - - - - - -//Class implementation -CMusicPlayer::CMusicPlayer(void) -{ -/* - oal_Init(); - - if(!alIsExtensionPresent((ALubyte*)"AL_EXT_vorbis")) - debug_warn(L"no OpenAL ogg extension"); -*/ - is_open = false; -} - -CMusicPlayer::~CMusicPlayer(void) -{ - Release(); -} - -void CMusicPlayer::Open(const VfsPath& UNUSED(pathname)) -{ - // If a new file is opened while another is already in memory, - // close the old one first. - if (is_open) - Release(); - -/* - void* p; - size_t sizeOfFile; - if(vfs_load(pathname, p, sizeOfFile) != INFO::OK) - { - LOG(CLogger::Error, LOG_CATEGORY, L"CMusicPlayer::open(): vfs_load for %ls failed!\n", pathname.string().c_str()); - return; - } - else - LOG(CLogger::Normal, LOG_CATEGORY, L"CMusicPlayer::open(): file %ls loaded successfully\n", pathname.string().c_str()); - memFile.dataPtr = (char*)p; - memFile.dataRead = 0; - memFile.dataSize = sizeOfFile; -*/ -/* - hf = vfs_open(pathname); - - for(int i = 0; i < NUM_BUFS; i++) - { - alGenBuffers(1, &bufs[i].al_buffer); - bufs[i].raw_buf = malloc(RAW_BUF_SIZE); - } - - alGenSources(1, &source); - check(); - - 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); - check(); -*/ - is_open = true; -} - - -void CMusicPlayer::Release() -{ -/* - if(!is_open) - return; - is_open = false; - - alSourceStop(source); - empty(); - - alDeleteSources(1,&source); - source = 0; - - - for(int i = 0; i < NUM_BUFS; i++) - { - alDeleteBuffers(1, &bufs[i].al_buffer); - bufs[i].al_buffer = 0; - mem_free(bufs[i].raw_buf); - } - - check(); - - mem_free(memFile.dataPtr); -*/ -} - - -bool CMusicPlayer::IsPlaying() -{ - return false; -} -/* - // guard against OpenAL using source when it's not initialized - if(!is_open) - return false; - - ALenum state = 0; - alGetSourcei(source,AL_SOURCE_STATE,&state); - check(); - return (state == AL_PLAYING); -} -*/ -/* - -bool CMusicPlayer::Issue(int slot_idx) -{ - Buf* buf = &bufs[slot_idx]; - - ssize_t left = (ssize_t)memFile.dataSize - (ssize_t)memFile.dataRead; - ssize_t size = std::min(64*KB, left); - debug_assert(size >= 0); - void* data = memFile.dataPtr; - data = (char*)data + memFile.dataRead; - memFile.dataRead += size; - - alBufferData(buf->al_buffer, AL_FORMAT_VORBIS_EXT, data, (ALsizei)size, 1); - alSourceQueueBuffers(source, 1, buf->al_buffer); - - check(); - - return true; -} -*/ - -bool CMusicPlayer::Play() -{ - Check(); - - if(IsPlaying()) - return true; - - if(!is_open) - debug_warn(L"play() called before open()"); -/* - if(!stream(0)) - return false; - if(!stream(1)) - return false; -*/ - //bind the 2 buffers to the source - -// alSourcePlay(source); -// check(); - - return true; -} - - -bool CMusicPlayer::Update() -{ - if(!IsPlaying()) - return false; -/* - //check which buffers have already been played - int processed; - alGetSourcei(source,AL_BUFFERS_PROCESSED, &processed); - check(); - - // start transfers on any buffers that have completed playing - // any transfers that have completed: add the corresponding buffer - - bool active = true; - - while(processed-- && processed >= 0) - { - ALuint buffer; - - //remove buffer from queue - alSourceUnqueueBuffers(source,1,&buffer); - check(); - - // to which Buf does the al_buffer belong? - int i; - for(i = 0; i < NUM_BUFS; i++) - if(bufs[i].al_buffer == buffer) - goto found; - debug_warn(L"al_buffer not found!"); -found: - - //fill buffer with new data if false is returned the there is no more data - active = stream(buffer); - - //attach buffer to end of queue - alSourceQueueBuffers(source,1,&buffer); - check(); - } - - return active; -*/ - return false; -} - - -void CMusicPlayer::Check() -{ -/* - int error = alGetError(); - - if(error != AL_NO_ERROR) - { - std::string str = errorString(error); - LOG(CLogger::Error, LOG_CATEGORY, L"OpenAL error: %hs\n", str.c_str()); - } -*/ -} - - -void CMusicPlayer::Empty() -{ -/* - int queued; - alGetSourcei(source,AL_BUFFERS_QUEUED,&queued); - - while(queued-- > 0) - { - ALuint buffer; - - alSourceUnqueueBuffers(source,1,&buffer); - check(); - } -*/ -} - - - - -std::string CMusicPlayer::ErrorString(int UNUSED(errorcode)) -{ - /* - switch(errorcode) - { - case AL_INVALID_NAME: - return "AL_INVALID_NAME"; - case AL_INVALID_ENUM: - return "AL_INVALID_ENUM"; - case AL_INVALID_VALUE: - return "AL_INVALID_VALUE"; - case AL_INVALID_OPERATION: - return "AL_INVALID_OPERATION"; - case AL_OUT_OF_MEMORY: - return "AL_OUT_OF_MEMORY"; - default: - std::stringstream str; - str << "Unknown Ogg error (code "<< errorcode << ")"; - return str.str(); - } - */ - return "ENOSYS"; -} diff --git a/source/sound/CMusicPlayer.h b/source/sound/CMusicPlayer.h deleted file mode 100644 index 8e241d646c..0000000000 --- a/source/sound/CMusicPlayer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 of the License, or - * (at your option) any later version. - * - * 0 A.D. is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with 0 A.D. If not, see . - */ - -#ifndef INCLUDED_CMUSICPLAYER -#define INCLUDED_CMUSICPLAYER - -#include -#include -#include - -#include "lib/file/vfs/vfs_path.h" - -//#include "oal.h" - -/* -#include -#include -#include -*/ - -/* -struct SOggFile -{ - char *dataPtr; - size_t dataSize; - size_t dataRead; -}; - -const size_t RAW_BUF_SIZE = 32*KB; -const int NUM_SLOTS = 3; - -*/ - -class CMusicPlayer -{ -public: - CMusicPlayer(void); - ~CMusicPlayer(void); - void Open(const VfsPath& pathname); - void Release(); - bool Play(); - bool IsPlaying(); - bool Update(); - -protected: -// bool Stream(ALuint buffer); - void Check(); - void Empty(); - std::string ErrorString(int errorcode); - -private: - bool is_open; - // between open() and release(); used to determine - // if source is actually valid, for isPlaying check. -/* - SOggFile memFile; - - Handle hf; - - struct IOSlot - { - ALuint al_buffer; - Handle hio; - void* raw_buf; - }; - IOSlot slots[NUM_SLOTS]; -*/ -// ALuint source; -}; - -#endif diff --git a/source/sound/CPlayList.cpp b/source/sound/CPlayList.cpp deleted file mode 100644 index f78ae586be..0000000000 --- a/source/sound/CPlayList.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 of the License, or - * (at your option) any later version. - * - * 0 A.D. is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with 0 A.D. If not, see . - */ - -#include "precompiled.h" - -#include "CPlayList.h" - -#include // sscanf -#include "ps/Filesystem.h" - - -CPlayList::CPlayList() -{ - tracks.clear(); -} - -CPlayList::CPlayList(const VfsPath& pathname) -{ - Load(pathname); -} - -CPlayList::~CPlayList() -{ - -} - -void CPlayList::Load(const VfsPath& pathname) -{ - tracks.clear(); - - shared_ptr buf; size_t size; - if(g_VFS->LoadFile(pathname, buf, size) < 0) - return; - - const char* playlist = (const char*)buf.get(); - char track[512]; - - while(sscanf(playlist, "%511s\n", track) == 1) - tracks.push_back(CStrW(track)); -} - - -void CPlayList::List() -{ - for(size_t i = 0; i < tracks.size(); i++) - { - debug_printf(L"%ls\n", tracks[i].c_str()); - } -} - -void CPlayList::Add(std::wstring name) -{ - tracks.push_back(name); -} diff --git a/source/sound/CPlayList.h b/source/sound/CPlayList.h deleted file mode 100644 index bc93cd83a2..0000000000 --- a/source/sound/CPlayList.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 of the License, or - * (at your option) any later version. - * - * 0 A.D. is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with 0 A.D. If not, see . - */ - -#ifndef INCLUDED_CPLAYLIST -#define INCLUDED_CPLAYLIST - -#include - -#include "lib/file/vfs/vfs_path.h" -#include "ps/CStr.h" - -class CPlayList -{ -public: - CPlayList(); - CPlayList(const VfsPath& pathname); - ~CPlayList(); - void Load(const VfsPath& pathname); - void List(); - void Add(std::wstring name); - -private: - std::vector tracks; -}; - -#endif