Merge branch 'next' into occlusionquery
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.
|
@ -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.
|
Binary file not shown.
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 50 KiB |
@ -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
|
Binary file not shown.
Binary file not shown.
@ -1,28 +1,26 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<MyGUI>
|
<MyGUI>
|
||||||
|
<MyGUI type="List">
|
||||||
<MyGUI type="List">
|
<List file="core.skin" />
|
||||||
<List file="core.skin" group="General"/>
|
<List file="openmw_images.xml" />
|
||||||
<List file="openmw_images.xml" group="General"/>
|
<List file="openmw_layers.xml" />
|
||||||
<List file="openmw_layers.xml" group="General"/>
|
<List file="openmw.pointer.xml" />
|
||||||
<List file="openmw.pointer.xml" group="General"/>
|
<List file="openmw.font.xml" />
|
||||||
<List file="openmw.font.xml" group="General"/>
|
<List file="openmw_text.skin.xml" />
|
||||||
<List file="openmw_text.skin.xml" group="General"/>
|
<List file="openmw_windows.skin.xml" />
|
||||||
<List file="openmw_windows.skin.xml" group="General"/>
|
<List file="openmw_button.skin.xml" />
|
||||||
<List file="openmw_button.skin.xml" group="General"/>
|
<List file="openmw_list.skin.xml" />
|
||||||
<List file="openmw_list.skin.xml" group="General"/>
|
<List file="openmw_edit.skin.xml" />
|
||||||
<List file="openmw_edit.skin.xml" group="General"/>
|
<List file="openmw_box.skin.xml" />
|
||||||
<List file="openmw_box.skin.xml" group="General"/>
|
<List file="openmw_progress.skin.xml" />
|
||||||
<List file="openmw_progress.skin.xml" group="General"/>
|
<List file="openmw_hud_energybar.skin.xml" />
|
||||||
<List file="openmw_hud_energybar.skin.xml" group="General"/>
|
<List file="openmw_hud_box.skin.xml" />
|
||||||
<List file="openmw_hud_box.skin.xml" group="General"/>
|
<List file="openmw_mainmenu_skin.xml" />
|
||||||
<List file="openmw_mainmenu_skin.xml" group="General"/>
|
<List file="openmw_console.skin.xml" />
|
||||||
<List file="openmw_console.skin.xml" group="General"/>
|
<List file="openmw_journal_skin.xml" />
|
||||||
<List file="openmw_journal_skin.xml" group="General"/>
|
<List file="openmw_map_window_skin.xml" />
|
||||||
<List file="openmw_map_window_skin.xml" group="General"/>
|
<List file="openmw_dialogue_window_skin.xml" />
|
||||||
<List file="openmw_dialogue_window_skin.xml" group="General"/>
|
<List file="openmw_settings.xml" />
|
||||||
<List file="openmw_settings.xml" group="General"/>
|
</MyGUI>
|
||||||
</MyGUI>
|
|
||||||
|
|
||||||
</MyGUI>
|
</MyGUI>
|
||||||
|
|
||||||
|
@ -1,95 +1,40 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MyGUI type="Resource" version="1.1">
|
||||||
<MyGUI type="Font">
|
<Resource type="ResourceTrueTypeFont" name="EB Garamond">
|
||||||
|
<Property key="Source" value="EBGaramond-Regular.ttf"/>
|
||||||
<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">
|
<Property key="Size" value="16"/>
|
||||||
<Code range="33 126"/>
|
<Property key="Resolution" value="72"/>
|
||||||
<Code range="192 382"/><!-- Central and Eastern European languages glyphs -->
|
<Property key="Antialias" value="false"/>
|
||||||
<Code range="1025 1105"/>
|
<Property key="TabWidth" value="8"/>
|
||||||
<Code range="8470 8470" help="№"/>
|
<Property key="CursorWidth" value="2"/>
|
||||||
<Code hide="128"/>
|
<Property key="OffsetHeight" value="0"/>
|
||||||
<Code hide="1026 1039"/>
|
<Codes>
|
||||||
<Code hide="1104"/>
|
<Code range="33 126"/>
|
||||||
</Font>
|
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
|
||||||
|
<Code range="1025 1105"/>
|
||||||
<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="8470"/>
|
||||||
<Code range="33 126"/>
|
<Code hide="128"/>
|
||||||
<Code range="192 382"/><!-- Central and Eastern European glyphs -->
|
<Code hide="1026 1039"/>
|
||||||
<Code range="1025 1105"/>
|
<Code hide="1104"/>
|
||||||
</Font>
|
</Codes>
|
||||||
|
</Resource>
|
||||||
<!-- 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">
|
<Resource type="ResourceTrueTypeFont" name="MonoFont">
|
||||||
<Code range="33 126"/>
|
<Property key="Source" value="VeraMono.ttf"/>
|
||||||
<Code range="192 382"/><!-- Central and Eastern European languages glyphs -->
|
<Property key="Size" value="18"/>
|
||||||
<Code range="1025 1105"/>
|
<Property key="Resolution" value="50"/>
|
||||||
</Font>
|
<Property key="Antialias" value="false"/>
|
||||||
|
<Property key="TabWidth" value="8"/>
|
||||||
<!--
|
<Property key="CursorWidth" value="2"/>
|
||||||
<Font name="gothic" source="gothic.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
<Property key="OffsetHeight" value="0"/>
|
||||||
<Code range="33 126"/>
|
<Codes>
|
||||||
<Code range="1025 1105"/>
|
<Code range="33 126"/>
|
||||||
<Code range="8470 8470" help="№"/>
|
<Code range="192 382"/>
|
||||||
<Code hide="128"/>
|
<Code range="1025 1105"/>
|
||||||
<Code hide="1026 1039"/>
|
<Code range="8470"/>
|
||||||
<Code hide="1104"/>
|
<Code hide="128"/>
|
||||||
</Font>
|
<Code hide="1026 1039"/>
|
||||||
|
<Code hide="1104"/>
|
||||||
<Font name="daedric" source="daedric.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
</Codes>
|
||||||
<Code range="33 98"/>
|
</Resource>
|
||||||
<Code hide="128"/>
|
</MyGUI>
|
||||||
<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>
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -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 +0,0 @@
|
|||||||
*_test
|
|
@ -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 +0,0 @@
|
|||||||
Please specify an MP3 file
|
|
@ -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 +0,0 @@
|
|||||||
Playing cow.wav
|
|
@ -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;
|
|
||||||
}
|
|
@ -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…
Reference in New Issue