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.
This commit is contained in:
janwas 2010-08-01 10:52:12 +00:00
parent aa2d305b6e
commit e20f93ffdc
10 changed files with 260 additions and 486 deletions

View File

@ -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 <vorbis/vorbisfile.h>
#if MSC_VERSION
# ifdef NDEBUG
# pragma comment(lib, "vorbisfile.lib")
# else
# pragma comment(lib, "vorbisfile_d.lib")
# endif
#endif
#endif // #ifndef INCLUDED_VORBIS

View File

@ -45,7 +45,6 @@ namespace FileImpl
class File
{
NONCOPYABLE(File);
public:
File()
: m_pathname(), m_fd(0)

View File

@ -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;

View File

@ -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<OggStreamImpl> tmp(new OggStreamImpl(file));
RETURN_ERR(tmp->Open());
stream = tmp;
return INFO::OK;
}

View File

@ -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<OggStream> OggStreamPtr;
extern LibError OpenOggStream(const fs::wpath& pathname, OggStreamPtr& stream);
#endif // INCLUDED_OGG

View File

@ -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();
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CMusicPlayer.h"
#include "ps/CLogger.h"
#include <sstream>
#include <list>
#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";
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CMUSICPLAYER
#define INCLUDED_CMUSICPLAYER
#include <string>
#include <iostream>
#include <stdio.h>
#include "lib/file/vfs/vfs_path.h"
//#include "oal.h"
/*
#include <ogg/ogg.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
*/
/*
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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CPlayList.h"
#include <stdio.h> // 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<u8> 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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CPLAYLIST
#define INCLUDED_CPLAYLIST
#include <vector>
#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<CStrW> tracks;
};
#endif