Merge branch 'next' into occlusionquery

actorid
scrawl 13 years ago
commit 18c2b5eb0a

@ -0,0 +1,123 @@
Bitstream Vera Fonts Copyright
The fonts have a generous copyright, allowing derivative works (as
long as "Bitstream" or "Vera" are not in the names), and full
redistribution (so long as they are not *sold* by themselves). They
can be be bundled, redistributed and sold with any software.
The fonts are distributed under the following copyright:
Copyright
=========
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
Vera is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute
the Font Software, including without limitation the rights to use,
copy, merge, publish, distribute, and/or sell copies of the Font
Software, and to permit persons to whom the Font Software is furnished
to do so, subject to the following conditions:
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Bitstream" or the word "Vera".
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Bitstream Vera" names.
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font
Software without prior written authorization from the Gnome Foundation
or Bitstream Inc., respectively. For further information, contact:
fonts at gnome dot org.
Copyright FAQ
=============
1. I don't understand the resale restriction... What gives?
Bitstream is giving away these fonts, but wishes to ensure its
competitors can't just drop the fonts as is into a font sale system
and sell them as is. It seems fair that if Bitstream can't make money
from the Bitstream Vera fonts, their competitors should not be able to
do so either. You can sell the fonts as part of any software package,
however.
2. I want to package these fonts separately for distribution and
sale as part of a larger software package or system. Can I do so?
Yes. A RPM or Debian package is a "larger software package" to begin
with, and you aren't selling them independently by themselves.
See 1. above.
3. Are derivative works allowed?
Yes!
4. Can I change or add to the font(s)?
Yes, but you must change the name(s) of the font(s).
5. Under what terms are derivative works allowed?
You must change the name(s) of the fonts. This is to ensure the
quality of the fonts, both to protect Bitstream and Gnome. We want to
ensure that if an application has opened a font specifically of these
names, it gets what it expects (though of course, using fontconfig,
substitutions could still could have occurred during font
opening). You must include the Bitstream copyright. Additional
copyrights can be added, as per copyright law. Happy Font Hacking!
6. If I have improvements for Bitstream Vera, is it possible they might get
adopted in future versions?
Yes. The contract between the Gnome Foundation and Bitstream has
provisions for working with Bitstream to ensure quality additions to
the Bitstream Vera font family. Please contact us if you have such
additions. Note, that in general, we will want such additions for the
entire family, not just a single font, and that you'll have to keep
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
glyphs to the font, they must be stylistically in keeping with Vera's
design. Vera cannot become a "ransom note" font. Jim Lyles will be
providing a document describing the design elements used in Vera, as a
guide and aid for people interested in contributing to Vera.
7. I want to sell a software package that uses these fonts: Can I do so?
Sure. Bundle the fonts with your software and sell your software
with the fonts. That is the intent of the copyright.
8. If applications have built the names "Bitstream Vera" into them,
can I override this somehow to use fonts of my choosing?
This depends on exact details of the software. Most open source
systems and software (e.g., Gnome, KDE, etc.) are now converting to
use fontconfig (see www.fontconfig.org) to handle font configuration,
selection and substitution; it has provisions for overriding font
names and subsituting alternatives. An example is provided by the
supplied local.conf file, which chooses the family Bitstream Vera for
"sans", "serif" and "monospace". Other software (e.g., the XFree86
core server) has other mechanisms for font substitution.

@ -30,7 +30,6 @@ configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_
option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
# Sound source selection
option(USE_AUDIERE "use Audiere for sound" OFF)
option(USE_FFMPEG "use ffmpeg for sound" OFF)
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
@ -120,52 +119,31 @@ set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
)
# Sound setup
if (USE_AUDIERE)
set(MANGLE_SOUND_OUTPUT
${LIBDIR}/mangle/sound/sources/audiere_source.cpp
${LIBDIR}/mangle/sound/sources/sample_reader.cpp
${LIBDIR}/mangle/stream/clients/audiere_file.cpp)
find_package(Audiere REQUIRED)
set(SOUND_INPUT_INCLUDES ${AUDIERE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${AUDIERE_LIBRARY})
set(SOUND_DEFINE -DOPENMW_USE_AUDIERE)
endif (USE_AUDIERE)
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET})
source_group(libs\\openengine FILES ${OENGINE_ALL})
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
set(OPENMW_LIBS_HEADER)
# Sound setup
set(SOUND_INPUT_INCLUDES "")
set(SOUND_INPUT_LIBRARY "")
set(SOUND_DEFINE "")
if (USE_FFMPEG)
set(MANGLE_SOUND_OUTPUT
${LIBDIR}/mangle/sound/sources/ffmpeg_source.cpp)
find_package(FFMPEG REQUIRED)
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
set(SOUND_DEFINE -DOPENMW_USE_FFMPEG)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
endif (USE_FFMPEG)
if (USE_MPG123)
set(MANGLE_SOUND_OUTPUT
${LIBDIR}/mangle/sound/sources/mpg123_source.cpp
${LIBDIR}/mangle/sound/sources/libsndfile.cpp
${LIBDIR}/mangle/sound/sources/sample_reader.cpp)
find_package(MPG123 REQUIRED)
find_package(SNDFILE REQUIRED)
set(SOUND_INPUT_INCLUDES ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
set(SOUND_DEFINE -DOPENMW_USE_MPG123)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
endif (USE_MPG123)
set(OENGINE_SOUND
# Mangle and OEngine sound files are sort of intertwined, so put
# them together here
${LIBDIR}/openengine/sound/sndmanager.cpp
${LIBDIR}/mangle/sound/outputs/openal_out.cpp
${MANGLE_SOUND_OUTPUT}
)
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND} ${OENGINE_BULLET})
source_group(libs\\openengine FILES ${OENGINE_ALL})
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
set(OPENMW_LIBS_HEADER)
# Platform specific
if (WIN32)
set(PLATFORM_INCLUDE_DIR "platform")

@ -0,0 +1,93 @@
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -45,9 +45,28 @@ MainDialog::MainDialog()
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setMinimumSize(QSize(575, 575));
// Install the stylesheet font
QFile file;
QFontDatabase fontDatabase;
const QStringList fonts = fontDatabase.families();
// Check if the font is installed
if (!fonts.contains("EB Garamond")) {
QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
file.setFileName(font);
if (!file.exists()) {
font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
}
fontDatabase.addApplicationFont(font);
}
// Load the stylesheet
QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string());
QFile file(config);
file.setFileName(config);
if (!file.exists()) {
file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string()));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 50 KiB

@ -39,7 +39,7 @@ add_openmw_dir (mwscript
)
add_openmw_dir (mwsound
soundmanager
soundmanager openal_output mpgsnd_decoder ffmpeg_decoder
)
add_openmw_dir (mwworld

@ -117,11 +117,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
// sound
if (mUseSound)
{
mEnvironment.mSoundManager->playPlaylist();
mEnvironment.mSoundManager->update (evt.timeSinceLastFrame);
}
// update GUI
Ogre::RenderWindow* window = mOgre->getWindow();
@ -337,10 +333,7 @@ void OMW::Engine::go()
mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"));
// Create sound system
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(),
mOgre->getCamera(),
mDataDirs,
mUseSound, mFSStrict, mEnvironment);
mEnvironment.mSoundManager = new MWSound::SoundManager(mUseSound, mEnvironment);
// Create script system
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,

@ -202,6 +202,7 @@ void MapWindow::setVisible(bool b)
void MapWindow::setCellName(const std::string& cellName)
{
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(cellName);
adjustWindowCaption();
}
void MapWindow::setPlayerPos(const float x, const float y)

@ -0,0 +1,407 @@
#ifdef OPENMW_USE_FFMPEG
#include "ffmpeg_decoder.hpp"
namespace MWSound
{
static void fail(const std::string &msg)
{ throw std::runtime_error("FFmpeg exception: "+msg); }
struct PacketList {
AVPacket pkt;
PacketList *next;
};
struct FFmpeg_Decoder::MyStream {
AVCodecContext *mCodecCtx;
int mStreamIdx;
PacketList *mPackets;
char *mDecodedData;
size_t mDecodedDataSize;
FFmpeg_Decoder *mParent;
void clearPackets();
void *getAVAudioData(size_t *length);
size_t readAVAudioData(void *data, size_t length);
};
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
{
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
return stream->read(buf, buf_size);
}
int FFmpeg_Decoder::writePacket(void *user_data, uint8_t *buf, int buf_size)
{
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
return stream->write(buf, buf_size);
}
int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
{
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
whence &= ~AVSEEK_FORCE;
if(whence == AVSEEK_SIZE)
return stream->size();
if(whence == SEEK_SET)
stream->seek(offset);
else if(whence == SEEK_CUR)
stream->seek(stream->tell()+offset);
else if(whence == SEEK_END)
stream->seek(stream->size()+offset);
else
return -1;
return stream->tell();
}
/* Used by getAV*Data to search for more compressed data, and buffer it in the
* correct stream. It won't buffer data for streams that the app doesn't have a
* handle for. */
bool FFmpeg_Decoder::getNextPacket(int streamidx)
{
PacketList *packet;
packet = (PacketList*)av_malloc(sizeof(*packet));
packet->next = NULL;
next_packet:
while(av_read_frame(mFormatCtx, &packet->pkt) >= 0)
{
std::vector<MyStream*>::iterator iter = mStreams.begin();
/* Check each stream the user has a handle for, looking for the one
* this packet belongs to */
while(iter != mStreams.end())
{
if((*iter)->mStreamIdx == packet->pkt.stream_index)
{
PacketList **last;
last = &(*iter)->mPackets;
while(*last != NULL)
last = &(*last)->next;
*last = packet;
if((*iter)->mStreamIdx == streamidx)
return true;
packet = (PacketList*)av_malloc(sizeof(*packet));
packet->next = NULL;
goto next_packet;
}
iter++;
}
/* Free the packet and look for another */
av_free_packet(&packet->pkt);
}
av_free(packet);
return false;
}
void FFmpeg_Decoder::MyStream::clearPackets()
{
while(mPackets)
{
PacketList *self = mPackets;
mPackets = self->next;
av_free_packet(&self->pkt);
av_free(self);
}
}
void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length)
{
int size;
int len;
if(length) *length = 0;
if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
return NULL;
mDecodedDataSize = 0;
next_packet:
if(!mPackets && !mParent->getNextPacket(mStreamIdx))
return NULL;
/* Decode some data, and check for errors */
size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
while((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size,
&mPackets->pkt)) == 0)
{
PacketList *self;
if(size > 0)
break;
/* Packet went unread and no data was given? Drop it and try the next,
* I guess... */
self = mPackets;
mPackets = self->next;
av_free_packet(&self->pkt);
av_free(self);
if(!mPackets)
goto next_packet;
size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
}
if(len < 0)
return NULL;
if(len < mPackets->pkt.size)
{
/* Move the unread data to the front and clear the end bits */
int remaining = mPackets->pkt.size - len;
memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining);
memset(&mPackets->pkt.data[remaining], 0, mPackets->pkt.size - remaining);
mPackets->pkt.size -= len;
}
else
{
PacketList *self;
self = mPackets;
mPackets = self->next;
av_free_packet(&self->pkt);
av_free(self);
}
if(size == 0)
goto next_packet;
/* Set the output buffer size */
mDecodedDataSize = size;
if(length) *length = mDecodedDataSize;
return mDecodedData;
}
size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length)
{
size_t dec = 0;
while(dec < length)
{
/* If there's no decoded data, find some */
if(mDecodedDataSize == 0)
{
if(getAVAudioData(NULL) == NULL)
break;
}
if(mDecodedDataSize > 0)
{
/* Get the amount of bytes remaining to be written, and clamp to
* the amount of decoded data we have */
size_t rem = length-dec;
if(rem > mDecodedDataSize)
rem = mDecodedDataSize;
/* Copy the data to the app's buffer and increment */
if(data != NULL)
{
memcpy(data, mDecodedData, rem);
data = (char*)data + rem;
}
dec += rem;
/* If there's any decoded data left, move it to the front of the
* buffer for next time */
if(rem < mDecodedDataSize)
memmove(mDecodedData, &mDecodedData[rem], mDecodedDataSize - rem);
mDecodedDataSize -= rem;
}
}
/* Return the number of bytes we were able to get */
return dec;
}
void FFmpeg_Decoder::open(const std::string &fname)
{
close();
mDataStream = mResourceMgr.openResource(fname);
if((mFormatCtx=avformat_alloc_context()) == NULL)
fail("Failed to allocate context");
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
{
avformat_free_context(mFormatCtx);
mFormatCtx = NULL;
fail("Failed to allocate input stream");
}
try
{
if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
fail("Failed to find stream info in "+fname);
for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
{
if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
std::auto_ptr<MyStream> stream(new MyStream);
stream->mCodecCtx = mFormatCtx->streams[j]->codec;
stream->mStreamIdx = j;
stream->mPackets = NULL;
AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id);
if(!codec)
{
std::stringstream ss("No codec found for id ");
ss << stream->mCodecCtx->codec_id;
fail(ss.str());
}
if(avcodec_open(stream->mCodecCtx, codec) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name));
stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
stream->mDecodedDataSize = 0;
stream->mParent = this;
mStreams.push_back(stream.release());
break;
}
}
if(mStreams.empty())
fail("No audio streams in "+fname);
}
catch(std::exception &e)
{
av_close_input_file(mFormatCtx);
mFormatCtx = NULL;
throw;
}
}
void FFmpeg_Decoder::close()
{
while(!mStreams.empty())
{
MyStream *stream = mStreams.front();
stream->clearPackets();
avcodec_close(stream->mCodecCtx);
av_free(stream->mDecodedData);
delete stream;
mStreams.erase(mStreams.begin());
}
if(mFormatCtx)
av_close_input_file(mFormatCtx);
mFormatCtx = NULL;
mDataStream.setNull();
}
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{
if(mStreams.empty())
fail("No audio stream info");
MyStream *stream = mStreams[0];
if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8)
*type = SampleType_UInt8;
else if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16)
*type = SampleType_Int16;
else
fail(std::string("Unsupported sample format: ")+
av_get_sample_fmt_name(stream->mCodecCtx->sample_fmt));
if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
*chans = ChannelConfig_Mono;
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO)
*chans = ChannelConfig_Stereo;
else if(stream->mCodecCtx->channel_layout == 0)
{
/* Unknown channel layout. Try to guess. */
if(stream->mCodecCtx->channels == 1)
*chans = ChannelConfig_Mono;
else if(stream->mCodecCtx->channels == 2)
*chans = ChannelConfig_Stereo;
else
{
std::stringstream sstr("Unsupported raw channel count: ");
sstr << stream->mCodecCtx->channels;
fail(sstr.str());
}
}
else
{
char str[1024];
av_get_channel_layout_string(str, sizeof(str), stream->mCodecCtx->channels,
stream->mCodecCtx->channel_layout);
fail(std::string("Unsupported channel layout: ")+str);
}
*samplerate = stream->mCodecCtx->sample_rate;
}
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
{
if(mStreams.empty())
fail("No audio streams");
return mStreams.front()->readAVAudioData(buffer, bytes);
}
void FFmpeg_Decoder::readAll(std::vector<char> &output)
{
if(mStreams.empty())
fail("No audio streams");
MyStream *stream = mStreams.front();
char *inbuf;
size_t got;
while((inbuf=(char*)stream->getAVAudioData(&got)) != NULL && got > 0)
output.insert(output.end(), inbuf, inbuf+got);
}
void FFmpeg_Decoder::rewind()
{
av_seek_frame(mFormatCtx, -1, 0, 0);
std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets));
}
FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL)
{
static bool done_init = false;
/* We need to make sure ffmpeg is initialized. Optionally silence warning
* output from the lib */
if(!done_init)
{
av_register_all();
av_log_set_level(AV_LOG_ERROR);
done_init = true;
}
}
FFmpeg_Decoder::~FFmpeg_Decoder()
{
close();
}
}
#endif

@ -0,0 +1,59 @@
#ifndef GAME_SOUND_FFMPEG_DECODER_H
#define GAME_SOUND_FFMPEG_DECODER_H
#include <string>
// FIXME: This can't be right? The headers refuse to build without UINT64_C,
// which only gets defined in stdint.h in either C99 mode or with this macro
// defined...
#define __STDC_CONSTANT_MACROS
#include <stdint.h>
extern "C"
{
#include <avcodec.h>
#include <avformat.h>
}
#include "sound_decoder.hpp"
namespace MWSound
{
class FFmpeg_Decoder : public Sound_Decoder
{
AVFormatContext *mFormatCtx;
struct MyStream;
std::vector<MyStream*> mStreams;
bool getNextPacket(int streamidx);
Ogre::DataStreamPtr mDataStream;
static int readPacket(void *user_data, uint8_t *buf, int buf_size);
static int writePacket(void *user_data, uint8_t *buf, int buf_size);
static int64_t seek(void *user_data, int64_t offset, int whence);
virtual void open(const std::string &fname);
virtual void close();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes);
virtual void readAll(std::vector<char> &output);
virtual void rewind();
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder();
public:
virtual ~FFmpeg_Decoder();
friend class SoundManager;
};
#ifndef DEFAULT_DECODER
#define DEFAULT_DECODER (::MWSound::FFmpeg_Decoder)
#endif
};
#endif

@ -0,0 +1,231 @@
#ifdef OPENMW_USE_MPG123
#include <stdexcept>
#include <iostream>
#include "mpgsnd_decoder.hpp"
static void fail(const std::string &msg)
{ throw std::runtime_error("MpgSnd exception: "+msg); }
namespace MWSound
{
//
// libSndFile io callbacks
//
sf_count_t MpgSnd_Decoder::ogresf_get_filelen(void *user_data)
{
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
return stream->size();
}
sf_count_t MpgSnd_Decoder::ogresf_seek(sf_count_t offset, int whence, void *user_data)
{
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
if(whence == SEEK_CUR)
stream->seek(stream->tell()+offset);
else if(whence == SEEK_SET)
stream->seek(offset);
else if(whence == SEEK_END)
stream->seek(stream->size()+offset);
else
return -1;
return stream->tell();
}
sf_count_t MpgSnd_Decoder::ogresf_read(void *ptr, sf_count_t count, void *user_data)
{
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
return stream->read(ptr, count);
}
sf_count_t MpgSnd_Decoder::ogresf_write(const void*, sf_count_t, void*)
{ return -1; }
sf_count_t MpgSnd_Decoder::ogresf_tell(void *user_data)
{
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
return stream->tell();
}
//
// libmpg13 io callbacks
//
ssize_t MpgSnd_Decoder::ogrempg_read(void *user_data, void *ptr, size_t count)
{
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
return stream->read(ptr, count);
}
off_t MpgSnd_Decoder::ogrempg_lseek(void *user_data, off_t offset, int whence)
{
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
if(whence == SEEK_CUR)
stream->seek(stream->tell()+offset);
else if(whence == SEEK_SET)
stream->seek(offset);
else if(whence == SEEK_END)
stream->seek(stream->size()+offset);
else
return -1;
return stream->tell();
}
void MpgSnd_Decoder::open(const std::string &fname)
{
close();
mDataStream = mResourceMgr.openResource(fname);
SF_VIRTUAL_IO streamIO = {
ogresf_get_filelen, ogresf_seek,
ogresf_read, ogresf_write, ogresf_tell
};
mSndFile = sf_open_virtual(&streamIO, SFM_READ, &mSndInfo, this);
if(mSndFile)
{
if(mSndInfo.channels == 1)
mChanConfig = ChannelConfig_Mono;
else if(mSndInfo.channels == 2)
mChanConfig = ChannelConfig_Stereo;
else
{
sf_close(mSndFile);
mSndFile = NULL;
fail("Unsupported channel count in "+fname);
}
mSampleRate = mSndInfo.samplerate;
return;
}
mDataStream->seek(0);
mMpgFile = mpg123_new(NULL, NULL);
if(mMpgFile && mpg123_replace_reader_handle(mMpgFile, ogrempg_read, ogrempg_lseek, NULL) == MPG123_OK &&
mpg123_open_handle(mMpgFile, this) == MPG123_OK)
{
try
{
int encoding, channels;
long rate;
if(mpg123_getformat(mMpgFile, &rate, &channels, &encoding) != MPG123_OK)
fail("Failed to get audio format");
if(encoding != MPG123_ENC_SIGNED_16)
fail("Unsupported encoding in "+fname);
if(channels != 1 && channels != 2)
fail("Unsupported channel count in "+fname);
mChanConfig = ((channels==2)?ChannelConfig_Stereo:ChannelConfig_Mono);
mSampleRate = rate;
return;
}
catch(std::exception &e)
{
mpg123_close(mMpgFile);
mpg123_delete(mMpgFile);
mMpgFile = NULL;
throw;
}
mpg123_close(mMpgFile);
}
if(mMpgFile)
mpg123_delete(mMpgFile);
mMpgFile = NULL;
fail("Unsupported file type: "+fname);
}
void MpgSnd_Decoder::close()
{
if(mSndFile)
sf_close(mSndFile);
mSndFile = NULL;
if(mMpgFile)
{
mpg123_close(mMpgFile);
mpg123_delete(mMpgFile);
mMpgFile = NULL;
}
mDataStream.setNull();
}
void MpgSnd_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{
if(!mSndFile && !mMpgFile)
fail("No open file");
*samplerate = mSampleRate;
*chans = mChanConfig;
*type = SampleType_Int16;
}
size_t MpgSnd_Decoder::read(char *buffer, size_t bytes)
{
size_t got = 0;
if(mSndFile)
{
got = sf_read_short(mSndFile, (short*)buffer, bytes/2)*2;
}
else if(mMpgFile)
{
int err;
err = mpg123_read(mMpgFile, (unsigned char*)buffer, bytes, &got);
if(err != MPG123_OK && err != MPG123_DONE)
fail("Failed to read from file");
}
return got;
}
void MpgSnd_Decoder::readAll(std::vector<char> &output)
{
if(mSndFile && mSndInfo.frames > 0)
{
size_t pos = output.size();
output.resize(pos + mSndInfo.frames*mSndInfo.channels*2);
sf_readf_short(mSndFile, (short*)(output.data()+pos), mSndInfo.frames);
return;
}
// Fallback in case we don't know the total already
Sound_Decoder::readAll(output);
}
void MpgSnd_Decoder::rewind()
{
if(!mSndFile && !mMpgFile)
fail("No open file");
if(mSndFile)
{
if(sf_seek(mSndFile, 0, SEEK_SET) == -1)
fail("seek failed");
}
else if(mMpgFile)
{
if(mpg123_seek(mMpgFile, 0, SEEK_SET) < 0)
fail("seek failed");
}
}
MpgSnd_Decoder::MpgSnd_Decoder() : mSndFile(NULL), mMpgFile(NULL)
{
static bool initdone = false;
if(!initdone)
mpg123_init();
initdone = true;
}
MpgSnd_Decoder::~MpgSnd_Decoder()
{
close();
}
}
#endif

@ -0,0 +1,57 @@
#ifndef GAME_SOUND_MPGSND_DECODER_H
#define GAME_SOUND_MPGSND_DECODER_H
#include <string>
#include <OgreDataStream.h>
#include "mpg123.h"
#include "sndfile.h"
#include "sound_decoder.hpp"
namespace MWSound
{
class MpgSnd_Decoder : public Sound_Decoder
{
SF_INFO mSndInfo;
SNDFILE *mSndFile;
mpg123_handle *mMpgFile;
Ogre::DataStreamPtr mDataStream;
static sf_count_t ogresf_get_filelen(void *user_data);
static sf_count_t ogresf_seek(sf_count_t offset, int whence, void *user_data);
static sf_count_t ogresf_read(void *ptr, sf_count_t count, void *user_data);
static sf_count_t ogresf_write(const void*, sf_count_t, void*);
static sf_count_t ogresf_tell(void *user_data);
static ssize_t ogrempg_read(void*, void*, size_t);
static off_t ogrempg_lseek(void*, off_t, int);
ChannelConfig mChanConfig;
int mSampleRate;
virtual void open(const std::string &fname);
virtual void close();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes);
virtual void readAll(std::vector<char> &output);
virtual void rewind();
MpgSnd_Decoder& operator=(const MpgSnd_Decoder &rhs);
MpgSnd_Decoder(const MpgSnd_Decoder &rhs);
MpgSnd_Decoder();
public:
virtual ~MpgSnd_Decoder();
friend class SoundManager;
};
#ifndef DEFAULT_DECODER
#define DEFAULT_DECODER (::MWSound::MpgSnd_Decoder)
#endif
};
#endif

@ -0,0 +1,772 @@
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <vector>
#include <boost/thread.hpp>
#include "openal_output.hpp"
#include "sound_decoder.hpp"
#include "sound.hpp"
#include "soundmanager.hpp"
#ifndef ALC_ALL_DEVICES_SPECIFIER
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
#endif
namespace MWSound
{
static void fail(const std::string &msg)
{ throw std::runtime_error("OpenAL exception: " + msg); }
static void throwALCerror(ALCdevice *device)
{
ALCenum err = alcGetError(device);
if(err != ALC_NO_ERROR)
fail(alcGetString(device, err));
}
static void throwALerror()
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
fail(alGetString(err));
}
static ALenum getALFormat(ChannelConfig chans, SampleType type)
{
static const struct {
ALenum format;
ChannelConfig chans;
SampleType type;
} fmtlist[] = {
{ AL_FORMAT_MONO16, ChannelConfig_Mono, SampleType_Int16 },
{ AL_FORMAT_MONO8, ChannelConfig_Mono, SampleType_UInt8 },
{ AL_FORMAT_STEREO16, ChannelConfig_Stereo, SampleType_Int16 },
{ AL_FORMAT_STEREO8, ChannelConfig_Stereo, SampleType_UInt8 },
};
static const size_t fmtlistsize = sizeof(fmtlist)/sizeof(fmtlist[0]);
for(size_t i = 0;i < fmtlistsize;i++)
{
if(fmtlist[i].chans == chans && fmtlist[i].type == type)
return fmtlist[i].format;
}
fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")");
return AL_NONE;
}
//
// A streaming OpenAL sound.
//
class OpenAL_SoundStream : public Sound
{
static const ALuint sNumBuffers = 6;
static const ALfloat sBufferLength = 0.125f;
OpenAL_Output &mOutput;
ALuint mSource;
ALuint mBuffers[sNumBuffers];
ALenum mFormat;
ALsizei mSampleRate;
ALuint mBufferSize;
DecoderPtr mDecoder;
volatile bool mIsFinished;
OpenAL_SoundStream(const OpenAL_SoundStream &rhs);
OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs);
public:
OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder);
virtual ~OpenAL_SoundStream();
virtual void stop();
virtual bool isPlaying();
virtual void update(const float *pos);
void play();
bool process();
};
//
// A background streaming thread (keeps active streams processed)
//
struct OpenAL_Output::StreamThread {
typedef std::vector<OpenAL_SoundStream*> StreamVec;
StreamVec mStreams;
boost::mutex mMutex;
boost::thread mThread;
StreamThread()
: mThread(boost::ref(*this))
{
}
~StreamThread()
{
mThread.interrupt();
}
// boost::thread entry point
void operator()()
{
while(1)
{
mMutex.lock();
StreamVec::iterator iter = mStreams.begin();
while(iter != mStreams.end())
{
if((*iter)->process() == false)
iter = mStreams.erase(iter);
else
iter++;
}
mMutex.unlock();
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
}
}
void add(OpenAL_SoundStream *stream)
{
mMutex.lock();
if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end())
mStreams.push_back(stream);
mMutex.unlock();
}
void remove(OpenAL_SoundStream *stream)
{
mMutex.lock();
StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream);
if(iter != mStreams.end())
mStreams.erase(iter);
mMutex.unlock();
}
void removeAll()
{
mMutex.lock();
mStreams.clear();
mMutex.unlock();
}
private:
StreamThread(const StreamThread &rhs);
StreamThread& operator=(const StreamThread &rhs);
};
OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder)
: mOutput(output), mSource(src), mDecoder(decoder), mIsFinished(true)
{
throwALerror();
alGenBuffers(sNumBuffers, mBuffers);
throwALerror();
try
{
int srate;
ChannelConfig chans;
SampleType type;
mDecoder->getInfo(&srate, &chans, &type);
mFormat = getALFormat(chans, type);
mSampleRate = srate;
mBufferSize = static_cast<ALuint>(sBufferLength*srate);
mBufferSize = framesToBytes(mBufferSize, chans, type);
}
catch(std::exception &e)
{
mOutput.mFreeSources.push_back(mSource);
alDeleteBuffers(sNumBuffers, mBuffers);
alGetError();
throw;
}
}
OpenAL_SoundStream::~OpenAL_SoundStream()
{
mOutput.mStreamThread->remove(this);
alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0);
mOutput.mFreeSources.push_back(mSource);
alDeleteBuffers(sNumBuffers, mBuffers);
alGetError();
mDecoder->close();
}
void OpenAL_SoundStream::play()
{
std::vector<char> data(mBufferSize);
alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0);
throwALerror();
for(ALuint i = 0;i < sNumBuffers;i++)
{
size_t got;
got = mDecoder->read(data.data(), data.size());
alBufferData(mBuffers[i], mFormat, data.data(), got, mSampleRate);
}
throwALerror();
alSourceQueueBuffers(mSource, sNumBuffers, mBuffers);
alSourcePlay(mSource);
throwALerror();
mIsFinished = false;
mOutput.mStreamThread->add(this);
}
void OpenAL_SoundStream::stop()
{
mOutput.mStreamThread->remove(this);
mIsFinished = true;
alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0);
throwALerror();
mDecoder->rewind();
}
bool OpenAL_SoundStream::isPlaying()
{
ALint state;
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
throwALerror();
if(state == AL_PLAYING)
return true;
return !mIsFinished;
}
void OpenAL_SoundStream::update(const float *pos)
{
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
throwALerror();
}
bool OpenAL_SoundStream::process()
{
bool finished = mIsFinished;
ALint processed, state;
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
throwALerror();
if(processed > 0)
{
std::vector<char> data(mBufferSize);
do {
ALuint bufid;
size_t got;
alSourceUnqueueBuffers(mSource, 1, &bufid);
processed--;
if(finished)
continue;
got = mDecoder->read(data.data(), data.size());
finished = (got < data.size());
if(got > 0)
{
alBufferData(bufid, mFormat, data.data(), got, mSampleRate);
alSourceQueueBuffers(mSource, 1, &bufid);
}
} while(processed > 0);
throwALerror();
}
if(state != AL_PLAYING && state != AL_PAUSED)
{
ALint queued;
alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
throwALerror();
if(queued > 0)
{
alSourcePlay(mSource);
throwALerror();
}
}
mIsFinished = finished;
return !finished;
}
//
// A regular OpenAL sound
//
class OpenAL_Sound : public Sound
{
OpenAL_Output &mOutput;
ALuint mSource;
ALuint mBuffer;
OpenAL_Sound(const OpenAL_Sound &rhs);
OpenAL_Sound& operator=(const OpenAL_Sound &rhs);
public:
OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf);
virtual ~OpenAL_Sound();
virtual void stop();
virtual bool isPlaying();
virtual void update(const float *pos);
};
OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf)
: mOutput(output), mSource(src), mBuffer(buf)
{
}
OpenAL_Sound::~OpenAL_Sound()
{
alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0);
mOutput.mFreeSources.push_back(mSource);
mOutput.bufferFinished(mBuffer);
}
void OpenAL_Sound::stop()
{
alSourceStop(mSource);
throwALerror();
}
bool OpenAL_Sound::isPlaying()
{
ALint state;
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
throwALerror();
return state==AL_PLAYING;
}
void OpenAL_Sound::update(const float *pos)
{
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
throwALerror();
}
//
// An OpenAL output device
//
std::vector<std::string> OpenAL_Output::enumerate()
{
std::vector<std::string> devlist;
const ALCchar *devnames;
if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
devnames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
else
devnames = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
while(devnames && *devnames)
{
devlist.push_back(devnames);
devnames += strlen(devnames)+1;
}
return devlist;
}
void OpenAL_Output::init(const std::string &devname)
{
if(mDevice || mContext)
fail("Device already open");
mDevice = alcOpenDevice(devname.c_str());
if(!mDevice)
{
if(devname.empty())
fail("Failed to open default device");
else
fail("Failed to open \""+devname+"\"");
}
if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
std::cout << "Opened \""<<alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER)<<"\"" << std::endl;
else
std::cout << "Opened \""<<alcGetString(mDevice, ALC_DEVICE_SPECIFIER)<<"\"" << std::endl;
mContext = alcCreateContext(mDevice, NULL);
if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice)));
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
throwALerror();
ALCint maxmono, maxstereo;
alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
throwALCerror(mDevice);
mFreeSources.resize(std::min(maxmono+maxstereo, 256));
for(size_t i = 0;i < mFreeSources.size();i++)
{
ALuint src;
alGenSources(1, &src);
if(alGetError() != AL_NO_ERROR)
{
mFreeSources.resize(i);
break;
}
mFreeSources[i] = src;
}
if(mFreeSources.size() == 0)
fail("Could not allocate any sources");
}
void OpenAL_Output::deinit()
{
mStreamThread->removeAll();
if(!mFreeSources.empty())
{
alDeleteSources(mFreeSources.size(), mFreeSources.data());
mFreeSources.clear();
}
mBufferRefs.clear();
mUnusedBuffers.clear();
while(!mBufferCache.empty())
{
alDeleteBuffers(1, &mBufferCache.begin()->second);
mBufferCache.erase(mBufferCache.begin());
}
alcMakeContextCurrent(0);
if(mContext)
alcDestroyContext(mContext);
mContext = 0;
if(mDevice)
alcCloseDevice(mDevice);
mDevice = 0;
}
ALuint OpenAL_Output::getBuffer(const std::string &fname)
{
ALuint buf = 0;
NameMap::iterator iditer = mBufferCache.find(fname);
if(iditer != mBufferCache.end())
{
buf = iditer->second;
if(mBufferRefs[buf]++ == 0)
{
IDDq::iterator iter = std::find(mUnusedBuffers.begin(),
mUnusedBuffers.end(), buf);
if(iter != mUnusedBuffers.end())
mUnusedBuffers.erase(iter);
}
return buf;
}
throwALerror();
std::vector<char> data;
ChannelConfig chans;
SampleType type;
ALenum format;
int srate;
DecoderPtr decoder = mManager.getDecoder();
try
{
decoder->open(fname);
}
catch(Ogre::FileNotFoundException &e)
{
std::string::size_type pos = fname.rfind('.');
if(pos == std::string::npos)
throw;
decoder->open(fname.substr(0, pos)+".mp3");
}
decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type);
decoder->readAll(data);
decoder->close();
alGenBuffers(1, &buf);
throwALerror();
alBufferData(buf, format, data.data(), data.size(), srate);
mBufferCache[fname] = buf;
mBufferRefs[buf] = 1;
ALint bufsize = 0;
alGetBufferi(buf, AL_SIZE, &bufsize);
mBufferCacheMemSize += bufsize;
// NOTE: Max buffer cache: 15MB
while(mBufferCacheMemSize > 15*1024*1024)
{
if(mUnusedBuffers.empty())
{
std::cout <<"No more unused buffers to clear!"<< std::endl;
break;
}
ALuint oldbuf = mUnusedBuffers.front();
mUnusedBuffers.pop_front();
NameMap::iterator nameiter = mBufferCache.begin();
while(nameiter != mBufferCache.end())
{
if(nameiter->second == oldbuf)
mBufferCache.erase(nameiter++);
else
nameiter++;
}
bufsize = 0;
alGetBufferi(oldbuf, AL_SIZE, &bufsize);
alDeleteBuffers(1, &oldbuf);
mBufferCacheMemSize -= bufsize;
}
return buf;
}
void OpenAL_Output::bufferFinished(ALuint buf)
{
if(mBufferRefs.at(buf)-- == 1)
{
mBufferRefs.erase(mBufferRefs.find(buf));
mUnusedBuffers.push_back(buf);
}
}
Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop)
{
throwALerror();
std::auto_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
try
{
buf = getBuffer(fname);
sound.reset(new OpenAL_Sound(*this, src, buf));
}
catch(std::exception &e)
{
mFreeSources.push_back(src);
if(buf && alIsBuffer(buf))
bufferFinished(buf);
alGetError();
throw;
}
alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f);
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
throwALerror();
alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src);
throwALerror();
return sound.release();
}
Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop)
{
throwALerror();
std::auto_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
try
{
buf = getBuffer(fname);
sound.reset(new OpenAL_Sound(*this, src, buf));
}
catch(std::exception &e)
{
mFreeSources.push_back(src);
if(buf && alIsBuffer(buf))
bufferFinished(buf);
alGetError();
throw;
}
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, min);
alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
throwALerror();
alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src);
throwALerror();
return sound.release();
}
Sound* OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch)
{
throwALerror();
std::auto_ptr<OpenAL_SoundStream> sound;
ALuint src;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
try
{
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
}
catch(std::exception &e)
{
mFreeSources.push_back(src);
throw;
}
alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f);
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, AL_FALSE);
throwALerror();
sound->play();
return sound.release();
}
Sound* OpenAL_Output::streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max)
{
throwALerror();
std::auto_ptr<OpenAL_SoundStream> sound;
ALuint src;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
try
{
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
}
catch(std::exception &e)
{
mFreeSources.push_back(src);
throw;
}
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, min);
alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcei(src, AL_LOOPING, AL_FALSE);
throwALerror();
sound->play();
return sound.release();
}
void OpenAL_Output::updateListener(const float *pos, const float *atdir, const float *updir)
{
float orient[6] = {
atdir[0], atdir[2], -atdir[1],
updir[0], updir[2], -updir[1]
};
alListener3f(AL_POSITION, pos[0], pos[2], -pos[1]);
alListenerfv(AL_ORIENTATION, orient);
throwALerror();
}
OpenAL_Output::OpenAL_Output(SoundManager &mgr)
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
mStreamThread(new StreamThread)
{
}
OpenAL_Output::~OpenAL_Output()
{
deinit();
}
}

@ -0,0 +1,73 @@
#ifndef GAME_SOUND_OPENAL_OUTPUT_H
#define GAME_SOUND_OPENAL_OUTPUT_H
#include <string>
#include <vector>
#include <map>
#include <deque>
#include "alc.h"
#include "al.h"
#include "sound_output.hpp"
namespace MWSound
{
class SoundManager;
class Sound;
class OpenAL_Output : public Sound_Output
{
ALCdevice *mDevice;
ALCcontext *mContext;
typedef std::vector<ALuint> IDVec;
IDVec mFreeSources;
typedef std::map<std::string,ALuint> NameMap;
NameMap mBufferCache;
typedef std::map<ALuint,ALuint> IDRefMap;
IDRefMap mBufferRefs;
typedef std::deque<ALuint> IDDq;
IDDq mUnusedBuffers;
uint64_t mBufferCacheMemSize;
ALuint getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer);
virtual std::vector<std::string> enumerate();
virtual void init(const std::string &devname="");
virtual void deinit();
virtual Sound *playSound(const std::string &fname, float volume, float pitch, bool loop);
virtual Sound *playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop);
virtual Sound *streamSound(const std::string &fname, float volume, float pitch);
virtual Sound *streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max);
virtual void updateListener(const float *pos, const float *atdir, const float *updir);
OpenAL_Output& operator=(const OpenAL_Output &rhs);
OpenAL_Output(const OpenAL_Output &rhs);
OpenAL_Output(SoundManager &mgr);
virtual ~OpenAL_Output();
class StreamThread;
std::auto_ptr<StreamThread> mStreamThread;
friend class OpenAL_Sound;
friend class OpenAL_SoundStream;
friend class SoundManager;
};
#ifndef DEFAULT_OUTPUT
#define DEFAULT_OUTPUT (::MWSound::OpenAL_Output)
#endif
};
#endif

@ -0,0 +1,24 @@
#ifndef GAME_SOUND_SOUND_H
#define GAME_SOUND_SOUND_H
namespace MWSound
{
class Sound
{
virtual void stop() = 0;
virtual bool isPlaying() = 0;
virtual void update(const float *pos) = 0;
Sound& operator=(const Sound &rhs);
Sound(const Sound &rhs);
public:
Sound() { }
virtual ~Sound() { }
friend class OpenAL_Output;
friend class SoundManager;
};
}
#endif

@ -0,0 +1,48 @@
#ifndef GAME_SOUND_SOUND_DECODER_H
#define GAME_SOUND_SOUND_DECODER_H
#include <string>
#include <OgreResourceGroupManager.h>
namespace MWSound
{
enum SampleType {
SampleType_UInt8,
SampleType_Int16
};
const char *getSampleTypeName(SampleType type);
enum ChannelConfig {
ChannelConfig_Mono,
ChannelConfig_Stereo
};
const char *getChannelConfigName(ChannelConfig config);
size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type);
size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type);
struct Sound_Decoder
{
Ogre::ResourceGroupManager &mResourceMgr;
virtual void open(const std::string &fname) = 0;
virtual void close() = 0;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;
virtual size_t read(char *buffer, size_t bytes) = 0;
virtual void readAll(std::vector<char> &output);
virtual void rewind() = 0;
Sound_Decoder() : mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
{ }
virtual ~Sound_Decoder() { }
private:
Sound_Decoder(const Sound_Decoder &rhs);
Sound_Decoder& operator=(const Sound_Decoder &rhs);
};
}
#endif

@ -0,0 +1,44 @@
#ifndef GAME_SOUND_SOUND_OUTPUT_H
#define GAME_SOUND_SOUND_OUTPUT_H
#include <string>
#include <memory>
#include "../mwworld/ptr.hpp"
namespace MWSound
{
class SoundManager;
class Sound_Decoder;
class Sound;
class Sound_Output
{
SoundManager &mManager;
virtual std::vector<std::string> enumerate() = 0;
virtual void init(const std::string &devname="") = 0;
virtual void deinit() = 0;
virtual Sound *playSound(const std::string &fname, float volume, float pitch, bool loop) = 0;
virtual Sound *playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop) = 0;
virtual Sound *streamSound(const std::string &fname, float volume, float pitch) = 0;
virtual Sound *streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max) = 0;
virtual void updateListener(const float *pos, const float *atdir, const float *updir) = 0;
Sound_Output& operator=(const Sound_Output &rhs);
Sound_Output(const Sound_Output &rhs);
Sound_Output(SoundManager &mgr) : mManager(mgr) { }
public:
virtual ~Sound_Output() { }
friend class OpenAL_Output;
friend class SoundManager;
};
}
#endif

@ -6,111 +6,79 @@
#include <OgreRoot.h>
#include <openengine/sound/sndmanager.hpp>
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include <mangle/sound/clients/ogre_output_updater.hpp>
#include <components/esm_store/store.hpp>
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "../mwworld/player.hpp"
/* Set up the sound manager to use Audiere, FFMPEG or
MPG123/libsndfile for input. The OPENMW_USE_x macros are set in
CMakeLists.txt.
*/
#ifdef OPENMW_USE_AUDIERE
#include <mangle/sound/filters/openal_audiere.hpp>
#define SOUND_FACTORY OpenAL_Audiere_Factory
#define SOUND_OUT "OpenAL"
#define SOUND_IN "Audiere"
#endif
#include "sound_output.hpp"
#include "sound_decoder.hpp"
#include "sound.hpp"
#ifdef OPENMW_USE_FFMPEG
#include <mangle/sound/filters/openal_ffmpeg.hpp>
#define SOUND_FACTORY OpenAL_FFMpeg_Factory
#include "openal_output.hpp"
#define SOUND_OUT "OpenAL"
/* Set up the sound manager to use FFMPEG or MPG123+libsndfile for input. The
* OPENMW_USE_x macros are set in CMakeLists.txt.
*/
#ifdef OPENMW_USE_FFMPEG
#include "ffmpeg_decoder.hpp"
#ifndef SOUND_IN
#define SOUND_IN "FFmpeg"
#endif
#endif
#ifdef OPENMW_USE_MPG123
#include <mangle/sound/filters/openal_sndfile_mpg123.hpp>
#define SOUND_FACTORY OpenAL_SndFile_Mpg123_Factory
#define SOUND_OUT "OpenAL"
#include "mpgsnd_decoder.hpp"
#ifndef SOUND_IN
#define SOUND_IN "mpg123,sndfile"
#endif
#endif
using namespace Mangle::Sound;
typedef OEngine::Sound::SoundManager OEManager;
// Set the position on a sound based on a Ptr.
static void setPos(SoundPtr &snd, const MWWorld::Ptr ref)
{
// Get sound position from the reference
const float *pos = ref.getCellRef().pos.pos;
// Move the sound, converting from MW coordinates to Ogre
// coordinates.
snd->setPos(pos[0], pos[2], -pos[1]);
}
namespace MWSound
{
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
const Files::PathContainer& dataDirs,
bool useSound, bool fsstrict, MWWorld::Environment& environment)
: mFSStrict(fsstrict)
SoundManager::SoundManager(bool useSound, MWWorld::Environment& environment)
: mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
, mEnvironment(environment)
, mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
, updater(mgr)
, cameraTracker(mgr)
, mCurrentPlaylist(NULL)
, mUsingSound(useSound)
{
if(useSound)
{
// The music library will accept these filetypes
// If none is given then it will accept all filetypes
std::vector<std::string> acceptableExtensions;
acceptableExtensions.push_back(".mp3");
acceptableExtensions.push_back(".wav");
acceptableExtensions.push_back(".ogg");
acceptableExtensions.push_back(".flac");
// Makes a list of all sound files, searches in reverse for priority reasons
for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it)
{
Files::FileLister(*it / std::string("Sound"), mSoundFiles, true);
}
if(!useSound)
return;
// Makes a FileLibrary of all music files, searches in reverse for priority reasons
for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it)
{
mMusicLibrary.add(*it / std::string("Music"), true, mFSStrict, acceptableExtensions);
}
std::cout << "Sound output: " << SOUND_OUT << std::endl;
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
std::string anything = "anything"; // anything is better that a segfault
mCurrentPlaylist = mMusicLibrary.section(anything, mFSStrict); // now points to an empty path
try
{
mOutput.reset(new DEFAULT_OUTPUT(*this));
std::cout << "Sound output: " << SOUND_OUT << std::endl;
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
// Attach the camera to the camera tracker
cameraTracker.followCamera(camera);
std::vector<std::string> names = mOutput->enumerate();
std::cout <<"Enumerated output devices:"<< std::endl;
for(size_t i = 0;i < names.size();i++)
std::cout <<" "<<names[i]<< std::endl;
// Tell Ogre to update the sound system each frame
root->addFrameListener(&updater);
mOutput->init();
}
catch(std::exception &e)
{
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
mOutput.reset();
return;
}
}
}
SoundManager::~SoundManager()
{
if(mUsingSound)
{
Ogre::Root::getSingleton().removeFrameListener(&updater);
cameraTracker.unfollowCamera();
}
mLooseSounds.clear();
mActiveSounds.clear();
mMusic.reset();
mOutput.reset();
}
// Return a new decoder instance, used as needed by the output implementations
DecoderPtr SoundManager::getDecoder()
{
return DecoderPtr(new DEFAULT_DECODER);
}
// Convert a soundId to file name, and modify the volume
@ -119,418 +87,406 @@ namespace MWSound
std::string SoundManager::lookup(const std::string &soundId,
float &volume, float &min, float &max)
{
const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId);
if(snd == NULL) return "";
if(snd->data.volume == 0)
volume = 0.0f;
else
volume *= pow(10.0, (snd->data.volume/255.0f*3348.0 - 3348.0) / 2000.0);
if(snd->data.minRange == 0 && snd->data.maxRange == 0)
{
min = 100.0f;
max = 2000.0f;
}
else
{
min = snd->data.minRange * 20.0f;
max = snd->data.maxRange * 50.0f;
min = std::max(min, 1.0f);
max = std::max(min, max);
}
return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict, false);
}
const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId);
if(snd == NULL)
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
// Add a sound to the list and play it
void SoundManager::add(const std::string &file,
MWWorld::Ptr ptr,
const std::string &id,
float volume, float pitch,
float min, float max,
bool loop, bool untracked)
{
try
if(snd->data.volume == 0)
volume = 0.0f;
else
volume *= pow(10.0, (snd->data.volume/255.0f*3348.0 - 3348.0) / 2000.0);
if(snd->data.minRange == 0 && snd->data.maxRange == 0)
{
SoundPtr snd = mgr->load(file);
snd->setRepeat(loop);
snd->setVolume(volume);
snd->setPitch(pitch);
snd->setRange(min,max);
setPos(snd, ptr);
snd->play();
if (!untracked)
{
sounds[ptr][id] = WSoundPtr(snd);
}
min = 100.0f;
max = 2000.0f;
}
catch(...)
else
{
std::cout << "Error loading " << file << ", skipping.\n";
min = snd->data.minRange * 20.0f;
max = snd->data.maxRange * 50.0f;
min = std::max(min, 1.0f);
max = std::max(min, max);
}
return std::string("Sound/")+snd->sound;
}
// Clears all the sub-elements of a given iterator, and then
// removes it from 'sounds'.
void SoundManager::clearAll(PtrMap::iterator& it)
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
{
IDMap::iterator sit = it->second.begin();
SoundMap::const_iterator snditer = mActiveSounds.find(ptr);
if(snditer == mActiveSounds.end())
return false;
while(sit != it->second.end())
{
// Get sound pointer, if any
SoundPtr snd = sit->second.lock();
IDMap::const_iterator iditer = snditer->second.find(id);
if(iditer == snditer->second.end())
return false;
// Stop the sound
if(snd) snd->stop();
return true;
}
sit++;
}
// Remove the ptr reference
sounds.erase(it);
void SoundManager::stopMusic()
{
if(mMusic)
mMusic->stop();
mMusic.reset();
}
// Stop a sound and remove it from the list. If id="" then
// remove the entire object and stop all its sounds.
void SoundManager::remove(MWWorld::Ptr ptr, const std::string &id)
void SoundManager::streamMusicFull(const std::string& filename)
{
PtrMap::iterator it = sounds.find(ptr);
if(it != sounds.end())
std::cout <<"Playing "<<filename<< std::endl;
try
{
if(id == "")
// Kill all references to 'ptr'
clearAll(it);
else
{
// Only find the id we're looking for
IDMap::iterator it2 = it->second.find(id);
if(it2 != it->second.end())
{
// Stop the sound and remove it from the list
SoundPtr snd = it2->second.lock();
if(snd) snd->stop();
it->second.erase(it2);
}
}
if(mMusic)
mMusic->stop();
mMusic.reset(mOutput->streamSound(filename, 0.4f, 1.0f));
}
}
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
{
PtrMap::const_iterator it = sounds.find(ptr);
if(it != sounds.end())
catch(std::exception &e)
{
IDMap::const_iterator it2 = it->second.find(id);
if(it2 != it->second.end())
{
// Get a shared_ptr from the weak_ptr
SoundPtr snd = it2->second.lock();;
// Is it still alive?
if(snd)
{
// Then return its status!
return snd->isPlaying();
}
}
std::cout << "Music Error: " << e.what() << "\n";
}
// Nothing found, sound is not playing
return false;
}
// Remove all references to objects belonging to a given cell
void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell)
void SoundManager::streamMusic(const std::string& filename)
{
PtrMap::iterator it2, it = sounds.begin();
while(it != sounds.end())
{
// Make sure to increase the iterator before we erase it.
it2 = it++;
if(it2->first.getCell() == cell)
clearAll(it2);
}
streamMusicFull("Music/"+filename);
}
void SoundManager::updatePositions(MWWorld::Ptr ptr)
void SoundManager::startRandomTitle()
{
// Find the reference (if any)
PtrMap::iterator it = sounds.find(ptr);
if(it != sounds.end())
{
// Then find all sounds in it (if any)
IDMap::iterator it2 = it->second.begin();
for(;it2 != it->second.end(); it2++)
{
// Get the sound (if it still exists)
SoundPtr snd = it2->second.lock();
if(snd)
// Update position
setPos(snd, ptr);
}
}
Ogre::StringVectorPtr filelist;
filelist = mResourceMgr.findResourceNames(Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
"Music/"+mCurrentPlaylist+"/*");
if(!filelist->size())
return;
int i = rand()%filelist->size();
streamMusicFull((*filelist)[i]);
}
void SoundManager::stopMusic()
bool SoundManager::isMusicPlaying()
{
if (music)
music->stop();
setPlaylist();
return mMusic && mMusic->isPlaying();
}
void SoundManager::playPlaylist(const std::string &playlist)
{
mCurrentPlaylist = playlist;
startRandomTitle();
}
void SoundManager::streamMusicFull(const std::string& filename)
{
// Play the sound and tell it to stream, if possible. TODO:
// Store the reference, the jukebox will need to check status,
// control volume etc.
if (music)
music->stop();
music = mgr->load(filename);
music->setStreaming(true);
music->setVolume(0.4);
music->play();
}
void SoundManager::streamMusic(const std::string& filename)
void SoundManager::say(MWWorld::Ptr ptr, const std::string& filename)
{
std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string();
if(!filePath.empty())
try
{
// The range values are not tested
const ESM::Position &pos = ptr.getCellRef().pos;
std::string filePath = std::string("Sound/")+filename;
SoundPtr sound(mOutput->playSound3D(filePath, pos.pos, 1.0f, 1.0f, 100.0f, 20000.0f, false));
mActiveSounds[ptr]["_say_sound"] = sound;
}
catch(std::exception &e)
{
streamMusicFull(filePath);
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
}
void SoundManager::startRandomTitle()
{
if(mCurrentPlaylist && !mCurrentPlaylist->empty())
bool SoundManager::sayDone(MWWorld::Ptr ptr) const
{
Files::PathContainer::const_iterator fileIter = mCurrentPlaylist->begin();
srand( time(NULL) );
int r = rand() % mCurrentPlaylist->size() + 1; //old random code
return !isPlaying(ptr, "_say_sound");
}
std::advance(fileIter, r - 1);
std::string music = fileIter->string();
std::cout << "Playing " << music << "\n";
void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
{
float min, max;
try
{
streamMusicFull(music);
std::string file = lookup(soundId, volume, min, max);
Sound *sound = mOutput->playSound(file, volume, pitch, loop);
mLooseSounds[soundId] = SoundPtr(sound);
}
catch (std::exception &e)
catch(std::exception &e)
{
std::cout << " Music Error: " << e.what() << "\n";
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
}
}
bool SoundManager::isMusicPlaying()
void SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, bool loop, bool untracked)
{
bool test = false;
if(music)
float min, max;
try
{
test = music->isPlaying();
}
return test;
}
// Look up the sound in the ESM data
const ESM::Position &pos = ptr.getCellRef().pos;
std::string file = lookup(soundId, volume, min, max);
bool SoundManager::setPlaylist(std::string playlist)
{
const Files::PathContainer* previousPlaylist;
previousPlaylist = mCurrentPlaylist;
if (playlist == "")
{
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
SoundPtr sound(mOutput->playSound3D(file, pos.pos, volume, pitch, min, max, loop));
if(untracked) mLooseSounds[soundId] = sound;
else mActiveSounds[ptr][soundId] = sound;
}
else if(mMusicLibrary.containsSection(playlist, mFSStrict))
{
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
}
else
catch(std::exception &e)
{
std::cout << "Warning: playlist named " << playlist << " does not exist.\n";
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
return previousPlaylist == mCurrentPlaylist;
}
void SoundManager::playPlaylist(std::string playlist)
void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId)
{
if (!mUsingSound)
// Stop a sound and remove it from the list. If soundId="" then
// stop all its sounds.
SoundMap::iterator snditer = mActiveSounds.find(ptr);
if(snditer == mActiveSounds.end())
return;
if (playlist == "")
if(!soundId.empty())
{
if(!isMusicPlaying())
IDMap::iterator iditer = snditer->second.find(soundId);
if(iditer != snditer->second.end())
{
startRandomTitle();
iditer->second->stop();
snditer->second.erase(iditer);
if(snditer->second.empty())
mActiveSounds.erase(snditer);
}
return;
}
if(!setPlaylist(playlist))
{
startRandomTitle();
}
else if (!isMusicPlaying())
else
{
startRandomTitle();
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
{
iditer->second->stop();
iditer++;
}
mActiveSounds.erase(snditer);
}
}
void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename)
{
if (!mUsingSound)
return;
// The range values are not tested
std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict, true);
if(!filePath.empty())
add(filePath, ptr, "_say_sound", 1, 1, 100, 20000, false);
else
std::cout << "Sound file " << filename << " not found, skipping.\n";
}
bool SoundManager::sayDone (MWWorld::Ptr ptr) const
{
return !isPlaying(ptr, "_say_sound");
}
void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
{
float min, max;
const std::string &file = lookup(soundId, volume, min, max);
if (file != "")
void SoundManager::stopSound(MWWorld::Ptr::CellStore *cell)
{
SoundPtr snd = mgr->load(file);
snd->setRepeat(loop);
snd->setVolume(volume);
snd->setRange(min,max);
snd->setPitch(pitch);
snd->setRelative(true);
snd->play();
if (loop)
// Remove all references to objects belonging to a given cell
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
// Only add the looping sound once
IDMap::iterator it = mLoopedSounds.find(soundId);
if(it == mLoopedSounds.end())
if(snditer->first.getCell() == cell)
{
mLoopedSounds[soundId] = WSoundPtr(snd);
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
{
iditer->second->stop();
iditer++;
}
mActiveSounds.erase(snditer++);
}
else
snditer++;
}
}
}
void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, bool loop, bool untracked)
{
// Look up the sound in the ESM data
float min, max;
const std::string &file = lookup(soundId, volume, min, max);
if (file != "")
add(file, ptr, soundId, volume, pitch, min, max, loop, untracked);
}
void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId)
{
remove(ptr, soundId);
}
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
{
removeCell(cell);
}
void SoundManager::stopSound(const std::string& soundId)
{
IDMap::iterator it = mLoopedSounds.find(soundId);
if(it != mLoopedSounds.end())
IDMap::iterator iditer = mLooseSounds.find(soundId);
if(iditer != mLooseSounds.end())
{
SoundPtr snd = it->second.lock();
if(snd) snd->stop();
mLoopedSounds.erase(it);
iditer->second->stop();
mLooseSounds.erase(iditer);
}
}
bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const
{
// Mark all sounds as playing, otherwise the scripts will just
// keep trying to play them every frame.
bool SoundManager::getSoundPlaying(MWWorld::Ptr ptr, const std::string& soundId) const
{
return isPlaying(ptr, soundId);
}
return isPlaying(ptr, soundId);
}
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
SoundMap::iterator snditer = mActiveSounds.find(ptr);
if(snditer == mActiveSounds.end())
return;
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
updatePositions(ptr);
}
const ESM::Position &pos = ptr.getCellRef().pos;
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
{
iditer->second->update(pos.pos);
iditer++;
}
}
void SoundManager::update (float duration)
{
void SoundManager::updateRegionSound(float duration)
{
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
static int total = 0;
static std::string regionName = "";
static float timePassed = 0.0;
timePassed += duration;
//If the region has changed
if(!(current->cell->data.flags & current->cell->Interior) && timePassed >= 10)
{
timePassed += duration;
if((current->cell->data.flags & current->cell->Interior) || timePassed < 10)
return;
timePassed = 0;
ESM::Region test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region));
if(regionName != current->cell->region)
{
regionName = current->cell->region;
total = 0;
}
timePassed = 0;
if (regionName != current->cell->region)
const ESM::Region *regn = mEnvironment.mWorld->getStore().regions.find(regionName);
std::vector<ESM::Region::SoundRef>::const_iterator soundIter;
if(total == 0)
{
soundIter = regn->soundList.begin();
while(soundIter != regn->soundList.end())
{
regionName = current->cell->region;
total = 0;
total += (int)soundIter->chance;
soundIter++;
}
if(total == 0)
return;
}
int r = (int)(rand()/((double)RAND_MAX+1) * total);
int pos = 0;
if(test.soundList.size() > 0)
soundIter = regn->soundList.begin();
while(soundIter != regn->soundList.end())
{
const std::string go = soundIter->sound.toString();
int chance = (int) soundIter->chance;
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
soundIter++;
if(r - pos < chance)
{
std::vector<ESM::Region::SoundRef>::iterator soundIter = test.soundList.begin();
//mEnvironment.mSoundManager
if(total == 0)
{
while (soundIter != test.soundList.end())
{
int chance = (int) soundIter->chance;
//ESM::NAME32 go = soundIter->sound;
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
soundIter++;
total += chance;
}
}
//play sound
std::cout << "Sound: " << go <<" Chance:" << chance << "\n";
playSound(go, 1.0f, 1.0f);
break;
}
pos += chance;
}
}
int r = rand() % total; //old random code
int pos = 0;
soundIter = test.soundList.begin();
while (soundIter != test.soundList.end())
{
const std::string go = soundIter->sound.toString();
int chance = (int) soundIter->chance;
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
soundIter++;
if( r - pos < chance)
{
//play sound
std::cout << "Sound: " << go <<" Chance:" << chance << "\n";
mEnvironment.mSoundManager->playSound(go, 20.0, 1.0);
break;
}
pos += chance;
}
void SoundManager::updateSounds(float duration)
{
static float timePassed = 0.0;
timePassed += duration;
if(timePassed < (1.0f/30.0f))
return;
timePassed = 0.0f;
// Make sure music is still playing
if(!isMusicPlaying())
startRandomTitle();
Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera();
Ogre::Vector3 nPos, nDir, nUp;
nPos = cam->getRealPosition();
nDir = cam->getRealDirection();
nUp = cam->getRealUp();
// The output handler is expecting vectors oriented like the game
// (that is, -Z goes down, +Y goes forward), but that's not what we
// get from Ogre's camera, so we have to convert.
float pos[3] = { nPos[0], -nPos[2], nPos[1] };
float at[3] = { nDir[0], -nDir[2], nDir[1] };
float up[3] = { nUp[0], -nUp[2], nUp[1] };
mOutput->updateListener(pos, at, up);
// Check if any sounds are finished playing, and trash them
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
{
if(!iditer->second->isPlaying())
snditer->second.erase(iditer++);
else
iditer++;
}
if(snditer->second.empty())
mActiveSounds.erase(snditer++);
else
snditer++;
}
IDMap::iterator iditer = mLooseSounds.begin();
while(iditer != mLooseSounds.end())
{
if(!iditer->second->isPlaying())
mLooseSounds.erase(iditer++);
else
iditer++;
}
}
void SoundManager::update(float duration)
{
updateSounds(duration);
updateRegionSound(duration);
}
// Default readAll implementation, for decoders that can't do anything
// better
void Sound_Decoder::readAll(std::vector<char> &output)
{
size_t total = output.size();
size_t got;
output.resize(total+32768);
while((got=read(&output[total], output.size()-total)) > 0)
{
total += got;
output.resize(total*2);
}
else if(current->cell->data.flags & current->cell->Interior)
output.resize(total);
}
const char *getSampleTypeName(SampleType type)
{
switch(type)
{
regionName = "";
case SampleType_UInt8: return "U8";
case SampleType_Int16: return "S16";
}
return "(unknown sample type)";
}
}
const char *getChannelConfigName(ChannelConfig config)
{
switch(config)
{
case ChannelConfig_Mono: return "Mono";
case ChannelConfig_Stereo: return "Stereo";
}
return "(unknown channel config)";
}
size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type)
{
switch(config)
{
case ChannelConfig_Mono: frames *= 1; break;
case ChannelConfig_Stereo: frames *= 2; break;
}
switch(type)
{
case SampleType_UInt8: frames *= 1; break;
case SampleType_Int16: frames *= 2; break;
}
return frames;
}
size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type)
{
return bytes / framesToBytes(1, config, type);
}
}

@ -3,10 +3,7 @@
#include <string>
#include <mangle/sound/clients/ogre_output_updater.hpp>
#include <mangle/sound/clients/ogre_listener_mover.hpp>
#include <openengine/sound/sndmanager.hpp>
#include <OgreResourceGroupManager.h>
#include <components/files/filelibrary.hpp>
@ -19,16 +16,6 @@ namespace Ogre
class Camera;
}
namespace Mangle
{
namespace Sound
{
typedef boost::shared_ptr<Sound> SoundPtr;
}
}
typedef OEngine::Sound::SoundManagerPtr OEManagerPtr;
namespace MWWorld
{
struct Environment;
@ -36,128 +23,96 @@ namespace MWWorld
namespace MWSound
{
class SoundManager
{
class Sound_Output;
class Sound_Decoder;
class Sound;
// This is used for case insensitive and slash-type agnostic file
// finding. It takes DOS paths (any case, \\ slashes or / slashes)
// relative to the sound dir, and translates them into full paths
// of existing files in the filesystem, if they exist.
bool mFSStrict;
typedef boost::shared_ptr<Sound_Decoder> DecoderPtr;
MWWorld::Environment& mEnvironment;
void streamMusicFull (const std::string& filename);
///< Play a soundifle
/// \param absolute filename
/* This is the sound manager. It loades, stores and deletes
sounds based on the sound factory it is given.
*/
OEManagerPtr mgr;
Mangle::Sound::SoundPtr music;
/* This class calls update() on the sound manager each frame
using and Ogre::FrameListener
*/
Mangle::Sound::OgreOutputUpdater updater;
/* This class tracks the movement of an Ogre::Camera and moves
a sound listener automatically to follow it.
*/
Mangle::Sound::OgreListenerMover cameraTracker;
typedef std::map<std::string,Mangle::Sound::WSoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> PtrMap;
PtrMap sounds;
// A list of all sound files used to lookup paths
Files::PathContainer mSoundFiles;
class SoundManager
{
Ogre::ResourceGroupManager& mResourceMgr;
// A library of all Music file paths stored by the folder they are contained in
Files::FileLibrary mMusicLibrary;
MWWorld::Environment& mEnvironment;
// Points to the current playlist of music files stored in the music library
const Files::PathContainer* mCurrentPlaylist;
std::auto_ptr<Sound_Output> mOutput;
IDMap mLoopedSounds;
boost::shared_ptr<Sound> mMusic;
std::string mCurrentPlaylist;
bool mUsingSound;
typedef boost::shared_ptr<Sound> SoundPtr;
typedef std::map<std::string,SoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> SoundMap;
SoundMap mActiveSounds;
IDMap mLooseSounds;
std::string lookup(const std::string &soundId,
float &volume, float &min, float &max);
void add(const std::string &file,
MWWorld::Ptr ptr, const std::string &id,
float volume, float pitch, float min, float max,
bool loop, bool untracked=false);
void clearAll(PtrMap::iterator& it);
void remove(MWWorld::Ptr ptr, const std::string &id = "");
bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const;
void removeCell(const MWWorld::Ptr::CellStore *cell);
void updatePositions(MWWorld::Ptr ptr);
std::string lookup(const std::string &soundId,
float &volume, float &min, float &max);
void streamMusicFull(const std::string& filename);
bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const;
void updateSounds(float duration);
void updateRegionSound(float duration);
public:
SoundManager(const SoundManager &rhs);
SoundManager& operator=(const SoundManager &rhs);
SoundManager(Ogre::Root*, Ogre::Camera*,
const Files::PathContainer& dataDir, bool useSound, bool fsstrict,
MWWorld::Environment& environment);
~SoundManager();
protected:
DecoderPtr getDecoder();
friend class OpenAL_Output;
void stopMusic();
///< Stops music if it's playing
public:
SoundManager(bool useSound, MWWorld::Environment& environment);
~SoundManager();
void streamMusic(const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void stopMusic();
///< Stops music if it's playing
void startRandomTitle();
///< Starts a random track from the current playlist
void streamMusic(const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
bool isMusicPlaying();
///< Returns true if music is playing
void startRandomTitle();
///< Starts a random track from the current playlist
bool setPlaylist(std::string playlist="");
///< Set the playlist to an existing folder
/// \param name of the folder that contains the playlist
/// if none is set then it is set to an empty playlist
/// \return Return true if the previous playlist was the same
bool isMusicPlaying();
///< Returns true if music is playing
void playPlaylist(std::string playlist="");
///< Start playing music from the selected folder
/// \param name of the folder that contains the playlist
/// if none is set then it plays from the current playlist
void playPlaylist(const std::string &playlist);
///< Start playing music from the selected folder
/// \param name of the folder that contains the playlist
void say (MWWorld::Ptr reference, const std::string& filename);
///< Make an actor say some text.
/// \param filename name of a sound file in "Sound/Vo/" in the data directory.
void say(MWWorld::Ptr reference, const std::string& filename);
///< Make an actor say some text.
/// \param filename name of a sound file in "Sound/Vo/" in the data directory.
bool sayDone (MWWorld::Ptr reference) const;
///< Is actor not speaking?
bool sayDone(MWWorld::Ptr reference) const;
///< Is actor not speaking?
void playSound (const std::string& soundId, float volume, float pitch, bool loop=false);
///< Play a sound, independently of 3D-position
void playSound(const std::string& soundId, float volume, float pitch, bool loop=false);
///< Play a sound, independently of 3D-position
void playSound3D (MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop, bool untracked=false);
///< Play a sound from an object
void playSound3D(MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop,
bool untracked=false);
///< Play a sound from an object
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId = "");
///< Stop the given object from playing the given sound, If no soundId is given,
/// all sounds for this reference will stop.
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId="");
///< Stop the given object from playing the given sound, If no soundId is given,
/// all sounds for this reference will stop.
void stopSound (MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
void stopSound(MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
void stopSound(const std::string& soundId);
///< Stop a non-3d looping sound
void stopSound(const std::string& soundId);
///< Stop a non-3d looping sound
bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const;
///< Is the given sound currently playing on the given object?
bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const;
///< Is the given sound currently playing on the given object?
void updateObject(MWWorld::Ptr reference);
///< Update the position of all sounds connected to the given object.
void updateObject(MWWorld::Ptr reference);
///< Update the position of all sounds connected to the given object.
void update (float duration);
void update(float duration);
};
}

@ -22,7 +22,7 @@
stop:0.9 rgba(0, 0, 0, 55),
stop:1 rgba(0, 0, 0, 100));
font: 24pt "Trebuchet MS";
font: 26pt "EB Garamond";
color: black;
border-right: 1px solid rgba(0, 0, 0, 155);
@ -54,7 +54,7 @@
}
#ProfileLabel {
font: 14pt "Trebuchet MS";
font: 18pt "EB Garamond";
}
#ProfilesComboBox {
@ -82,7 +82,7 @@
padding-top: 3px;
padding-left: 4px;
font: 11pt "Trebuchet MS";
font: 12pt "EB Garamond";
}
#ProfilesComboBox::drop-down {

@ -6,7 +6,6 @@ set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui)
configure_file("${SDIR}/bigbars.png" "${DDIR}/bigbars.png" COPYONLY)
configure_file("${SDIR}/black.png" "${DDIR}/black.png" COPYONLY)
configure_file("${SDIR}/Comic.TTF" "${DDIR}/Comic.TTF" COPYONLY)
configure_file("${SDIR}/core.skin" "${DDIR}/core.skin" COPYONLY)
configure_file("${SDIR}/core.xml" "${DDIR}/core.xml" COPYONLY)
configure_file("${SDIR}/mwpointer.png" "${DDIR}/mwpointer.png" COPYONLY)
@ -54,4 +53,5 @@ configure_file("${SDIR}/openmw_journal_layout.xml" "${DDIR}/openmw_journal_layou
configure_file("${SDIR}/openmw_journal_skin.xml" "${DDIR}/openmw_journal_skin.xml" COPYONLY)
configure_file("${SDIR}/smallbars.png" "${DDIR}/smallbars.png" COPYONLY)
configure_file("${SDIR}/transparent.png" "${DDIR}/transparent.png" COPYONLY)
configure_file("${SDIR}/EBGaramond-Regular.ttf" "${DDIR}/EBGaramond-Regular.ttf" COPYONLY)
configure_file("${SDIR}/VeraMono.ttf" "${DDIR}/VeraMono.ttf" COPYONLY)

Binary file not shown.

@ -2,11 +2,11 @@
<MyGUI type="Skin">
<Skin name = "TextBox" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "EB Garamond" />
<Property key="FontHeight" value = "16" />
<Property key="TextAlign" value = "ALIGN_DEFAULT" />
<Property key="TextColour" value = "0.7 0.7 0.7" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
</Skin>

@ -1,28 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI>
<MyGUI type="List">
<List file="core.skin" group="General"/>
<List file="openmw_images.xml" group="General"/>
<List file="openmw_layers.xml" group="General"/>
<List file="openmw.pointer.xml" group="General"/>
<List file="openmw.font.xml" group="General"/>
<List file="openmw_text.skin.xml" group="General"/>
<List file="openmw_windows.skin.xml" group="General"/>
<List file="openmw_button.skin.xml" group="General"/>
<List file="openmw_list.skin.xml" group="General"/>
<List file="openmw_edit.skin.xml" group="General"/>
<List file="openmw_box.skin.xml" group="General"/>
<List file="openmw_progress.skin.xml" group="General"/>
<List file="openmw_hud_energybar.skin.xml" group="General"/>
<List file="openmw_hud_box.skin.xml" group="General"/>
<List file="openmw_mainmenu_skin.xml" group="General"/>
<List file="openmw_console.skin.xml" group="General"/>
<List file="openmw_journal_skin.xml" group="General"/>
<List file="openmw_map_window_skin.xml" group="General"/>
<List file="openmw_dialogue_window_skin.xml" group="General"/>
<List file="openmw_settings.xml" group="General"/>
</MyGUI>
<MyGUI type="List">
<List file="core.skin" />
<List file="openmw_images.xml" />
<List file="openmw_layers.xml" />
<List file="openmw.pointer.xml" />
<List file="openmw.font.xml" />
<List file="openmw_text.skin.xml" />
<List file="openmw_windows.skin.xml" />
<List file="openmw_button.skin.xml" />
<List file="openmw_list.skin.xml" />
<List file="openmw_edit.skin.xml" />
<List file="openmw_box.skin.xml" />
<List file="openmw_progress.skin.xml" />
<List file="openmw_hud_energybar.skin.xml" />
<List file="openmw_hud_box.skin.xml" />
<List file="openmw_mainmenu_skin.xml" />
<List file="openmw_console.skin.xml" />
<List file="openmw_journal_skin.xml" />
<List file="openmw_map_window_skin.xml" />
<List file="openmw_dialogue_window_skin.xml" />
<List file="openmw_settings.xml" />
</MyGUI>
</MyGUI>

@ -1,95 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Font">
<Font name="MyGUI_CoreFont.18" source="Comic.TTF" size="13" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5" distance="10">
<Code range="33 126"/>
<Code range="192 382"/><!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/>
<Code range="8470 8470" help="№"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
<Font name="MonoFont" default_height="17" source="VeraMono.ttf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_width="8" cursor_width="2" distance="10" offset_height="0">
<Code range="33 126"/>
<Code range="192 382"/><!-- Central and Eastern European glyphs -->
<Code range="1025 1105"/>
</Font>
<!-- Useful for youtube videos :) -->
<Font name="youtube" default_height="34" source="VeraMono.ttf" size="36" resolution="50" antialias_colour="false" space_width="8" tab_width="16" cursor_width="4" distance="15" offset_height="0">
<Code range="33 126"/>
<Code range="192 382"/><!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/>
</Font>
<!--
<Font name="gothic" source="gothic.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 126"/>
<Code range="1025 1105"/>
<Code range="8470 8470" help="№"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
<Font name="daedric" source="daedric.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 98"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
<Font name="daedric_orig" source="daedric_orig.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 98"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
<Font name="daedric36" source="daedric.ttf" size="36" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 98"/>
</Font>
<Font name="daedric_orig36" source="daedric_orig.ttf" size="36" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 98"/>
</Font>
<Font name="cards" default_height="17" source="magiccards.ttf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_width="8" cursor_width="2" distance="5" offset_height="0">
<Code range="33 98"/>
</Font>
<Font name="cards2" default_height="17" source="magiccards2.ttf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_width="8" cursor_width="2" distance="5" offset_height="0">
<Code range="33 98"/>
</Font>
<Font name="dorcla" source="dorcla.ttf" size="20" resolution="72" antialias_colour="false" space_width="5" tab_count="4" spacer="5">
<Code range="33 126"/>
<Code range="1025 1105"/>
<Code range="8470 8470" help="№"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
<Font name="perrygot" source="perrygot.ttf" size="16" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 126"/>
<Code range="1025 1105"/>
<Code range="8470 8470" help="№"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
<Font name="albertis" source="AlbertisADF.otf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
<Code range="33 126"/>
<Code range="1025 1105"/>
<Code range="8470 8470" help="№"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Font>
-->
</MyGUI>
<MyGUI type="Resource" version="1.1">
<Resource type="ResourceTrueTypeFont" name="EB Garamond">
<Property key="Source" value="EBGaramond-Regular.ttf"/>
<Property key="Size" value="16"/>
<Property key="Resolution" value="72"/>
<Property key="Antialias" value="false"/>
<Property key="TabWidth" value="8"/>
<Property key="CursorWidth" value="2"/>
<Property key="OffsetHeight" value="0"/>
<Codes>
<Code range="33 126"/>
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/>
<Code range="8470"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Codes>
</Resource>
<Resource type="ResourceTrueTypeFont" name="MonoFont">
<Property key="Source" value="VeraMono.ttf"/>
<Property key="Size" value="18"/>
<Property key="Resolution" value="50"/>
<Property key="Antialias" value="false"/>
<Property key="TabWidth" value="8"/>
<Property key="CursorWidth" value="2"/>
<Property key="OffsetHeight" value="0"/>
<Codes>
<Code range="33 126"/>
<Code range="192 382"/>
<Code range="1025 1105"/>
<Code range="8470"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Codes>
</Resource>
</MyGUI>

@ -45,10 +45,7 @@
<!-- Button widget -->
<Skin name="MW_Button" size="136 24">
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
<!--Property key="FontName" value = "cards" /-->
<!--Property key="FontName" value = "albertis"/-->
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default"/>
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.6 0.6 0.6" />

@ -2,8 +2,6 @@
<MyGUI type="Skin">
<Skin name = "MW_ConsoleWindow" size = "256 54">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "17" />
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.8 0.8 0.8" />
<Property key="Snap" value = "true" />

@ -12,7 +12,6 @@
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="8 39 400 375" name="History" align="ALIGN_LEFT ALIGN_TOP STRETCH">
<Property key="Static" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="FontHeight" value="18"/>
<Property key="MultiLine" value="1" />
<Property key="VisibleVScroll" value="1" />
<!-- invisible box for receiving mouse events -->
@ -21,8 +20,8 @@
<!-- The disposition bar-->
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 39 132 18"
align="Right Top" name="Disposition">
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
align="Right Top" name="Disposition">
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
</Widget>
<!-- The list of topics -->
<Widget type="ListBox" skin="MW_List" position="432 62 132 318" name="TopicsList">

@ -11,8 +11,7 @@
</Skin>
<Skin name="MW_TextEdit" size="512 20" texture="mwgui.png">
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default"/>
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_VCENTER" />
<Property key="TextColour" value = "0.6 0.6 0.6" />
@ -33,8 +32,7 @@
</Skin>
<Skin name="MW_TextBoxEdit" size="512 20" texture="mwgui.png">
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default"/>
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_TOP" />
<Property key="TextColour" value = "0.6 0.6 0.6" />
@ -51,7 +49,7 @@
<State name="normal" offset = "2 20 512 2"/>
</BasisSkin>
<Child type="TextBox" skin="MW_TextBoxEditClient" offset = "2 2 490 18" align = "Stretch" name = "Client"/>
<Child type="TextBox" skin="MW_TextBoxEditClient" offset = "2 2 490 18" align = "Stretch" name = "Client"/>
<Child type="ScrollBar" skin="MW_VScroll" offset = "494 3 14 14" align = "Right VStretch" name = "VScroll"/>
</Skin>
</MyGUI>

@ -3,14 +3,13 @@
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 500 400" name="_Main">
<Widget type="EditBox" skin="MW_TextEditClient" position="10 10 490 20" align="ALIGN_LEFT ALIGN_TOP STRETCH" name="message">
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value="ALIGN_CENTER" />
<Property key="TextColour" value = "0.7 0.7 0.7" />
<Property key="Static" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="FontHeight" value="18"/>
<Property key="MultiLine" value="1" />
<Property key="VisibleVScroll" value="1" />
<Property key="TextAlign" value="ALIGN_CENTER" />
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="TextColour" value = "0.7 0.7 0.7" />
</Widget>
<Widget type="Widget" skin="" position="0 0 500 400" align="ALIGN_STRETCH" name="buttons">
<!-- Widget type="Button" skin="MW_Button" position="0 0 30 18" name="somefunnybutton"/ -->

@ -14,13 +14,13 @@
</Skin>
<Skin name = "MW_BookClient" size = "10 10">
<Property key="FontName" value = "MonoFont" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "Left Top" />
<Property key="TextColour" value = "0 0 0" />
<!--Property key="Pointer" value = "beam" /-->
<BasisSkin type="EditText" offset = "0 0 10 10" align = "Stretch"/>
</Skin>
<Skin name="MW_BookPage" size="0 0 50 50">
<Property key="WordWrap" value = "true" />
<Child type="TextBox" skin="MW_BookClient" offset="0 0 35 10" align = "ALIGN_STRETCH" name = "Client"/>

@ -167,8 +167,7 @@
</Skin>
<Skin name = "HeaderText" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default"/>
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.82 0.74 0.58" />
@ -177,10 +176,9 @@
<!-- list and multilist skins -->
<Skin name = "MW_ListLine" size = "5 5" texture="mwgui.png">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_VCENTER" />
<BasisSkin type="SimpleText" offset = "2 0 1 5" align = "ALIGN_STRETCH">
<State name="normal" colour = "0.70 0.57 0.33"/>
<State name="active" colour = "0.70 0.57 0.33"/>

@ -6,14 +6,13 @@
</Widget-->
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 0 0" name="_Main">
<Widget type="EditBox" skin="MW_TextEditClient" position="5 -5 0 0" name="message" align="ALIGN_LEFT ALIGN_TOP STRETCH">
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value="ALIGN_CENTER" />
<Property key="TextColour" value = "0.7 0.7 0.7" />
<Property key="Static" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="FontHeight" value="18"/>
<Property key="MultiLine" value="1" />
<Property key="VisibleVScroll" value="1" />
<Property key="TextAlign" value="ALIGN_CENTER" />
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="TextColour" value = "0.7 0.7 0.7" />
</Widget>
</Widget>
</MyGUI>

@ -19,11 +19,10 @@
</Skin>
<Skin name = "ProgressText" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default"/>
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.75 0.6 0.35" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
</Skin>

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI>
<MyGUI type="Font">
<Property key="Default" value="MyGUI_CoreFont.18"/>
</MyGUI>
<Property key="Default" value="EB Garamond"/>
</MyGUI>
<MyGUI type="Pointer">
<Property key="Default" value="arrow"/>
<Property key="Layer" value="Pointer"/>

@ -2,40 +2,35 @@
<MyGUI type="Skin">
<Skin name = "NormalText" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_BOTTOM" />
<Property key="TextColour" value = "0.87 0.78 0.62" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
</Skin>
<Skin name = "NumFPS" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_HCENTER ALIGN_BOTTOM" />
<Property key="TextColour" value = "1 1 1" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
</Skin>
<Skin name = "SandText" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_BOTTOM" />
<Property key="TextColour" value = "0.75 0.6 0.35" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
</Skin>
<Skin name = "SandTextC" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "TOP HCENTER" />
<Property key="TextColour" value = "0.75 0.6 0.35" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
</Skin>
<Skin name = "SandTextRight" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_RIGHT ALIGN_BOTTOM" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH">
<State name="normal" colour = "0.75 0.6 0.35"/>
@ -45,8 +40,7 @@
</Skin>
<Skin name = "SandBrightText" size = "16 16">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_BOTTOM" />
<Property key="TextColour" value = "0.85 0.76 0.60" />
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>

@ -189,14 +189,7 @@
<!-- The actual caption. It contains the edges of the blocks on
its sides as well -->
<Skin name = "MW_Caption" size = "88 20" texture = "black.png" >
<!--
<Property key="FontName" value = "dorcla" />
<Property key="FontHeight" value = "20" />
<Property key="FontName" value = "albertis" />
<Property key="FontName" value = "cards" />
-->
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.75 0.62 0.36" />
@ -221,8 +214,7 @@
------------------------------------------------------ -->
<Skin name = "MW_Window" size = "256 54">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.8 0.8 0.8" />
<Property key="Snap" value = "true" />
@ -298,8 +290,7 @@
</Skin>
<Skin name = "MW_Dialog" size = "256 54">
<Property key="FontName" value = "MyGUI_CoreFont.18" />
<Property key="FontHeight" value = "18" />
<Property key="FontName" value = "Default" />
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.8 0.8 0.8" />

@ -1,79 +0,0 @@
#ifndef MANGLE_SOUND_OGRELISTENERMOVER_H
#define MANGLE_SOUND_OGRELISTENERMOVER_H
#include <OgreCamera.h>
#include <assert.h>
#include "../output.hpp"
namespace Mangle {
namespace Sound {
/** This class lets a sound listener (ie. the SoundFactory) track a
given camera in Ogre3D. The position and orientation of the
listener will be updated to match the camera whenever the camera
is moved.
*/
struct OgreListenerMover : Ogre::Camera::Listener
{
OgreListenerMover(Mangle::Sound::SoundFactoryPtr snd)
: soundFact(snd), camera(NULL)
{}
/// Follow a camera. WARNING: This will OVERRIDE any other
/// MovableObject::Listener you may have attached to the camera.
void followCamera(Ogre::Camera *cam)
{
camera = cam;
camera->addListener(this);
}
void unfollowCamera()
{
// If the camera is null, this object wasn't following a camera.
// It doesn't make sense to call unfollow
assert(camera != NULL);
camera->removeListener(this);
camera = NULL;
}
private:
Mangle::Sound::SoundFactoryPtr soundFact;
Ogre::Camera *camera;
Ogre::Vector3 pos, dir, up;
/// From Camera::Listener. This is called once per
/// frame. Unfortunately, Ogre doesn't allow us to be notified
/// only when the camera itself has moved, so we must poll every
/// frame.
void cameraPreRenderScene(Ogre::Camera *cam)
{
assert(cam == camera);
Ogre::Vector3 nPos, nDir, nUp;
nPos = camera->getRealPosition();
nDir = camera->getRealDirection();
nUp = camera->getRealUp();
// Don't bother the sound system needlessly
if(nDir != dir || nPos != pos || nUp != up)
{
pos = nPos;
dir = nDir;
up = nUp;
soundFact->setListenerPos(pos.x, pos.y, pos.z,
dir.x, dir.y, dir.z,
up.x, up.y, up.z);
}
}
void cameraDestroyed(Ogre::Camera *cam)
{
assert(cam == camera);
camera = NULL;
}
};
}}
#endif

@ -1,31 +0,0 @@
#ifndef MANGLE_SOUND_OGREUPDATER_H
#define MANGLE_SOUND_OGREUPDATER_H
/*
This Ogre FrameListener calls update on a SoundFactory
*/
#include <OgreFrameListener.h>
#include "../output.hpp"
#include <assert.h>
namespace Mangle {
namespace Sound {
struct OgreOutputUpdater : Ogre::FrameListener
{
Mangle::Sound::SoundFactoryPtr driver;
OgreOutputUpdater(Mangle::Sound::SoundFactoryPtr drv)
: driver(drv)
{ assert(drv->needsUpdate); }
bool frameStarted(const Ogre::FrameEvent &evt)
{
driver->update();
return true;
}
};
}}
#endif

@ -1,68 +0,0 @@
#ifndef MANGLE_INPUT_FILTER_H
#define MANGLE_INPUT_FILTER_H
#include "../output.hpp"
#include <assert.h>
namespace Mangle {
namespace Sound {
/**
@brief This filter class adds file loading capabilities to a
Sound::SoundFactory class, by associating a SampleSourceLoader with
it.
The class takes an existing SoundFactory able to load streams, and
associates a SampleSourceLoader with it. The combined class is able
to load files directly. */
class InputFilter : public SoundFactory
{
protected:
SoundFactoryPtr snd;
SampleSourceLoaderPtr inp;
public:
/// Empty constructor
InputFilter() {}
/// Assign an input manager and a sound manager to this object
InputFilter(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
{ set(_snd, _inp); }
/// Assign an input manager and a sound manager to this object
void set(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
{
inp = _inp;
snd = _snd;
// Set capabilities
needsUpdate = snd->needsUpdate;
has3D = snd->has3D;
canLoadStream = inp->canLoadStream;
// Both these should be true, or the use of this class is pretty
// pointless
canLoadSource = snd->canLoadSource;
canLoadFile = inp->canLoadFile;
assert(canLoadSource && canLoadFile);
}
virtual SoundPtr load(const std::string &file)
{ return loadRaw(inp->load(file)); }
virtual SoundPtr load(Stream::StreamPtr input)
{ return loadRaw(inp->load(input)); }
virtual SoundPtr loadRaw(SampleSourcePtr input)
{ return snd->loadRaw(input); }
virtual void update() { snd->update(); }
virtual void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz)
{ snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); }
};
}}
#endif

@ -1,24 +0,0 @@
#ifndef MANGLE_AUDIERE_OPENAL_H
#define MANGLE_AUDIERE_OPENAL_H
#include "input_filter.hpp"
#include "../sources/audiere_source.hpp"
#include "../outputs/openal_out.hpp"
namespace Mangle {
namespace Sound {
/// A InputFilter that adds audiere decoding to OpenAL. Audiere has
/// it's own output, but OpenAL sports 3D and other advanced features.
class OpenAL_Audiere_Factory : public InputFilter
{
public:
OpenAL_Audiere_Factory()
{
set(SoundFactoryPtr(new OpenAL_Factory),
SampleSourceLoaderPtr(new AudiereLoader));
}
};
}}
#endif

@ -1,23 +0,0 @@
#ifndef MANGLE_FFMPEG_OPENAL_H
#define MANGLE_FFMPEG_OPENAL_H
#include "input_filter.hpp"
#include "../sources/ffmpeg_source.hpp"
#include "../outputs/openal_out.hpp"
namespace Mangle {
namespace Sound {
/// A InputFilter that adds ffmpeg decoding to OpenAL.
class OpenAL_FFMpeg_Factory : public InputFilter
{
public:
OpenAL_FFMpeg_Factory()
{
set(SoundFactoryPtr(new OpenAL_Factory),
SampleSourceLoaderPtr(new FFMpegLoader));
}
};
}}
#endif

@ -1,24 +0,0 @@
#ifndef MANGLE_MPG123_OPENAL_H
#define MANGLE_MPG123_OPENAL_H
#include "input_filter.hpp"
#include "../sources/mpg123_source.hpp"
#include "../outputs/openal_out.hpp"
namespace Mangle {
namespace Sound {
/// A InputFilter that adds mpg123 decoding to OpenAL. Only supports
/// MP3 files.
class OpenAL_Mpg123_Factory : public InputFilter
{
public:
OpenAL_Mpg123_Factory()
{
set(SoundFactoryPtr(new OpenAL_Factory),
SampleSourceLoaderPtr(new Mpg123Loader));
}
};
}}
#endif

@ -1,24 +0,0 @@
#ifndef MANGLE_SNDFILE_OPENAL_H
#define MANGLE_SNDFILE_OPENAL_H
#include "input_filter.hpp"
#include "../sources/libsndfile.hpp"
#include "../outputs/openal_out.hpp"
namespace Mangle {
namespace Sound {
/// A InputFilter that adds libsnd decoding to OpenAL. libsndfile
/// supports most formats except MP3.
class OpenAL_SndFile_Factory : public InputFilter
{
public:
OpenAL_SndFile_Factory()
{
set(SoundFactoryPtr(new OpenAL_Factory),
SampleSourceLoaderPtr(new SndFileLoader));
}
};
}}
#endif

@ -1,33 +0,0 @@
#ifndef MANGLE_SNDFILE_MPG123_OPENAL_H
#define MANGLE_SNDFILE_MPG123_OPENAL_H
#include "input_filter.hpp"
#include "source_splicer.hpp"
#include "../sources/mpg123_source.hpp"
#include "../sources/libsndfile.hpp"
#include "../outputs/openal_out.hpp"
namespace Mangle {
namespace Sound {
/// A InputFilter that uses OpenAL for output, and mpg123 (for MP3) +
/// libsndfile (for everything else) to decode files. Can only load
/// from the file system, and uses the file name to differentiate
/// between mp3 and non-mp3 types.
class OpenAL_SndFile_Mpg123_Factory : public InputFilter
{
public:
OpenAL_SndFile_Mpg123_Factory()
{
SourceSplicer *splice = new SourceSplicer;
splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader));
splice->setDefault(SampleSourceLoaderPtr(new SndFileLoader));
set(SoundFactoryPtr(new OpenAL_Factory),
SampleSourceLoaderPtr(splice));
}
};
}}
#endif

@ -1,39 +0,0 @@
#ifndef MANGLE_VARIOUS_OPENAL_H
#define MANGLE_VARIOUS_OPENAL_H
#include "input_filter.hpp"
#include "source_splicer.hpp"
#include "../sources/mpg123_source.hpp"
#include "../sources/wav_source.hpp"
#include "../outputs/openal_out.hpp"
namespace Mangle {
namespace Sound {
/** A InputFilter that uses OpenAL for output, and load input from
various individual sources, depending on file extension. Currently
supports:
MP3: mpg123
WAV: custom wav loader (PCM only)
This could be an alternative to using eg. 3rd party decoder
libraries like libsndfile.
*/
class OpenAL_Various_Factory : public InputFilter
{
public:
OpenAL_Various_Factory()
{
SourceSplicer *splice = new SourceSplicer;
splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader));
splice->add("wav", SampleSourceLoaderPtr(new WavLoader));
set(SoundFactoryPtr(new OpenAL_Factory),
SampleSourceLoaderPtr(splice));
}
};
}}
#endif

@ -1,73 +0,0 @@
#ifndef MANGLE_SOUND_OUTPUT_PUREFILTER_H
#define MANGLE_SOUND_OUTPUT_PUREFILTER_H
#include "../output.hpp"
namespace Mangle
{
namespace Sound
{
// For use in writing other filters
class SoundFilter : public Sound
{
protected:
SoundPtr client;
public:
SoundFilter(SoundPtr c) : client(c) {}
void play() { client->play(); }
void stop() { client->stop(); }
void pause() { client->pause(); }
bool isPlaying() const { return client->isPlaying(); }
void setVolume(float f) { client->setVolume(f); }
void setPan(float f) { client->setPan(f); }
void setPos(float x, float y, float z)
{ client->setPos(x,y,z); }
void setPitch(float p) { client->setPitch(p); }
void setRepeat(bool b) { client->setRepeat(b); }
void setRange(float a, float b=0, float c=0)
{ client->setRange(a,b,c); }
void setStreaming(bool b) { client->setStreaming(b); }
void setRelative(bool b) { client->setRelative(b); }
// The clone() function is not implemented here, as you will
// almost certainly want to override it yourself
};
class FactoryFilter : public SoundFactory
{
protected:
SoundFactoryPtr client;
public:
FactoryFilter(SoundFactoryPtr c) : client(c)
{
needsUpdate = client->needsUpdate;
has3D = client->has3D;
canLoadFile = client->canLoadFile;
canLoadStream = client->canLoadStream;
canLoadSource = client->canLoadSource;
}
SoundPtr loadRaw(SampleSourcePtr input)
{ return client->loadRaw(input); }
SoundPtr load(Stream::StreamPtr input)
{ return client->load(input); }
SoundPtr load(const std::string &file)
{ return client->load(file); }
void update()
{ client->update(); }
void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz)
{
client->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz);
}
};
}
}
#endif

@ -1,90 +0,0 @@
#ifndef MANGLE_SOUND_SOURCE_SPLICE_H
#define MANGLE_SOUND_SOURCE_SPLICE_H
#include "../source.hpp"
#include <stdexcept>
#include <string>
#include <list>
#include <assert.h>
namespace Mangle
{
namespace Sound
{
class SourceSplicer : public SampleSourceLoader
{
struct SourceType
{
std::string type;
SampleSourceLoaderPtr loader;
};
typedef std::list<SourceType> TypeList;
TypeList list;
SampleSourceLoaderPtr catchAll;
static bool isMatch(char a, char b)
{
if(a >= 'A' && a <= 'Z')
a += 'a' - 'A';
if(b >= 'A' && b <= 'Z')
b += 'a' - 'A';
return a == b;
}
public:
SourceSplicer()
{
canLoadStream = false;
canLoadFile = true;
}
void add(const std::string &type, SampleSourceLoaderPtr fact)
{
SourceType tp;
tp.type = type;
tp.loader = fact;
list.push_back(tp);
}
void setDefault(SampleSourceLoaderPtr def)
{
catchAll = def;
}
SampleSourcePtr load(const std::string &file)
{
// Search the list for this file type.
for(TypeList::iterator it = list.begin();
it != list.end(); it++)
{
const std::string &t = it->type;
int diff = file.size() - t.size();
if(diff < 0) continue;
bool match = true;
for(unsigned i=0; i<t.size(); i++)
if(!isMatch(t[i], file[i+diff]))
{
match = false;
break;
}
// Got something! We're done.
if(match)
return it->loader->load(file);
}
// If not found, use the catch-all
if(catchAll)
return catchAll->load(file);
throw std::runtime_error("No handler for sound file " + file);
}
SampleSourcePtr load(Stream::StreamPtr input) { assert(0); }
};
}
}
#endif

@ -1,183 +0,0 @@
#ifndef MANGLE_SOUND_OUTPUT_H
#define MANGLE_SOUND_OUTPUT_H
#include <string>
#include <assert.h>
#include "source.hpp"
#include "../stream/stream.hpp"
namespace Mangle {
namespace Sound {
/// Abstract interface for a single playable sound
/** This class represents one sound outlet, which may be played,
stopped, paused and so on.
Sound instances are created from the SoundFactory class. Sounds
may be connected to a SampleSource or read directly from a file,
and they may support 3d sounds, looping and other features
depending on the capabilities of the backend system.
To create multiple instances of one sound, it is recommended to
'clone' an existing instance instead of reloading it from
file. Cloned sounds will often (depending on the back-end) use
less memory due to shared buffers.
*/
class Sound;
typedef boost::shared_ptr<Sound> SoundPtr;
typedef boost::weak_ptr <Sound> WSoundPtr;
class Sound
{
public:
/// Play or resume the sound
virtual void play() = 0;
/// Stop the sound
virtual void stop() = 0;
/// Pause the sound, may be resumed later
virtual void pause() = 0;
/// Check if the sound is still playing
virtual bool isPlaying() const = 0;
/// Set the volume. The parameter must be between 0.0 and 1.0.
virtual void setVolume(float) = 0;
/// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right.
virtual void setPan(float) = 0;
/// Set pitch (1.0 is normal speed)
virtual void setPitch(float) = 0;
/// Set range factors for 3D sounds. The meaning of the fields
/// depend on implementation.
virtual void setRange(float a, float b=0.0, float c=0.0) = 0;
/// Set the position. May not work with all backends.
virtual void setPos(float x, float y, float z) = 0;
/// Set loop mode
virtual void setRepeat(bool) = 0;
/// If set to true the sound will not be affected by player movement
virtual void setRelative(bool) = 0;
/// Set streaming mode.
/** This may be used by implementations to optimize for very large
files. If streaming mode is off (default), most implementations
will load the entire file into memory before starting playback.
*/
virtual void setStreaming(bool) = 0;
/// Create a new instance of this sound.
/** Playback status is not cloned, only the sound data
itself. Back-ends can use this as a means of sharing data and
saving memory. */
virtual SoundPtr clone() = 0;
/// Virtual destructor
virtual ~Sound() {}
};
/// Factory interface for creating Sound objects
/** The SoundFactory is the main entry point to a given sound output
system. It is used to create Sound objects, which may be connected
to a sound file or stream, and which may be individually played,
paused, and so on.
The class also contains a set of public bools which describe the
capabilities the particular system. These should be set by
implementations (base classes) in their respective constructors.
*/
class SoundFactory
{
public:
/// Virtual destructor
virtual ~SoundFactory() {}
/** @brief If set to true, you should call update() regularly (every frame
or so) on this sound manager. If false, update() should not be
called.
*/
bool needsUpdate;
/** @brief true if 3D functions are available. If false, all use of
3D sounds and calls to setPos / setListenerPos will result in
undefined behavior.
*/
bool has3D;
/// true if we can load sounds directly from file (containing encoded data)
bool canLoadFile;
/// If true, we can lound sound files from a Stream (containing encoded data)
bool canLoadStream;
/// true if we can load sounds from a SampleSource (containing raw data)
bool canLoadSource;
/**
@brief Load a sound from a sample source. Only valid if
canLoadSource is true.
This function loads a sound from a given stream as defined by
SampleSource.
@param input the input source
@param stream true if the file should be streamed.
Implementations may use this for optimizing playback of
large files, but they are not required to.
@return a new Sound object
*/
virtual SoundPtr loadRaw(SampleSourcePtr input) = 0;
/**
@brief Load a sound file from stream. Only valid if canLoadStream
is true.
@param input audio file stream
@param stream true if the file should be streamed
@see load(InputSource*,bool)
*/
virtual SoundPtr load(Stream::StreamPtr input) = 0;
/**
@brief Load a sound directly from file. Only valid if canLoadFile
is true.
@param file filename
@param stream true if the file should be streamed
@see load(InputSource*,bool)
*/
virtual SoundPtr load(const std::string &file) = 0;
/// Call this every frame if needsUpdate is true
/**
This should be called regularly (about every frame in a normal
game setting.) Implementions may use this for filling streaming
buffers and similar tasks. Implementations that do not need this
should set needsUpdate to false.
*/
virtual void update() { assert(0); }
/// Set listener position (coordinates, front and up vectors)
/**
Only valid if has3D is true.
@param x,y,z listener position
@param fx,fy,fz listener's looking direction
@param ux,uy,uz listener's up direction
*/
virtual void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz) = 0;
};
typedef boost::shared_ptr<SoundFactory> SoundFactoryPtr;
}} // Namespaces
#endif

@ -1,500 +0,0 @@
#include "openal_out.hpp"
#include <assert.h>
#include <stdexcept>
#include "../../stream/filters/buffer_stream.hpp"
#ifdef _WIN32
#include <al.h>
#include <alc.h>
#elif defined(__APPLE__)
#include <OpenAL/alc.h>
#include <OpenAL/al.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
using namespace Mangle::Sound;
// ---- Helper functions and classes ----
// Static buffer used to shuffle sound data from the input into
// OpenAL. The data is only stored temporarily and then immediately
// shuffled off to the library. This is not thread safe, but it works
// fine with multiple sounds in one thread. It could be made thread
// safe simply by using thread local storage.
const size_t BSIZE = 32*1024;
static char tmp_buffer[BSIZE];
// Number of buffers used (per sound) for streaming sounds. Each
// buffer is of size BSIZE. Increasing this will make streaming sounds
// more fault tolerant against temporary lapses in call to update(),
// but will also increase memory usage.
// This was changed from 4 to 150 for an estimated 30 seconds tolerance.
// At some point we should replace it with a more multithreading-ish
// solution.
const int STREAM_BUF_NUM = 150;
static void fail(const std::string &msg)
{ throw std::runtime_error("OpenAL exception: " + msg); }
/*
Check for AL error. Since we're always calling this with string
literals, and it only makes sense to optimize for the non-error
case, the parameter is const char* rather than std::string.
This way we don't force the compiler to create a string object each
time we're called (since the string is never used unless there's an
error), although a good compiler might have optimized that away in
any case.
*/
static void checkALError(const char *where)
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
{
std::string msg = where;
const ALchar* errmsg = alGetString(err);
if(errmsg)
fail("\"" + std::string(alGetString(err)) + "\" while " + msg);
else
fail("non-specified error while " + msg + " (did you forget to initialize OpenAL?)");
}
}
static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
{
boost::int32_t rate_, ch, bits;
inp->getInfo(&rate_, &ch, &bits);
rate = rate_;
fmt = 0;
if(bits == 8)
{
if(ch == 1) fmt = AL_FORMAT_MONO8;
if(ch == 2) fmt = AL_FORMAT_STEREO8;
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8");
if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8");
}
}
if(bits == 16)
{
if(ch == 1) fmt = AL_FORMAT_MONO16;
if(ch == 2) fmt = AL_FORMAT_STEREO16;
if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16");
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16");
if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16");
}
}
if(fmt == 0)
fail("Unsupported input format");
}
/// OpenAL sound output
class Mangle::Sound::OpenAL_Sound : public Sound
{
ALuint inst;
// Buffers. Only the first is used for non-streaming sounds.
ALuint bufferID[STREAM_BUF_NUM];
// Number of buffers used
int bufNum;
// Parameters used for filling buffers
int fmt, rate;
// Poor mans reference counting. Might improve this later. When
// NULL, the buffer has not been set up yet.
int *refCnt;
bool streaming;
// Input stream
SampleSourcePtr input;
OpenAL_Factory *owner;
bool ownerAlive;
// Used for streamed sound list
OpenAL_Sound *next, *prev;
void setupBuffer();
// Fill data into the given buffer and queue it, if there is any
// data left to queue. Assumes the buffer is already unqueued, if
// necessary.
void queueBuffer(ALuint buf)
{
// If there is no more data, do nothing
if(!input) return;
if(input->eof())
{
input.reset();
return;
}
// Get some new data
size_t bytes = input->read(tmp_buffer, BSIZE);
if(bytes == 0)
{
input.reset();
return;
}
// Move data into the OpenAL buffer
alBufferData(buf, fmt, tmp_buffer, bytes, rate);
// Queue it
alSourceQueueBuffers(inst, 1, &buf);
checkALError("Queueing buffer data");
}
public:
/// Read samples from the given input buffer
OpenAL_Sound(SampleSourcePtr input, OpenAL_Factory *fact);
/// Play an existing buffer, with a given ref counter. Used
/// internally for cloning.
OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact);
~OpenAL_Sound();
// Must be called regularly on streamed sounds
void update()
{
if(!streaming) return;
if(!input) return;
// Get the number of processed buffers
ALint count;
alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count);
checkALError("getting number of unprocessed buffers");
for(int i=0; i<count; i++)
{
ALuint buf;
// Unqueue one of the processed buffer
alSourceUnqueueBuffers(inst, 1, &buf);
// Then reload it with data (if any) and queue it up
queueBuffer(buf);
}
}
void play();
void stop();
void pause();
bool isPlaying() const;
void setVolume(float);
void setPos(float x, float y, float z);
void setPitch(float);
void setRepeat(bool);
void setRelative(bool);
void notifyOwnerDeath()
{ ownerAlive = false; }
// We can enable streaming, but never disable it.
void setStreaming(bool s)
{ if(s) streaming = true; }
SoundPtr clone();
// a = AL_REFERENCE_DISTANCE
// b = AL_MAX_DISTANCE
// c = ignored
void setRange(float a, float b=0.0, float c=0.0);
/// Not implemented
void setPan(float) {}
};
// ---- OpenAL_Factory ----
SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
{
return SoundPtr(new OpenAL_Sound(input, this));
}
void OpenAL_Factory::setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz)
{
ALfloat orient[6];
orient[0] = fx;
orient[1] = fy;
orient[2] = fz;
orient[3] = ux;
orient[4] = uy;
orient[5] = uz;
alListener3f(AL_POSITION, x, y, z);
alListenerfv(AL_ORIENTATION, orient);
}
OpenAL_Factory::OpenAL_Factory(bool doSetup)
: device(NULL), context(NULL), didSetup(doSetup)
{
needsUpdate = true;
has3D = true;
canLoadFile = false;
canLoadStream = false;
canLoadSource = true;
ALCdevice *Device;
ALCcontext *Context;
if(doSetup)
{
// Set up sound system
Device = alcOpenDevice(NULL);
Context = alcCreateContext(Device, NULL);
if(!Device || !Context)
fail("Failed to initialize context or device");
alcMakeContextCurrent(Context);
device = Device;
context = Context;
alDistanceModel(AL_LINEAR_DISTANCE);
}
}
void OpenAL_Factory::update()
{
// Loop through all streaming sounds and update them
StreamList::iterator it = streaming.begin();
for(;it != streaming.end(); it++)
(*it)->update();
}
void OpenAL_Factory::notifyStreaming(OpenAL_Sound *snd)
{
// Add the sound to the streaming list
streaming.push_back(snd);
}
void OpenAL_Factory::notifyDelete(OpenAL_Sound *snd)
{
// Remove the sound from the stream list
streaming.remove(snd);
}
OpenAL_Factory::~OpenAL_Factory()
{
// Notify remaining streamed sounds that we're dying
StreamList::iterator it = streaming.begin();
for(;it != streaming.end(); it++)
(*it)->notifyOwnerDeath();
// Deinitialize sound system
if(didSetup)
{
alcMakeContextCurrent(NULL);
if(context) alcDestroyContext((ALCcontext*)context);
if(device) alcCloseDevice((ALCdevice*)device);
}
}
// ---- OpenAL_Sound ----
void OpenAL_Sound::play()
{
setupBuffer();
alSourcePlay(inst);
checkALError("starting playback");
}
void OpenAL_Sound::stop()
{
alSourceStop(inst);
checkALError("stopping");
}
void OpenAL_Sound::pause()
{
alSourcePause(inst);
checkALError("pausing");
}
bool OpenAL_Sound::isPlaying() const
{
ALint state;
alGetSourcei(inst, AL_SOURCE_STATE, &state);
return state == AL_PLAYING;
}
void OpenAL_Sound::setVolume(float volume)
{
if(volume > 1.0) volume = 1.0;
if(volume < 0.0) volume = 0.0;
alSourcef(inst, AL_GAIN, volume);
checkALError("setting volume");
}
void OpenAL_Sound::setRange(float a, float b, float)
{
alSourcef(inst, AL_REFERENCE_DISTANCE, a);
alSourcef(inst, AL_MAX_DISTANCE, b);
checkALError("setting sound ranges");
}
void OpenAL_Sound::setPos(float x, float y, float z)
{
alSource3f(inst, AL_POSITION, x, y, z);
checkALError("setting position");
}
void OpenAL_Sound::setPitch(float pitch)
{
alSourcef(inst, AL_PITCH, pitch);
checkALError("setting pitch");
}
void OpenAL_Sound::setRepeat(bool rep)
{
alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE);
}
void OpenAL_Sound::setRelative(bool rel)
{
alSourcei(inst, AL_SOURCE_RELATIVE, rel?AL_TRUE:AL_FALSE);
checkALError("setting relative");
}
SoundPtr OpenAL_Sound::clone()
{
setupBuffer();
assert(!streaming && "cloning streamed sounds not supported");
return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner));
}
// Constructor used for cloned sounds
OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact)
: refCnt(ref), streaming(false), owner(fact), ownerAlive(false)
{
// Increase the reference count
assert(ref != NULL);
(*refCnt)++;
// Set up buffer
bufferID[0] = buf;
bufNum = 1;
// Create a source
alGenSources(1, &inst);
checkALError("creating instance (clone)");
alSourcei(inst, AL_BUFFER, bufferID[0]);
checkALError("assigning buffer (clone)");
}
// Constructor used for original (non-cloned) sounds
OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input, OpenAL_Factory *fact)
: refCnt(NULL), streaming(false), input(_input), owner(fact), ownerAlive(false)
{
// Create a source
alGenSources(1, &inst);
checkALError("creating source");
// By default, the sound starts out in a buffer-less mode. We don't
// create a buffer until the sound is played. This gives the user
// the chance to call setStreaming(true) first.
}
void OpenAL_Sound::setupBuffer()
{
if(refCnt != NULL) return;
assert(input);
// Get the format
getALFormat(input, fmt, rate);
// Create a cheap reference counter for the buffer
refCnt = new int;
*refCnt = 1;
if(streaming) bufNum = STREAM_BUF_NUM;
else bufNum = 1;
// Set up the OpenAL buffer(s)
alGenBuffers(bufNum, bufferID);
checkALError("generating buffer(s)");
assert(bufferID[0] != 0);
// STREAMING.
if(streaming)
{
// Just queue all the buffers with data and exit. queueBuffer()
// will work correctly also in the case where there is not
// enough data to fill all the buffers.
for(int i=0; i<bufNum; i++)
queueBuffer(bufferID[i]);
// Notify the manager what we're doing
owner->notifyStreaming(this);
ownerAlive = true;
return;
}
// NON-STREAMING. We have to load all the data and shove it into the
// buffer.
// Does the stream support pointer operations?
if(input->hasPtr)
{
// If so, we can read the data directly from the stream
alBufferData(bufferID[0], fmt, input->getPtr(), input->size(), rate);
}
else
{
// Read the entire stream into a temporary buffer first
Mangle::Stream::BufferStream buf(input, 128*1024);
// Then copy that into OpenAL
alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate);
}
checkALError("loading sound data");
// We're done with the input stream, release the pointer
input.reset();
alSourcei(inst, AL_BUFFER, bufferID[0]);
checkALError("assigning buffer");
}
OpenAL_Sound::~OpenAL_Sound()
{
// Stop
alSourceStop(inst);
// Return sound
alDeleteSources(1, &inst);
// Notify the factory that we quit. You will hear from our union
// rep. The bool check is to handle cases where the manager goes out
// of scope before the sounds do. In that case, don't try to contact
// the factory.
if(ownerAlive)
owner->notifyDelete(this);
// Decrease the reference counter
if((-- (*refCnt)) == 0)
{
// We're the last owner. Delete the buffer(s) and the counter
// itself.
alDeleteBuffers(bufNum, bufferID);
checkALError("deleting buffer");
delete refCnt;
}
}

@ -1,44 +0,0 @@
#ifndef MANGLE_SOUND_OPENAL_OUT_H
#define MANGLE_SOUND_OPENAL_OUT_H
#include "../output.hpp"
#include <list>
namespace Mangle {
namespace Sound {
class OpenAL_Sound;
class OpenAL_Factory : public SoundFactory
{
void *device;
void *context;
bool didSetup;
// List of streaming sounds that need to be updated every frame.
typedef std::list<OpenAL_Sound*> StreamList;
StreamList streaming;
friend class OpenAL_Sound;
void notifyStreaming(OpenAL_Sound*);
void notifyDelete(OpenAL_Sound*);
public:
/// Initialize object. Pass true (default) if you want the
/// constructor to set up the current ALCdevice and ALCcontext for
/// you.
OpenAL_Factory(bool doSetup = true);
~OpenAL_Factory();
SoundPtr load(const std::string &file) { assert(0); return SoundPtr(); }
SoundPtr load(Stream::StreamPtr input) { assert(0); return SoundPtr(); }
SoundPtr loadRaw(SampleSourcePtr input);
void update();
void setListenerPos(float x, float y, float z,
float fx, float fy, float fz,
float ux, float uy, float uz);
};
}} // namespaces
#endif

@ -1,62 +0,0 @@
#ifndef MANGLE_SOUND_SOURCE_H
#define MANGLE_SOUND_SOURCE_H
#include <string>
#include <boost/cstdint.hpp>
#include <assert.h>
#include "../stream/stream.hpp"
namespace Mangle {
namespace Sound {
typedef boost::int32_t int32_t;
/// A stream containing raw sound data and information about the format
class SampleSource : public Stream::Stream
{
protected:
bool isEof;
public:
SampleSource() : isEof(false) {}
/// Get the sample rate, number of channels, and bits per
/// sample. NULL parameters are ignored.
virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0;
bool eof() const { return isEof; }
// Disabled functions by default. You can still override them in
// subclasses.
void seek(size_t pos) { assert(0); }
size_t tell() const { assert(0); return 0; }
size_t size() const { assert(0); return 0; }
};
typedef boost::shared_ptr<SampleSource> SampleSourcePtr;
/// A factory interface for loading SampleSources from file or stream
class SampleSourceLoader
{
public:
/// If true, the stream version of load() works
bool canLoadStream;
/// If true, the file version of load() works
bool canLoadFile;
/// Load a sound input source from file (if canLoadFile is true)
virtual SampleSourcePtr load(const std::string &file) = 0;
/// Load a sound input source from stream (if canLoadStream is true)
virtual SampleSourcePtr load(Stream::StreamPtr input) = 0;
/// Virtual destructor
virtual ~SampleSourceLoader() {}
};
typedef boost::shared_ptr<SampleSourceLoader> SampleSourceLoaderPtr;
}} // namespaces
#endif

@ -1,77 +0,0 @@
#include "audiere_source.hpp"
#include "../../stream/clients/audiere_file.hpp"
#include <stdexcept>
using namespace Mangle::Stream;
static void fail(const std::string &msg)
{ throw std::runtime_error("Audiere exception: " + msg); }
using namespace audiere;
using namespace Mangle::Sound;
// --- SampleSource ---
void AudiereSource::getInfo(Mangle::Sound::int32_t *rate,
Mangle::Sound::int32_t *channels, Mangle::Sound::int32_t *bits)
{
SampleFormat fmt;
int channels_, rate_;
sample->getFormat(channels_, rate_, fmt);
*channels = channels_;
*rate = rate_;
if(bits)
{
if(fmt == SF_U8)
*bits = 8;
else if(fmt == SF_S16)
*bits = 16;
else assert(0);
}
}
// --- Constructors ---
AudiereSource::AudiereSource(const std::string &file)
{
sample = OpenSampleSource(file.c_str());
if(!sample)
fail("Couldn't load file " + file);
doSetup();
}
AudiereSource::AudiereSource(StreamPtr input)
{
// Use our Stream::AudiereFile implementation to convert a Mangle
// 'Stream' to an Audiere 'File'
sample = OpenSampleSource(new AudiereFile(input));
if(!sample)
fail("Couldn't load stream");
doSetup();
}
AudiereSource::AudiereSource(audiere::SampleSourcePtr src)
: sample(src)
{ assert(sample); doSetup(); }
// Common function called from all constructors
void AudiereSource::doSetup()
{
assert(sample);
SampleFormat fmt;
int channels, rate;
sample->getFormat(channels, rate, fmt);
// Calculate the size of one frame, and pass it to SampleReader.
setup(GetSampleSize(fmt) * channels);
isSeekable = sample->isSeekable();
hasPosition = true;
hasSize = true;
}

@ -1,48 +0,0 @@
#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H
#define MANGLE_SOUND_AUDIERE_SOURCE_H
#include "sample_reader.hpp"
// audiere.h from 1.9.4 (latest) release uses
// cstring routines like strchr() and strlen() without
// including cstring itself.
#include <cstring>
#include <audiere.h>
namespace Mangle {
namespace Sound {
/// A sample source that decodes files using Audiere
class AudiereSource : public SampleReader
{
audiere::SampleSourcePtr sample;
size_t readSamples(void *data, size_t length)
{ return sample->read(length, data); }
void doSetup();
public:
/// Decode the given sound file
AudiereSource(const std::string &file);
/// Decode the given sound stream
AudiereSource(Mangle::Stream::StreamPtr src);
/// Read directly from an existing audiere::SampleSource
AudiereSource(audiere::SampleSourcePtr src);
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
void seek(size_t pos) { sample->setPosition(pos/frameSize); }
size_t tell() const { return sample->getPosition()*frameSize; }
size_t size() const { return sample->getLength()*frameSize; }
};
#include "loadertemplate.hpp"
/// A factory that loads AudiereSources from file and stream
typedef SSL_Template<AudiereSource,true,true> AudiereLoader;
}} // Namespace
#endif

@ -1,189 +0,0 @@
#include "ffmpeg_source.hpp"
#include <stdexcept>
using namespace Mangle::Sound;
// Static output buffer. Not thread safe, but supports multiple
// streams operated from the same thread.
static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE];
static void fail(const std::string &msg)
{ throw std::runtime_error("FFMpeg exception: " + msg); }
// --- Loader ---
static bool init = false;
FFMpegLoader::FFMpegLoader(bool setup)
{
if(setup && !init)
{
av_register_all();
av_log_set_level(AV_LOG_ERROR);
init = true;
}
}
// --- Source ---
FFMpegSource::FFMpegSource(const std::string &file)
{
std::string msg;
AVCodec *codec;
if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0)
fail("Error loading audio file " + file);
if(av_find_stream_info(FmtCtx) < 0)
{
msg = "Error in file stream " + file;
goto err;
}
// Pick the first audio stream, if any
for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++)
{
// Pick the first audio stream
if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO)
break;
}
if(StreamNum == FmtCtx->nb_streams)
fail("File '" + file + "' didn't contain any audio streams");
// Open the decoder
CodecCtx = FmtCtx->streams[StreamNum]->codec;
codec = avcodec_find_decoder(CodecCtx->codec_id);
if(!codec || avcodec_open(CodecCtx, codec) < 0)
{
msg = "Error loading '" + file + "': ";
if(codec)
msg += "coded error";
else
msg += "no codec found";
goto err;
}
// No errors, we're done
return;
// Handle errors
err:
av_close_input_file(FmtCtx);
fail(msg);
}
FFMpegSource::~FFMpegSource()
{
avcodec_close(CodecCtx);
av_close_input_file(FmtCtx);
}
void FFMpegSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits)
{
if(rate) *rate = CodecCtx->sample_rate;
if(channels) *channels = CodecCtx->channels;
if(bits) *bits = 16;
}
size_t FFMpegSource::read(void *data, size_t length)
{
if(isEof) return 0;
size_t left = length;
uint8_t *outPtr = (uint8_t*)data;
// First, copy over any stored data we might be sitting on
{
size_t s = storage.size();
size_t copy = s;
if(s)
{
// Make sure there's room
if(copy > left)
copy = left;
// Copy
memcpy(outPtr, &storage[0], copy);
outPtr += copy;
left -= copy;
// Is there anything left in the storage?
assert(s>= copy);
s -= copy;
if(s)
{
assert(left == 0);
// Move it to the start and resize
memmove(&storage[0], &storage[copy], s);
storage.resize(s);
}
}
}
// Next, get more input data from stream, and decode it
while(left)
{
AVPacket packet;
// Get the next packet, if any
if(av_read_frame(FmtCtx, &packet) < 0)
break;
// We only allow one stream per file at the moment
assert((int)StreamNum == packet.stream_index);
// Decode the packet
int len = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf,
&len, packet.data, packet.size);
assert(tmp < 0 || tmp == packet.size);
// We don't need the input packet any longer
av_free_packet(&packet);
if(tmp < 0)
fail("Error decoding audio stream");
// Copy whatever data we got, and advance the pointer
if(len > 0)
{
// copy = how many bytes do we copy now
size_t copy = len;
if(copy > left)
copy = left;
// len = how many bytes are left uncopied
len -= copy;
// copy data
memcpy(outPtr, outBuf, copy);
// left = how much space is left in the caller output
// buffer. This loop repeats as long left is > 0
left -= copy;
outPtr += copy;
assert(left >= 0);
if(len > 0)
{
// There were uncopied bytes. Store them for later.
assert(left == 0);
storage.resize(len);
memcpy(&storage[0], outBuf, len);
}
}
}
// End of loop. Return the number of bytes copied.
assert(left <= length);
// If we're returning less than asked for, then we're done
if(left > 0)
isEof = true;
return length - left;
}

@ -1,52 +0,0 @@
#ifndef MANGLE_SOUND_FFMPEG_H
#define MANGLE_SOUND_FFMPEG_H
#include "../source.hpp"
#include <vector>
#include <assert.h>
extern "C"
{
#include <avcodec.h>
#include <avformat.h>
}
namespace Mangle {
namespace Sound {
class FFMpegSource : public SampleSource
{
AVFormatContext *FmtCtx;
AVCodecContext *CodecCtx;
unsigned int StreamNum;
std::vector<uint8_t> storage;
public:
/// Decode the given sound file
FFMpegSource(const std::string &file);
/// Decode the given sound stream (not supported by FFmpeg)
FFMpegSource(Mangle::Stream::StreamPtr src) { assert(0); }
~FFMpegSource();
// Overrides
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
};
#include "loadertemplate.hpp"
/// A factory that loads FFMpegSources from file
class FFMpegLoader : public SSL_Template<FFMpegSource,false,true>
{
public:
/// Sets up the libavcodec library. If you want to do your own
/// setup, send a setup=false parameter.
FFMpegLoader(bool setup=true);
};
}} // namespaces
#endif

@ -1,48 +0,0 @@
#include "libsndfile.hpp"
#include <stdexcept>
#include <sndfile.h>
using namespace Mangle::Stream;
static void fail(const std::string &msg)
{ throw std::runtime_error("Mangle::libsndfile: " + msg); }
using namespace Mangle::Sound;
void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
{
*_rate = rate;
*_channels = channels;
*_bits = bits;
}
size_t SndFileSource::readSamples(void *data, size_t length)
{
// readf_* reads entire frames, including channels
return sf_readf_short((SNDFILE*)handle, (short*)data, length);
}
SndFileSource::SndFileSource(const std::string &file)
{
SF_INFO info;
info.format = 0;
handle = sf_open(file.c_str(), SFM_READ, &info);
if(handle == NULL)
fail("Failed to open " + file);
// I THINK that using sf_read_short forces the library to convert to
// 16 bits no matter what, but the libsndfile docs aren't exactly
// very clear on this point.
channels = info.channels;
rate = info.samplerate;
bits = 16;
// 16 bits per sample times number of channels
setup(2*channels);
}
SndFileSource::~SndFileSource()
{
sf_close((SNDFILE*)handle);
}

@ -1,36 +0,0 @@
#ifndef MANGLE_SOUND_SNDFILE_SOURCE_H
#define MANGLE_SOUND_SNDFILE_SOURCE_H
#include "sample_reader.hpp"
namespace Mangle {
namespace Sound {
/// A sample source that decodes files using libsndfile. Supports most
/// formats except mp3.
class SndFileSource : public SampleReader
{
void *handle;
int channels, rate, bits;
size_t readSamples(void *data, size_t length);
public:
/// Decode the given sound file
SndFileSource(const std::string &file);
/// Decode the given sound stream (not supported)
SndFileSource(Mangle::Stream::StreamPtr src) { assert(0); }
~SndFileSource();
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
};
#include "loadertemplate.hpp"
/// A factory that loads SndFileSources from file and stream
typedef SSL_Template<SndFileSource,false,true> SndFileLoader;
}} // Namespace
#endif

@ -1,28 +0,0 @@
#ifndef SSL_TEMPL_H
#define SSL_TEMPL_H
template <class SourceT, bool stream, bool file>
class SSL_Template : public SampleSourceLoader
{
public:
SSL_Template()
{
canLoadStream = stream;
canLoadFile = file;
}
SampleSourcePtr load(const std::string &filename)
{
assert(canLoadFile);
return SampleSourcePtr(new SourceT(filename));
}
SampleSourcePtr load(Stream::StreamPtr input)
{
assert(canLoadStream);
return SampleSourcePtr(new SourceT(input));
}
};
#endif

@ -1,115 +0,0 @@
#include "mpg123_source.hpp"
#include <stdexcept>
#include <mpg123.h>
using namespace Mangle::Stream;
/*
TODOs:
- mpg123 impressively enough supports custom stream reading. Which
means we could (and SHOULD!) support reading from Mangle::Streams
as well. But I'll save it til I need it.
An alternative way to do this is through feeding (which they also
support), but that's more messy.
- the library also supports output, via various other sources,
including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this
library as a pure output library (if that is possible) would be a
nice shortcut over using those libraries - OTOH it's another
dependency.
- we could implement seek(), tell() and size(), but they aren't
really necessary. Furthermore, since the returned size is only a
guess, it is not safe to rely on it.
*/
static void fail(const std::string &msg)
{ throw std::runtime_error("Mangle::Mpg123 exception: " + msg); }
static void checkError(int err, void *mh = NULL)
{
if(err != MPG123_OK)
{
std::string msg;
if(mh) msg = mpg123_strerror((mpg123_handle*)mh);
else msg = mpg123_plain_strerror(err);
fail(msg);
}
}
using namespace Mangle::Sound;
void Mpg123Source::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits)
{
// Use the values we found in the constructor
*pRate = rate;
*pChannels = channels;
*pBits = bits;
}
size_t Mpg123Source::read(void *data, size_t length)
{
size_t done;
// This is extraordinarily nice. I like this library.
int err = mpg123_read((mpg123_handle*)mh, (unsigned char*)data, length, &done);
assert(done <= length);
if(err == MPG123_DONE)
isEof = true;
else
checkError(err, mh);
return done;
}
Mpg123Loader::Mpg123Loader(bool setup)
{
// Do as we're told
if(setup)
{
int err = mpg123_init();
checkError(err);
}
didSetup = setup;
}
Mpg123Loader::~Mpg123Loader()
{
// Deinitialize the library on exit
if(didSetup)
mpg123_exit();
}
Mpg123Source::Mpg123Source(const std::string &file)
{
int err;
// Create a new handle
mh = mpg123_new(NULL, &err);
if(mh == NULL)
checkError(err, mh);
mpg123_handle *mhh = (mpg123_handle*)mh;
// Open the file (hack around constness)
err = mpg123_open(mhh, (char*)file.c_str());
checkError(err, mh);
// Get the format
int encoding;
err = mpg123_getformat(mhh, &rate, &channels, &encoding);
checkError(err, mh);
if(encoding != MPG123_ENC_SIGNED_16)
fail("Unsupported encoding in " + file);
// This is the only bit size we support.
bits = 16;
}
Mpg123Source::~Mpg123Source()
{
mpg123_close((mpg123_handle*)mh);
mpg123_delete((mpg123_handle*)mh);
}

@ -1,47 +0,0 @@
#ifndef MANGLE_SOUND_MPG123_SOURCE_H
#define MANGLE_SOUND_MPG123_SOURCE_H
#include "../source.hpp"
#include <assert.h>
namespace Mangle {
namespace Sound {
/// A sample source that decodes files using libmpg123. Only supports
/// MP3 files.
class Mpg123Source : public SampleSource
{
void *mh;
long int rate;
int channels, bits;
public:
/// Decode the given sound file
Mpg123Source(const std::string &file);
/// Needed by SSL_Template but not yet supported
Mpg123Source(Mangle::Stream::StreamPtr data)
{ assert(0); }
~Mpg123Source();
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
};
#include "loadertemplate.hpp"
/// A factory that loads Mpg123Sources from file and stream
struct Mpg123Loader : SSL_Template<Mpg123Source,false,true>
{
/** Sets up libmpg123 for you, and closes it on destruction. If you
want to do this yourself, send setup=false.
*/
Mpg123Loader(bool setup=true);
~Mpg123Loader();
private:
bool didSetup;
};
}} // Namespace
#endif

@ -1,99 +0,0 @@
#include "sample_reader.hpp"
#include <string.h>
using namespace Mangle::Sound;
void SampleReader::setup(int size)
{
pullSize = 0;
frameSize = size;
pullOver = new char[size];
}
SampleReader::~SampleReader()
{
if(pullOver)
delete[] pullOver;
}
size_t SampleReader::read(void *_data, size_t length)
{
if(isEof) return 0;
char *data = (char*)_data;
// Pullsize holds the number of bytes that were copied "extra" at
// the end of LAST round. If non-zero, it also means there is data
// left in the pullOver buffer.
if(pullSize)
{
// Amount of data left
size_t doRead = frameSize - pullSize;
assert(doRead > 0);
// Make sure we don't read more than we're supposed to
if(doRead > length) doRead = length;
memcpy(data, pullOver+pullSize, doRead);
// Update the number of bytes now copied
pullSize += doRead;
assert(pullSize <= frameSize);
if(pullSize < frameSize)
{
// There is STILL data left in the pull buffer, and we've
// done everything we were supposed to. Leave it and return.
assert(doRead == length);
return doRead;
}
// Set up variables for further reading below. No need to update
// pullSize, it is overwritten anyway.
length -= doRead;
data += doRead;
}
// Number of whole frames
size_t frames = length / frameSize;
// Read the data
size_t res = readSamples(data, frames);
assert(res <= frames);
// Total bytes read
size_t num = res*frameSize;
data += num;
if(res < frames)
{
// End of stream.
isEof = true;
// Determine how much we read
return data-(char*)_data;
}
// Determine the overshoot
pullSize = length - num;
assert(pullSize < frameSize && pullSize >= 0);
// Are we missing data?
if(pullSize)
{
// Fill in one sample
res = readSamples(pullOver,1);
assert(res == 1 || res == 0);
if(res)
{
// Move as much as we can into the output buffer
memcpy(data, pullOver, pullSize);
data += pullSize;
}
else
// Failed reading, we're out of data
isEof = true;
}
// Return the total number of bytes stored
return data-(char*)_data;
}

@ -1,48 +0,0 @@
#ifndef MANGLE_SOUND_SAMPLE_READER_H
#define MANGLE_SOUND_SAMPLE_READER_H
#include "../source.hpp"
namespace Mangle {
namespace Sound {
/* This is a helper base class for other SampleSource
implementations. Certain sources (like Audiere and libsndfile)
insist on reading whole samples rather than bytes. This class
compensates for that, and allows you to read bytes rather than
samples.
There are two ways for subclasses to use this class. EITHER call
setup() with the size of frameSize. This will allocate a buffer,
which the destructor frees. OR set frameSize manually and
manipulate the pullOver pointer yourself. In that case you MUST
reset it to NULL if you don't want the destructor to call
delete[] on it.
*/
class SampleReader : public SampleSource
{
// How much of the above buffer is in use.
int pullSize;
protected:
// Pullover buffer
char* pullOver;
// Size of one frame, in bytes. This is also the size of the
// pullOver buffer.
int frameSize;
// The parameter gives the size of one sample/frame, in bytes.
void setup(int);
// Read the given number of samples, in multiples of frameSize. Does
// not have to set or respect isEof.
virtual size_t readSamples(void *data, size_t num) = 0;
public:
SampleReader() : pullSize(0), pullOver(NULL) {}
~SampleReader();
size_t read(void *data, size_t length);
};
}} // Namespace
#endif

@ -1,47 +0,0 @@
#ifndef MANGLE_SOUND_STREAMSOURCE_H
#define MANGLE_SOUND_STREAMSOURCE_H
#include "../source.hpp"
namespace Mangle {
namespace Sound {
/// A class for reading raw samples directly from a stream.
class Stream2Samples : public SampleSource
{
Mangle::Stream::StreamPtr inp;
int32_t rate, channels, bits;
public:
Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits)
: inp(_inp), rate(_rate), channels(_channels), bits(_bits)
{
isSeekable = inp->isSeekable;
hasPosition = inp->hasPosition;
hasSize = inp->hasSize;
hasPtr = inp->hasPtr;
}
/// Get the sample rate, number of channels, and bits per
/// sample. NULL parameters are ignored.
void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits)
{
if(_rate) *_rate = rate;
if(_channels) *_channels = channels;
if(_bits) *_bits = bits;
}
size_t read(void *out, size_t count)
{ return inp->read(out, count); }
void seek(size_t pos) { inp->seek(pos); }
size_t tell() const { return inp->tell(); }
size_t size() const { return inp->size(); }
bool eof() const { return inp->eof(); }
const void *getPtr() { return inp->getPtr(); }
const void *getPtr(size_t size) { return inp->getPtr(size); }
const void *getPtr(size_t pos, size_t size) { return inp->getPtr(pos, size); }
};
}} // namespaces
#endif

@ -1,99 +0,0 @@
#include "wav_source.hpp"
#include "../../stream/servers/file_stream.hpp"
#include <stdexcept>
using namespace Mangle::Stream;
using namespace Mangle::Sound;
static void fail(const std::string &msg)
{ throw std::runtime_error("Mangle::Wav exception: " + msg); }
void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits)
{
// Use the values we found in the constructor
*pRate = rate;
*pChannels = channels;
*pBits = bits;
}
void WavSource::seek(size_t pos)
{
// Seek the stream and set 'left'
assert(isSeekable);
if(pos > total) pos = total;
input->seek(dataOffset + pos);
left = total-pos;
}
size_t WavSource::read(void *data, size_t length)
{
if(length > left)
length = left;
size_t read = input->read(data, length);
if(read < length)
// Something went wrong
fail("WAV read error");
return length;
}
void WavSource::open(Mangle::Stream::StreamPtr data)
{
input = data;
hasPosition = true;
hasSize = true;
// If we can check position and seek in the input stream, then we
// can seek the wav data too.
isSeekable = input->isSeekable && input->hasPosition;
// Read header
unsigned int val;
input->read(&val,4); // header
if(val != 0x46464952) // "RIFF"
fail("Not a WAV file");
input->read(&val,4); // size (ignored)
input->read(&val,4); // file format
if(val != 0x45564157) // "WAVE"
fail("Not a valid WAV file");
input->read(&val,4); // "fmt "
input->read(&val,4); // chunk size (must be 16)
if(val != 16)
fail("Unsupported WAV format");
input->read(&val,2);
if(val != 1)
fail("Non-PCM (compressed) WAV files not supported");
// Sound data specification
channels = 0;
input->read(&channels,2);
input->read(&rate, 4);
// Skip next 6 bytes
input->read(&val, 4);
input->read(&val, 2);
// Bits per sample
bits = 0;
input->read(&bits,2);
input->read(&val,4); // Data header
if(val != 0x61746164) // "data"
fail("Expected data block");
// Finally, read the data size
input->read(&total,4);
left = total;
// Store the beginning of the data block for later
if(input->hasPosition)
dataOffset = input->tell();
}
WavSource::WavSource(const std::string &file)
{ open(StreamPtr(new FileStream(file))); }

@ -1,49 +0,0 @@
#ifndef MANGLE_SOUND_WAV_SOURCE_H
#define MANGLE_SOUND_WAV_SOURCE_H
#include "../source.hpp"
#include <assert.h>
namespace Mangle {
namespace Sound {
/// WAV file decoder. Has no external library dependencies.
class WavSource : public SampleSource
{
// Sound info
uint32_t rate, channels, bits;
// Total size (of output) and bytes left
uint32_t total, left;
// Offset in input of the beginning of the data block
size_t dataOffset;
Mangle::Stream::StreamPtr input;
void open(Mangle::Stream::StreamPtr);
public:
/// Decode the given sound file
WavSource(const std::string&);
/// Decode from stream
WavSource(Mangle::Stream::StreamPtr s)
{ open(s); }
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
size_t read(void *data, size_t length);
void seek(size_t);
size_t tell() const { return total-left; }
size_t size() const { return total; }
bool eof() const { return left > 0; }
};
#include "loadertemplate.hpp"
/// A factory that loads WavSources from file and stream
typedef SSL_Template<WavSource,true,true> WavLoader;
}} // Namespace
#endif

@ -1,38 +0,0 @@
GCC=g++ -I../ -Wall
all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test wav_source_test openal_various_test
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat
L_OPENAL=$(shell pkg-config --libs openal)
L_AUDIERE=-laudiere
wav_source_test: wav_source_test.cpp ../sources/wav_source.cpp
$(GCC) $^ -o $@
openal_various_test: openal_various_test.cpp ../sources/mpg123_source.cpp ../sources/wav_source.cpp ../outputs/openal_out.cpp
$(GCC) $^ -o $@ -lmpg123 ${L_OPENAL}
openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL)
openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp
$(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG)
openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp
$(GCC) $^ -o $@ -lmpg123 ${L_OPENAL}
openal_sndfile_test: openal_sndfile_test.cpp ../sources/libsndfile.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp
$(GCC) $^ -o $@ -lsndfile ${L_OPENAL}
openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp
$(GCC) $^ -o $@ $(L_OPENAL)
audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp ../sources/sample_reader.cpp
$(GCC) $^ -o $@ $(L_AUDIERE)
ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp
$(GCC) $^ -o $@ $(L_FFMPEG) $(I_FFMPEG)
clean:
rm *_test

@ -1,68 +0,0 @@
#include <iostream>
#include "../../stream/servers/file_stream.hpp"
#include "../sources/audiere_source.hpp"
#include <assert.h>
#include <string.h>
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
// Contents and size of cow.raw
void *orig;
size_t orig_size;
void run(SampleSourcePtr &src)
{
size_t ss = src->size();
assert(ss == orig_size);
cout << "Source size: " << ss << endl;
int rate, channels, bits;
src->getInfo(&rate, &channels, &bits);
cout << "rate=" << rate << "\nchannels=" << channels
<< "\nbits=" << bits << endl;
cout << "Reading entire buffer into memory\n";
void *buf = malloc(ss);
src->read(buf, ss);
cout << "Comparing...\n";
if(memcmp(buf, orig, ss) != 0)
{
cout << "Oops!\n";
assert(0);
}
cout << "Done\n";
}
int main()
{
{
cout << "Reading cow.raw first\n";
FileStream tmp("cow.raw");
orig_size = tmp.size();
cout << "Size: " << orig_size << endl;
orig = malloc(orig_size);
tmp.read(orig, orig_size);
cout << "Done\n";
}
{
cout << "\nLoading cow.wav by filename:\n";
SampleSourcePtr cow_file( new AudiereSource("cow.wav") );
run(cow_file);
}
{
cout << "\nLoading cow.wav by stream:\n";
StreamPtr inp( new FileStream("cow.wav") );
SampleSourcePtr cow_stream( new AudiereSource(inp) );
run(cow_stream);
}
return 0;
}

Binary file not shown.

Binary file not shown.

@ -1,62 +0,0 @@
#include <iostream>
#include "../../stream/servers/file_stream.hpp"
#include "../sources/ffmpeg_source.hpp"
#include <assert.h>
#include <string.h>
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
// Contents and size of cow.raw
void *orig;
size_t orig_size;
void run(SampleSourcePtr &src)
{
int rate, channels, bits;
src->getInfo(&rate, &channels, &bits);
cout << "rate=" << rate << "\nchannels=" << channels
<< "\nbits=" << bits << endl;
cout << "Reading entire buffer into memory\n";
void *buf = malloc(orig_size);
size_t ss = src->read(buf, orig_size);
cout << "Actually read: " << ss << endl;
assert(ss == orig_size);
cout << "Comparing...\n";
if(memcmp(buf, orig, ss) != 0)
{
cout << "Oops!\n";
assert(0);
}
cout << "Done\n";
}
int main()
{
{
cout << "Reading cow.raw first\n";
FileStream tmp("cow.raw");
orig_size = tmp.size();
cout << "Size: " << orig_size << endl;
orig = malloc(orig_size);
tmp.read(orig, orig_size);
cout << "Done\n";
}
// Initializes the library, not used for anything else.
FFMpegLoader fm;
{
cout << "\nLoading cow.wav by filename:\n";
SampleSourcePtr cow_file( new FFMpegSource("cow.wav") );
run(cow_file);
}
return 0;
}

@ -1,52 +0,0 @@
#include <iostream>
#include <exception>
#include "../../stream/servers/file_stream.hpp"
#include "../filters/openal_audiere.hpp"
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
OpenAL_Audiere_Factory mg;
void play(const char* name, bool stream=false)
{
// Only load streams if the backend supports it
if(stream && !mg.canLoadStream)
return;
cout << "Playing " << name;
if(stream) cout << " (from stream)";
cout << "\n";
SoundPtr snd;
try
{
if(stream)
snd = mg.load(StreamPtr(new FileStream(name)));
else
snd = mg.load(name);
snd->play();
while(snd->isPlaying())
{
usleep(10000);
if(mg.needsUpdate) mg.update();
}
}
catch(exception &e)
{
cout << " ERROR: " << e.what() << "\n";
}
}
int main()
{
play("cow.wav");
play("owl.ogg");
play("cow.wav", true);
return 0;
}

@ -1,52 +0,0 @@
#include <iostream>
#include <exception>
#include "../../stream/servers/file_stream.hpp"
#include "../filters/openal_ffmpeg.hpp"
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
OpenAL_FFMpeg_Factory mg;
void play(const char* name, bool stream=false)
{
// Only load streams if the backend supports it
if(stream && !mg.canLoadStream)
return;
cout << "Playing " << name;
if(stream) cout << " (from stream)";
cout << "\n";
SoundPtr snd;
try
{
if(stream)
snd = mg.load(StreamPtr(new FileStream(name)));
else
snd = mg.load(name);
snd->play();
while(snd->isPlaying())
{
usleep(10000);
if(mg.needsUpdate) mg.update();
}
}
catch(exception &e)
{
cout << " ERROR: " << e.what() << "\n";
}
}
int main()
{
play("cow.wav");
play("owl.ogg");
play("cow.wav", true);
return 0;
}

@ -1,54 +0,0 @@
#include <iostream>
#include <exception>
#include "../../stream/servers/file_stream.hpp"
#include "../filters/openal_mpg123.hpp"
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
OpenAL_Mpg123_Factory mg;
void play(const char* name, bool stream=false)
{
// Only load streams if the backend supports it
if(stream && !mg.canLoadStream)
return;
cout << "Playing " << name;
if(stream) cout << " (from stream)";
cout << "\n";
SoundPtr snd;
try
{
if(stream)
snd = mg.load(StreamPtr(new FileStream(name)));
else
snd = mg.load(name);
snd->setStreaming(true);
snd->play();
while(snd->isPlaying())
{
usleep(10000);
if(mg.needsUpdate) mg.update();
}
}
catch(exception &e)
{
cout << " ERROR: " << e.what() << "\n";
}
}
int main(int argc, char**argv)
{
if(argc != 2)
cout << "Please specify an MP3 file\n";
else
play(argv[1]);
return 0;
}

@ -1,59 +0,0 @@
#include <iostream>
#include <exception>
#include "../../stream/servers/file_stream.hpp"
#include "../sources/stream_source.hpp"
#include "../outputs/openal_out.hpp"
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
int main()
{
cout << "Loading cow.raw\n";
int rate = 11025;
int chan = 1;
int bits = 16;
cout << " rate=" << rate << "\n channels=" << chan
<< "\n bits=" << bits << endl;
StreamPtr file( new FileStream("cow.raw") );
SampleSourcePtr source( new Stream2Samples( file, rate, chan, bits));
cout << "Playing\n";
OpenAL_Factory mg;
SoundPtr snd = mg.loadRaw(source);
try
{
// Try setting all kinds of stuff before playing. OpenAL_Sound
// uses delayed buffer loading, but these should still work
// without a buffer.
snd->stop();
snd->pause();
snd->setVolume(0.8);
snd->setPitch(0.9);
// Also test streaming, since all the other examples test
// non-streaming sounds.
snd->setStreaming(true);
snd->play();
while(snd->isPlaying())
{
usleep(10000);
mg.update();
}
}
catch(exception &e)
{
cout << " ERROR: " << e.what() << "\n";
}
return 0;
}

@ -1,52 +0,0 @@
#include <iostream>
#include <exception>
#include "../../stream/servers/file_stream.hpp"
#include "../filters/openal_sndfile.hpp"
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
OpenAL_SndFile_Factory mg;
void play(const char* name, bool stream=false)
{
// Only load streams if the backend supports it
if(stream && !mg.canLoadStream)
return;
cout << "Playing " << name;
if(stream) cout << " (from stream)";
cout << "\n";
SoundPtr snd;
try
{
if(stream)
snd = mg.load(StreamPtr(new FileStream(name)));
else
snd = mg.load(name);
snd->play();
while(snd->isPlaying())
{
usleep(10000);
if(mg.needsUpdate) mg.update();
}
}
catch(exception &e)
{
cout << " ERROR: " << e.what() << "\n";
}
}
int main()
{
play("cow.wav");
play("owl.ogg");
play("cow.wav", true);
return 0;
}

@ -1,51 +0,0 @@
#include <iostream>
#include <exception>
#include "../../stream/servers/file_stream.hpp"
#include "../filters/openal_various.hpp"
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
OpenAL_Various_Factory mg;
void play(const char* name, bool stream=false)
{
// Only load streams if the backend supports it
if(stream && !mg.canLoadStream)
return;
cout << "Playing " << name;
if(stream) cout << " (from stream)";
cout << "\n";
SoundPtr snd;
try
{
if(stream)
snd = mg.load(StreamPtr(new FileStream(name)));
else
snd = mg.load(name);
snd->play();
while(snd->isPlaying())
{
usleep(10000);
if(mg.needsUpdate) mg.update();
}
}
catch(exception &e)
{
cout << " ERROR: " << e.what() << "\n";
}
}
int main()
{
play("cow.wav");
play("cow.wav", true);
return 0;
}

@ -1,21 +0,0 @@
Reading cow.raw first
Size: 37502
Done
Loading cow.wav by filename:
Source size: 37502
rate=11025
channels=1
bits=16
Reading entire buffer into memory
Comparing...
Done
Loading cow.wav by stream:
Source size: 37502
rate=11025
channels=1
bits=16
Reading entire buffer into memory
Comparing...
Done

@ -1,12 +0,0 @@
Reading cow.raw first
Size: 37502
Done
Loading cow.wav by filename:
rate=11025
channels=1
bits=16
Reading entire buffer into memory
Actually read: 37502
Comparing...
Done

@ -1,3 +0,0 @@
Playing cow.wav
Playing owl.ogg
Playing cow.wav (from stream)

@ -1,2 +0,0 @@
Playing cow.wav
Playing owl.ogg

@ -1,5 +0,0 @@
Loading cow.raw
rate=11025
channels=1
bits=16
Playing

@ -1,2 +0,0 @@
Playing cow.wav
Playing owl.ogg

@ -1,12 +0,0 @@
Source size: 37502
rate=11025
channels=1
bits=16
Reading entire buffer into memory
Reading cow.raw
Size: 37502
Comparing...
Done

Binary file not shown.

@ -1,18 +0,0 @@
#!/bin/bash
make || exit
mkdir -p output
PROGS=*_test
for a in $PROGS; do
if [ -f "output/$a.out" ]; then
echo "Running $a:"
./$a | diff output/$a.out -
else
echo "Creating $a.out"
./$a > "output/$a.out"
git add "output/$a.out"
fi
done

@ -1,48 +0,0 @@
#include <iostream>
#include "../sources/wav_source.hpp"
#include "../../stream/servers/file_stream.hpp"
#include <assert.h>
#include <string.h>
using namespace std;
using namespace Mangle::Sound;
using namespace Mangle::Stream;
int main()
{
WavSource wav("cow.wav");
cout << "Source size: " << wav.size() << endl;
int rate, channels, bits;
wav.getInfo(&rate, &channels, &bits);
cout << "rate=" << rate << "\nchannels=" << channels
<< "\nbits=" << bits << endl;
cout << "Reading entire buffer into memory\n";
void *buf = malloc(wav.size());
wav.read(buf, wav.size());
cout << "\nReading cow.raw\n";
FileStream tmp("cow.raw");
cout << "Size: " << tmp.size() << endl;
void *buf2 = malloc(tmp.size());
tmp.read(buf2, tmp.size());
cout << "\nComparing...\n";
if(tmp.size() != wav.size())
{
cout << "SIZE MISMATCH!\n";
assert(0);
}
if(memcmp(buf, buf2, wav.size()) != 0)
{
cout << "CONTENT MISMATCH!\n";
assert(0);
}
cout << "\nDone\n";
return 0;
}

@ -80,6 +80,29 @@ namespace GUI
mMainWidget->setCoord(x,y,w,h);
}
void adjustWindowCaption()
{
// adjust the size of the window caption so that all text is visible
// NOTE: this assumes that mMainWidget is of type Window.
MyGUI::TextBox* box = static_cast<MyGUI::Window*>(mMainWidget)->getCaptionWidget();
box->setSize(box->getTextSize().width + 48, box->getSize().height);
// in order to trigger alignment updates, we need to update the parent
// mygui doesn't provide a proper way of doing this, so we are just changing size
box->getParent()->setCoord(MyGUI::IntCoord(
box->getParent()->getCoord().left,
box->getParent()->getCoord().top,
box->getParent()->getCoord().width,
box->getParent()->getCoord().height+1
));
box->getParent()->setCoord(MyGUI::IntCoord(
box->getParent()->getCoord().left,
box->getParent()->getCoord().top,
box->getParent()->getCoord().width,
box->getParent()->getCoord().height-1
));
}
void setVisible(bool b)
{
mMainWidget->setVisible(b);

@ -1,219 +0,0 @@
#include "sndmanager.hpp"
#include "../misc/list.hpp"
#include <boost/weak_ptr.hpp>
using namespace OEngine::Sound;
using namespace Mangle::Sound;
/** This is our own internal implementation of the
Mangle::Sound::Sound interface. This class links a SoundPtr to
itself and prevents itself from being deleted as long as the sound
is playing.
*/
struct OEngine::Sound::ManagedSound : SoundFilter
{
private:
/** Who's your daddy? This is set if and only if we are listed
internally in the given SoundManager.
It may be NULL if the manager has been deleted but the user
keeps their own SoundPtrs to the object.
*/
SoundManager *mgr;
/** Keep a weak pointer to ourselves, which we convert into a
'strong' pointer when we are playing. When 'self' is pointing to
ourselves, the object will never be deleted.
This is used to make sure the sound is not deleted while
playing, unless it is explicitly ordered to do so by the
manager.
TODO: This kind of construct is useful. If we need it elsewhere
later, template it. It would be generally useful in any system
where we poll to check if a resource is still needed, but where
manual references are allowed.
*/
WSoundPtr weak;
SoundPtr self;
// Keep this object from being deleted
void lock()
{
self = SoundPtr(weak);
}
// Release the lock. This may or may not delete the object. Never do
// anything after calling unlock()!
void unlock()
{
self.reset();
}
public:
// Used for putting ourselves in linked lists
ManagedSound *next, *prev;
/** Detach this sound from its manager. This means that the manager
will no longer know we exist. Typically only called when either
the sound or the manager is about to get deleted.
Since this means update() will no longer be called, we also have
to unlock the sound manually since it will no longer be able to
do that itself. This means that the sound may be deleted, even
if it is still playing, when the manager is deleted.
However, you are still allowed to keep and manage your own
SoundPtr references, but the lock/unlock system is disabled
after the manager is gone.
*/
void detach()
{
if(mgr)
{
mgr->detach(this);
mgr = NULL;
}
// Unlock must be last command. Object may get deleted at this
// point.
unlock();
}
ManagedSound(SoundPtr snd, SoundManager *mg)
: SoundFilter(snd), mgr(mg)
{}
~ManagedSound() { detach(); }
// Needed to set up the weak pointer
void setup(SoundPtr self)
{
weak = WSoundPtr(self);
}
// Override play() to mark the object as locked
void play()
{
SoundFilter::play();
// Lock the object so that it is not deleted while playing. Only
// do this if we have a manager, otherwise the object will never
// get unlocked.
if(mgr) lock();
}
// Called regularly by the manager
void update()
{
// If we're no longer playing, don't force object retention.
if(!isPlaying())
unlock();
// unlock() may delete the object, so don't do anything below this
// point.
}
SoundPtr clone()
{
// Cloning only works when we have a manager.
assert(mgr);
return mgr->wrap(client->clone());
}
};
struct SoundManager::SoundManagerList
{
private:
// A linked list of ManagedSound objects.
typedef Misc::List<ManagedSound> SoundList;
SoundList list;
public:
// Add a new sound to the list
void addNew(ManagedSound* snd)
{
list.insert(snd);
}
// Remove a sound from the list
void remove(ManagedSound *snd)
{
list.remove(snd);
}
// Number of sounds in the list
int numSounds() { return list.getNum(); }
// Update all sounds
void updateAll()
{
ManagedSound *s = list.getHead();
while(s)
{
ManagedSound *cur = s;
// Propagate first, since update() may delete object
s = s->next;
cur->update();
}
}
// Detach and unlock all sounds
void detachAll()
{
ManagedSound *s = list.getHead();
while(s)
{
ManagedSound *cur = s;
s = s->next;
cur->detach();
}
}
};
SoundManager::SoundManager(SoundFactoryPtr fact)
: FactoryFilter(fact)
{
needsUpdate = true;
list = new SoundManagerList;
}
SoundManager::~SoundManager()
{
// Detach all sounds
list->detachAll();
}
SoundPtr SoundManager::wrap(SoundPtr client)
{
// Create and set up the sound wrapper
ManagedSound *snd = new ManagedSound(client,this);
SoundPtr ptr(snd);
snd->setup(ptr);
// Add ourselves to the list of all sounds
list->addNew(snd);
return ptr;
}
// Remove the sound from this manager.
void SoundManager::detach(ManagedSound *sound)
{
list->remove(sound);
}
int SoundManager::numSounds()
{
return list->numSounds();
}
void SoundManager::update()
{
// Update all the sounds we own
list->updateAll();
// Update the source if it needs it
if(client->needsUpdate)
client->update();
}

@ -1,93 +0,0 @@
#ifndef OENGINE_SOUND_MANAGER_H
#define OENGINE_SOUND_MANAGER_H
#include <mangle/sound/filters/pure_filter.hpp>
namespace OEngine
{
namespace Sound
{
using namespace Mangle::Sound;
class ManagedSound;
/** A manager of Mangle::Sounds.
The sound manager is a wrapper around the more low-level
SoundFactory - although it is also itself an implementation of
SoundFactory. It will:
- keep a list of all created sounds
- let you iterate the list
- keep references to playing sounds so you don't have to
- auto-release references to sounds that are finished playing
(ie. deleting them if you're not referencing them)
*/
class SoundManager : public FactoryFilter
{
// Shove the implementation details into the cpp file.
struct SoundManagerList;
SoundManagerList *list;
// Create a new sound wrapper based on the given source sound.
SoundPtr wrap(SoundPtr snd);
/** Internal function. Will completely disconnect the given
sound from this manager. Called from ManagedSound.
*/
friend class ManagedSound;
void detach(ManagedSound *sound);
public:
SoundManager(SoundFactoryPtr fact);
~SoundManager();
void update();
/// Get number of sounds currently managed by this manager.
int numSounds();
SoundPtr loadRaw(SampleSourcePtr input)
{ return wrap(client->loadRaw(input)); }
SoundPtr load(Mangle::Stream::StreamPtr input)
{ return wrap(client->load(input)); }
SoundPtr load(const std::string &file)
{ return wrap(client->load(file)); }
// Play a sound immediately, and release when done unless you
// keep the returned SoundPtr.
SoundPtr play(Mangle::Stream::StreamPtr sound)
{
SoundPtr snd = load(sound);
snd->play();
return snd;
}
SoundPtr play(const std::string &sound)
{
SoundPtr snd = load(sound);
snd->play();
return snd;
}
// Ditto for 3D sounds
SoundPtr play3D(Mangle::Stream::StreamPtr sound, float x, float y, float z)
{
SoundPtr snd = load(sound);
snd->setPos(x,y,z);
snd->play();
return snd;
}
SoundPtr play3D(const std::string &sound, float x, float y, float z)
{
SoundPtr snd = load(sound);
snd->setPos(x,y,z);
snd->play();
return snd;
}
};
typedef boost::shared_ptr<SoundManager> SoundManagerPtr;
}
}
#endif

@ -1,16 +0,0 @@
GCC=g++ -I../
all: sound_manager_test sound_3d_test
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
L_OPENAL=$(shell pkg-config --libs openal)
L_AUDIERE=-laudiere
sound_manager_test: sound_manager_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
sound_3d_test: sound_3d_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp
$(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../..
clean:
rm *_test

@ -1,3 +0,0 @@
Playing at 0,0,0
Playing at 1,1,0
Playing at -1,0,0

@ -1,5 +0,0 @@
Playing ../../mangle/sound/tests/cow.wav
Replaying
pause
restart
Done playing.

@ -1,46 +0,0 @@
#include <iostream>
#include <exception>
#include <assert.h>
#include <mangle/stream/servers/file_stream.hpp>
#include <mangle/sound/filters/openal_audiere.hpp>
#include <sound/sndmanager.hpp>
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
using namespace OEngine::Sound;
const std::string sound = "../../mangle/sound/tests/cow.wav";
SoundManagerPtr m;
// Play and wait for finish
void play(float x, float y, float z)
{
cout << "Playing at " << x << "," << y << "," << z << endl;
SoundPtr snd = m->play3D(sound,x,y,z);
while(snd->isPlaying())
{
usleep(10000);
m->update();
}
}
int main()
{
SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
SoundManagerPtr mg(new SoundManager(oaf));
m = mg;
mg->setListenerPos(0,0,0,0,1,0,0,0,1);
play(0,0,0);
play(1,1,0);
play(-1,0,0);
return 0;
}

@ -1,73 +0,0 @@
#include <iostream>
#include <exception>
#include <assert.h>
#include <mangle/stream/servers/file_stream.hpp>
#include <mangle/sound/filters/openal_audiere.hpp>
#include <sound/sndmanager.hpp>
using namespace std;
using namespace Mangle::Stream;
using namespace Mangle::Sound;
using namespace OEngine::Sound;
const std::string sound = "../../mangle/sound/tests/cow.wav";
int main()
{
SoundFactoryPtr oaf(new OpenAL_Audiere_Factory);
SoundManagerPtr mg(new SoundManager(oaf));
cout << "Playing " << sound << "\n";
assert(mg->numSounds() == 0);
/** Start the sound playing, and then let the pointer go out of
scope. Lower-level players (like 'oaf' above) will immediately
delete the sound. SoundManager OTOH will keep it until it's
finished.
*/
mg->play(sound);
assert(mg->numSounds() == 1);
// Loop while there are still sounds to manage
while(mg->numSounds() != 0)
{
assert(mg->numSounds() == 1);
usleep(10000);
if(mg->needsUpdate)
mg->update();
}
SoundPtr snd = mg->play(sound);
cout << "Replaying\n";
int i = 0;
while(mg->numSounds() != 0)
{
assert(mg->numSounds() == 1);
usleep(10000);
if(mg->needsUpdate)
mg->update();
if(i++ == 70)
{
cout << "pause\n";
snd->pause();
}
if(i == 130)
{
cout << "restart\n";
snd->play();
// Let the sound go out of scope
snd.reset();
}
}
cout << "Done playing.\n";
assert(mg->numSounds() == 0);
return 0;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save