implement basic sound engine using OpenAL

This commit is contained in:
Eduardo Bart 2012-04-13 16:54:08 -03:00
parent 9b4115a7e5
commit c4525059ce
25 changed files with 1236 additions and 75 deletions

View File

@ -35,6 +35,8 @@ FIND_PACKAGE(Lua REQUIRED)
FIND_PACKAGE(PhysFS REQUIRED) FIND_PACKAGE(PhysFS REQUIRED)
FIND_PACKAGE(GMP REQUIRED) FIND_PACKAGE(GMP REQUIRED)
FIND_PACKAGE(ZLIB REQUIRED) FIND_PACKAGE(ZLIB REQUIRED)
FIND_PACKAGE(OpenAL REQUIRED)
FIND_PACKAGE(VorbisFile REQUIRED)
# setup compiler options # setup compiler options
SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable") SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable")
@ -114,24 +116,27 @@ ENDIF()
INCLUDE_DIRECTORIES( INCLUDE_DIRECTORIES(
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${OPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR}
${OPENAL_INCLUDE_DIR}
${LUA_INCLUDE_DIR} ${LUA_INCLUDE_DIR}
${PHYSFS_INCLUDE_DIR} ${PHYSFS_INCLUDE_DIR}
${GMP_INCLUDE_DIR} ${GMP_INCLUDE_DIR}
${ZLIB_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
${VORBISFILE_INCLUDE_DIR}
"${CMAKE_CURRENT_LIST_DIR}/.." "${CMAKE_CURRENT_LIST_DIR}/.."
) )
SET(framework_LIBRARIES SET(framework_LIBRARIES
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${OPENGL_LIBRARIES} ${OPENGL_LIBRARIES}
${OPENAL_LIBRARY}
${LUA_LIBRARIES} ${LUA_LIBRARIES}
${PHYSFS_LIBRARY} ${PHYSFS_LIBRARY}
${GMP_LIBRARY} ${GMP_LIBRARY}
${ZLIB_LIBRARY} ${ZLIB_LIBRARY}
${VORBISFILE_LIBRARY}
${ADDITIONAL_LIBRARIES} ${ADDITIONAL_LIBRARIES}
) )
SET(framework_SOURCES ${framework_SOURCES} SET(framework_SOURCES ${framework_SOURCES}
# framework # framework
${CMAKE_CURRENT_LIST_DIR}/application.cpp ${CMAKE_CURRENT_LIST_DIR}/application.cpp
@ -182,6 +187,14 @@ SET(framework_SOURCES ${framework_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/graphics/paintershaderprogram.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/paintershaderprogram.cpp
${CMAKE_CURRENT_LIST_DIR}/graphics/coordsbuffer.cpp ${CMAKE_CURRENT_LIST_DIR}/graphics/coordsbuffer.cpp
# framework sound
${CMAKE_CURRENT_LIST_DIR}/sound/soundsource.cpp
${CMAKE_CURRENT_LIST_DIR}/sound/soundbuffer.cpp
${CMAKE_CURRENT_LIST_DIR}/sound/soundfile.cpp
${CMAKE_CURRENT_LIST_DIR}/sound/soundmanager.cpp
${CMAKE_CURRENT_LIST_DIR}/sound/oggsoundfile.cpp
${CMAKE_CURRENT_LIST_DIR}/sound/streamsoundsource.cpp
# framework otml # framework otml
${CMAKE_CURRENT_LIST_DIR}/otml/otmldocument.cpp ${CMAKE_CURRENT_LIST_DIR}/otml/otmldocument.cpp
${CMAKE_CURRENT_LIST_DIR}/otml/otmlemitter.cpp ${CMAKE_CURRENT_LIST_DIR}/otml/otmlemitter.cpp

View File

@ -33,6 +33,7 @@
#include <framework/graphics/graphics.h> #include <framework/graphics/graphics.h>
#include <framework/graphics/particlemanager.h> #include <framework/graphics/particlemanager.h>
#include <framework/graphics/painter.h> #include <framework/graphics/painter.h>
#include <framework/sound/soundmanager.h>
#include <framework/luascript/luainterface.h> #include <framework/luascript/luainterface.h>
#include <framework/platform/crashhandler.h> #include <framework/platform/crashhandler.h>
@ -64,10 +65,8 @@ Application::~Application()
g_app = nullptr; g_app = nullptr;
} }
void Application::init(const std::vector<std::string>& args, int appFlags) void Application::init(const std::vector<std::string>& args)
{ {
m_appFlags = appFlags;
// capture exit signals // capture exit signals
signal(SIGTERM, exitSignalHandler); signal(SIGTERM, exitSignalHandler);
signal(SIGINT, exitSignalHandler); signal(SIGINT, exitSignalHandler);
@ -83,7 +82,6 @@ void Application::init(const std::vector<std::string>& args, int appFlags)
// initialize resources // initialize resources
g_resources.init(args[0].c_str()); g_resources.init(args[0].c_str());
if(m_appFlags & Fw::AppEnableConfigs) {
// setup configs write directory // setup configs write directory
if(!g_resources.setupWriteDir(m_appName)) if(!g_resources.setupWriteDir(m_appName))
logError("Could not setup write directory"); logError("Could not setup write directory");
@ -91,10 +89,8 @@ void Application::init(const std::vector<std::string>& args, int appFlags)
// load configs // load configs
if(!g_configs.load("/config.otml")) if(!g_configs.load("/config.otml"))
logInfo("Using default configurations."); logInfo("Using default configurations.");
}
// setup platform window // setup platform window
if(m_appFlags & Fw::AppEnableGraphics) {
g_ui.init(); g_ui.init();
g_window.init(); g_window.init();
@ -106,14 +102,15 @@ void Application::init(const std::vector<std::string>& args, int appFlags)
// initialize graphics // initialize graphics
g_graphics.init(); g_graphics.init();
// initialize sound
g_sounds.init();
// fire first resize // fire first resize
resize(g_window.getSize()); resize(g_window.getSize());
// display window when the application starts running // display window when the application starts running
//g_eventDispatcher.addEvent([]{ g_window.show(); }); //g_eventDispatcher.addEvent([]{ g_window.show(); });
}
if(m_appFlags & Fw::AppEnableModules)
g_modules.discoverModulesPath(); g_modules.discoverModulesPath();
m_initialized = true; m_initialized = true;
@ -127,11 +124,9 @@ void Application::terminate()
g_lua.callGlobalField("g_app", "onTerminate"); g_lua.callGlobalField("g_app", "onTerminate");
// hide the window because there is no render anymore // hide the window because there is no render anymore
if(m_appFlags & Fw::AppEnableGraphics)
g_window.hide(); g_window.hide();
// run modules unload events // run modules unload events
if(m_appFlags & Fw::AppEnableModules)
g_modules.unloadModules(); g_modules.unloadModules();
// release remaining lua object references // release remaining lua object references
@ -144,16 +139,16 @@ void Application::terminate()
Connection::terminate(); Connection::terminate();
// terminate graphics // terminate graphics
if(m_appFlags & Fw::AppEnableGraphics) {
g_ui.terminate(); g_ui.terminate();
g_window.terminate(); g_window.terminate();
}
// terminate sound
g_sounds.terminate();
// flush remaining dispatcher events // flush remaining dispatcher events
g_eventDispatcher.flush(); g_eventDispatcher.flush();
// save configurations // save configurations
if(m_appFlags & Fw::AppEnableConfigs)
g_configs.save(); g_configs.save();
// release resources // release resources
@ -186,7 +181,7 @@ void Application::run()
// poll all events before rendering // poll all events before rendering
poll(); poll();
if(m_appFlags & Fw::AppEnableGraphics && g_window.isVisible()) { if(g_window.isVisible()) {
g_graphics.beginRender(); g_graphics.beginRender();
render(); render();
g_graphics.endRender(); g_graphics.endRender();
@ -214,11 +209,9 @@ void Application::exit()
void Application::poll() void Application::poll()
{ {
if(m_appFlags & Fw::AppEnableGraphics) {
// poll input events // poll input events
g_window.poll(); g_window.poll();
//g_particleManager.update(); //g_particleManager.update();
}
Connection::poll(); Connection::poll();
g_eventDispatcher.poll(); g_eventDispatcher.poll();

View File

@ -35,7 +35,7 @@ public:
Application(const std::string& appName); Application(const std::string& appName);
~Application(); ~Application();
virtual void init(const std::vector<std::string>& args, int appFlags); virtual void init(const std::vector<std::string>& args);
virtual void registerLuaFunctions(); virtual void registerLuaFunctions();
virtual void terminate(); virtual void terminate();
virtual void run(); virtual void run();
@ -64,7 +64,6 @@ protected:
std::string m_appName; std::string m_appName;
std::string m_appVersion; std::string m_appVersion;
std::string m_appBuildDate; std::string m_appBuildDate;
int m_appFlags;
int m_frameSleep; int m_frameSleep;
Boolean<false> m_initialized; Boolean<false> m_initialized;
Boolean<false> m_running; Boolean<false> m_running;

View File

@ -0,0 +1,10 @@
# Try to find the VORBISFILE library
# VORBISFILE_FOUND - system has VORBISFILE
# VORBISFILE_INCLUDE_DIR - the VORBISFILE include directory
# VORBISFILE_LIBRARY - the VORBISFILE library
FIND_PATH(VORBISFILE_INCLUDE_DIR NAMES vorbis/vorbisfile.h)
FIND_LIBRARY(VORBISFILE_LIBRARY NAMES vorbisfile)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(VORBISFILE DEFAULT_MSG VORBISFILE_LIBRARY VORBISFILE_INCLUDE_DIR)
MARK_AS_ADVANCED(VORBISFILE_LIBRARY VORBISFILE_INCLUDE_DIR)

View File

@ -264,13 +264,6 @@ namespace Fw
DraggingState = 2048, DraggingState = 2048,
LastWidgetState = 4096 LastWidgetState = 4096
}; };
enum AppicationFlags {
AppEnableModules = 1,
AppEnableGraphics = 2,
AppEnableConfigs = 4,
AppEnableAll = AppEnableModules | AppEnableGraphics | AppEnableConfigs
};
} }
#endif #endif

View File

@ -32,15 +32,20 @@ Clock::Clock()
m_startupTime = std::chrono::high_resolution_clock::now(); m_startupTime = std::chrono::high_resolution_clock::now();
m_currentTicks = 0; m_currentTicks = 0;
} }
ticks_t Clock::updateTicks() ticks_t Clock::updateTicks()
{ {
auto timeNow = std::chrono::high_resolution_clock::now(); m_currentTicks = asyncTicks();
m_currentTicks = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow - m_startupTime).count();
m_currentTime = m_currentTicks/1000.0f; m_currentTime = m_currentTicks/1000.0f;
return m_currentTicks; return m_currentTicks;
} }
ticks_t Clock::asyncTicks()
{
auto timeNow = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(timeNow - m_startupTime).count();
}
void Clock::sleep(int ms) void Clock::sleep(int ms)
{ {
usleep(ms * 1000); usleep(ms * 1000);

View File

@ -33,10 +33,12 @@ public:
ticks_t updateTicks(); ticks_t updateTicks();
void sleep(int ms); void sleep(int ms);
ticks_t asyncTicks();
ticks_t ticks() { return m_currentTicks; } ticks_t ticks() { return m_currentTicks; }
ticks_t ticksElapsed(long prevTicks) { return m_currentTicks - prevTicks; } ticks_t ticksElapsed(long prevTicks) { return m_currentTicks - prevTicks; }
ticks_t ticksFor(int delay) { return m_currentTicks + delay; } ticks_t ticksFor(int delay) { return m_currentTicks + delay; }
float asyncTime() { return asyncTicks()/1000.0f; }
float time() { return m_currentTime; } float time() { return m_currentTime; }
float timeElapsed(float prevTime) { return m_currentTime - prevTime; } float timeElapsed(float prevTime) { return m_currentTime - prevTime; }
float timeFor(float delay) { return m_currentTime + delay; } float timeFor(float delay) { return m_currentTime + delay; }

View File

@ -35,38 +35,53 @@ FileStream::~FileStream()
close(); close();
} }
void FileStream::close() bool FileStream::close()
{ {
if(m_fileHandle) { if(m_fileHandle) {
if(PHYSFS_isInit() && PHYSFS_close(m_fileHandle) == 0) if(PHYSFS_isInit() && PHYSFS_close(m_fileHandle) == 0)
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
m_fileHandle = nullptr; m_fileHandle = nullptr;
return true;
} }
return false;
} }
void FileStream::flush() bool FileStream::flush()
{ {
if(PHYSFS_flush(m_fileHandle) == 0) if(PHYSFS_flush(m_fileHandle) == 0) {
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
return false;
}
return true;
} }
void FileStream::read(void *buffer, uint count) int FileStream::read(void *buffer, int size, int nmemb)
{ {
if(PHYSFS_read(m_fileHandle, buffer, 1, count) != count) int res = PHYSFS_read(m_fileHandle, buffer, size, nmemb);
if(res == -1) {
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
return 0;
}
return res;
} }
void FileStream::write(void *buffer, uint count) bool FileStream::write(void *buffer, int count)
{ {
if(PHYSFS_write(m_fileHandle, buffer, 1, count) != count) if(PHYSFS_write(m_fileHandle, buffer, 1, count) != count) {
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
return false;
}
return true;
} }
void FileStream::seek(uint pos) bool FileStream::seek(int pos)
{ {
if(PHYSFS_seek(m_fileHandle, pos) == 0) if(PHYSFS_seek(m_fileHandle, pos) == 0) {
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
return false;
}
return true;
} }
int FileStream::size() int FileStream::size()
@ -74,6 +89,11 @@ int FileStream::size()
return PHYSFS_fileLength(m_fileHandle); return PHYSFS_fileLength(m_fileHandle);
} }
int FileStream::tell()
{
return PHYSFS_tell(m_fileHandle);
}
uint8 FileStream::getU8() uint8 FileStream::getU8()
{ {
uint8 v = 0; uint8 v = 0;

View File

@ -37,12 +37,13 @@ protected:
public: public:
~FileStream(); ~FileStream();
void close(); bool close();
void flush(); bool flush();
void write(void *buffer, uint count); bool write(void *buffer, int count);
void read(void *buffer, uint count); int read(void *buffer, int size, int nmemb = 1);
void seek(uint pos); bool seek(int pos);
int size(); int size();
int tell();
std::string name() { return m_name; } std::string name() { return m_name; }
std::string readAll(); std::string readAll();

View File

@ -55,6 +55,9 @@
#include <unordered_map> #include <unordered_map>
#include <random> #include <random>
#include <chrono> #include <chrono>
#include <thread>
#include <mutex>
#include <atomic>
// boost utilities // boost utilities
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>

View File

@ -600,19 +600,26 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
switch(wParam) { switch(wParam) {
case SIZE_MAXIMIZED: case SIZE_MAXIMIZED:
m_maximized = true; m_maximized = true;
m_visible = true;
break; break;
case SIZE_RESTORED: case SIZE_RESTORED:
m_maximized = false; m_maximized = false;
m_visible = true;
break;
case SIZE_MINIMIZED:
m_visible = false;
break; break;
} }
m_visible = !(wParam == SIZE_MINIMIZED); Size size;
if(m_visible) { size.setWidth(std::max(std::min((int)LOWORD(lParam), 7680), m_minimumSize.width()));
m_size.setWidth(LOWORD(lParam)); size.setHeight(std::max(std::min((int)HIWORD(lParam), 4320), m_minimumSize.height()));
m_size.setHeight(HIWORD(lParam));
if(m_visible && m_size != size) {
m_size = size;
m_onResize(m_size); m_onResize(m_size);
} }
break; break;
} }
default: default:

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FRAMEWORK_SOUND_DECLARATIONS_H
#define FRAMEWORK_SOUND_DECLARATIONS_H
#include <framework/global.h>
#include <AL/al.h>
#include <AL/alc.h>
class SoundManager;
class SoundSource;
class SoundBuffer;
class SoundFile;
class StreamSoundSource;
class OggSoundFile;
typedef std::shared_ptr<SoundSource> SoundSourcePtr;
typedef std::shared_ptr<SoundFile> SoundFilePtr;
typedef std::shared_ptr<SoundBuffer> SoundBufferPtr;
typedef std::shared_ptr<StreamSoundSource> StreamSoundSourcePtr;
typedef std::shared_ptr<OggSoundFile> OggSoundFilePtr;
#endif

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "oggsoundfile.h"
OggSoundFile::OggSoundFile(const FileStreamPtr& fileStream) : SoundFile(fileStream)
{
memset(&m_vorbisFile, 0, sizeof(m_vorbisFile));
}
OggSoundFile::~OggSoundFile()
{
ov_clear(&m_vorbisFile);
}
bool OggSoundFile::prepareOgg()
{
ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
ov_open_callbacks(m_file.get(), &m_vorbisFile, 0, 0, callbacks);
vorbis_info* vi = ov_info(&m_vorbisFile, -1);
if(!vi) {
logError("ogg file not supported: ", m_file->name());
return false;
}
m_channels = vi->channels;
m_rate = vi->rate;
m_bps = 16;
m_size = ov_pcm_total(&m_vorbisFile, -1) * 2;
return true;
}
int OggSoundFile::read(void *buffer, int bufferSize)
{
char* bytesBuffer = reinterpret_cast<char*>(buffer);
int section = 0;
size_t totalBytesRead = 0;
while(bufferSize > 0) {
size_t bytesToRead = bufferSize;
long bytesRead = ov_read(&m_vorbisFile, bytesBuffer, bytesToRead, 0, 2, 1, &section);
if(bytesRead == 0)
break;
bufferSize -= bytesRead;
bytesBuffer += bytesRead;
totalBytesRead += bytesRead;
}
return totalBytesRead;
}
void OggSoundFile::reset()
{
ov_pcm_seek(&m_vorbisFile, 0);
}
size_t OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
{
FileStream *file = static_cast<FileStream*>(source);
return file->read(ptr, size, nmemb);
}
int OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
{
FileStream *file = static_cast<FileStream*>(source);
switch(whence) {
case SEEK_SET:
if(file->seek(offset))
return 0;
break;
case SEEK_CUR:
if(file->seek(file->tell() + offset))
return 0;
break;
case SEEK_END:
if(file->seek(file->size() + offset))
return 0;
break;
}
return -1;
}
int OggSoundFile::cb_close(void* source)
{
FileStream *file = static_cast<FileStream*>(source);
file->close();
return 0;
}
long OggSoundFile::cb_tell(void* source)
{
FileStream *file = static_cast<FileStream*>(source);
return file->tell();
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef OGGSOUNDFILE_H
#define OGGSOUNDFILE_H
#include "soundfile.h"
#include <vorbis/vorbisfile.h>
class OggSoundFile : public SoundFile
{
public:
OggSoundFile(const FileStreamPtr& fileStream);
virtual ~OggSoundFile();
bool prepareOgg();
int read(void *buffer, int bufferSize);
void reset();
private:
static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
static int cb_seek(void* source, ogg_int64_t offset, int whence);
static int cb_close(void* source);
static long cb_tell(void* source);
OggVorbis_File m_vorbisFile;
};
#endif

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundbuffer.h"
#include "soundfile.h"
#include <framework/util/databuffer.h>
SoundBuffer::SoundBuffer()
{
m_bufferId = 0;
alGenBuffers(1, &m_bufferId);
assert(alGetError() == AL_NO_ERROR);
}
SoundBuffer::~SoundBuffer()
{
alDeleteBuffers(1, &m_bufferId);
assert(alGetError() == AL_NO_ERROR);
}
bool SoundBuffer::loadSoundFile(const SoundFilePtr& soundFile)
{
ALenum format = soundFile->getSampleFormat();
if(format == AL_UNDETERMINED) {
logError("unable to determine sample format for '", soundFile->getName(), "'");
return false;
}
DataBuffer<char> samples(soundFile->getSize());
int read = soundFile->read(&samples[0], soundFile->getSize());
if(read <= 0) {
logError("unable to fill audio buffer data for '", soundFile->getName(), "'");
return false;
}
alBufferData(m_bufferId, format, &samples[0], soundFile->getSize(), soundFile->getRate());
ALenum err = alGetError();
if(err != AL_NO_ERROR) {
logError("unable to fill audio buffer data for '", soundFile->getName(), "': ", alGetString(err));
return false;
}
return true;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDBUFFER_H
#define SOUNDBUFFER_H
#include "declarations.h"
class SoundBuffer
{
public:
SoundBuffer();
~SoundBuffer();
bool loadSoundFile(const SoundFilePtr& soundFile);
int getBufferId() { return m_bufferId; }
private:
ALuint m_bufferId;
};
#endif

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundfile.h"
#include "oggsoundfile.h"
#include <framework/core/resourcemanager.h>
SoundFile::SoundFile(const FileStreamPtr& fileStream)
{
m_file = fileStream;
}
SoundFilePtr SoundFile::loadSoundFile(const std::string& filename)
{
FileStreamPtr file = g_resources.openFile(filename);
if(!file) {
logTraceError("unable to open ", filename);
return nullptr;
}
char magic[4];
file->read(magic, 4);
file->seek(0);
SoundFilePtr soundFile;
if(strncmp(magic, "OggS", 4) == 0) {
OggSoundFilePtr oggSoundFile = OggSoundFilePtr(new OggSoundFile(file));
if(oggSoundFile->prepareOgg())
soundFile = oggSoundFile;
} else
logError("unknown sound file format ", filename);
return soundFile;
}
ALenum SoundFile::getSampleFormat()
{
if(m_channels == 2) {
if(m_bps == 16)
return AL_FORMAT_STEREO16;
else if(m_bps == 8)
return AL_FORMAT_STEREO8;
} else if(m_channels == 1) {
if(m_bps == 16)
return AL_FORMAT_MONO16;
else if(m_bps == 8)
return AL_FORMAT_MONO8;
}
return AL_UNDETERMINED;
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDFILE_H
#define SOUNDFILE_H
#include "declarations.h"
#include <framework/core/filestream.h>
class SoundFile
{
public:
SoundFile(const FileStreamPtr& fileStream);
virtual ~SoundFile() { }
static SoundFilePtr loadSoundFile(const std::string& filename);
virtual int read(void *buffer, int bufferSize) = 0;
virtual void reset() = 0;
ALenum getSampleFormat();
int getChannels() { return m_channels; }
int getRate() { return m_rate; }
int getBps() { return m_bps; }
int getSize() { return m_size; }
std::string getName() { return m_file ? m_file->name() : std::string(); }
protected:
FileStreamPtr m_file;
int m_channels;
int m_rate;
int m_bps;
int m_size;
};
#endif

View File

@ -0,0 +1,245 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundmanager.h"
#include "soundsource.h"
#include "soundbuffer.h"
#include "soundfile.h"
#include "streamsoundsource.h"
#include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h>
SoundManager g_sounds;
void SoundManager::init()
{
m_run = false;
m_device = alcOpenDevice(NULL);
if(!m_device) {
logError("unable to open audio device");
return;
}
m_context = alcCreateContext(m_device, NULL);
if(!m_context) {
logError("unable to create audio context: ", alcGetString(m_device, alcGetError(m_device)));
return;
}
alcMakeContextCurrent(m_context);
m_thread = std::thread(std::bind(&SoundManager::audioThread, &g_sounds));
while(!m_run)
g_clock.sleep(1);
m_musicEnabled = true;
m_soundEnabled = true;
/*
g_eventDispatcher.scheduleEvent([this] {
play("/test.ogg");
}, 10);
*/
}
void SoundManager::terminate()
{
m_run = false;
m_thread.join();
m_sources.clear();
m_buffers.clear();
m_musicSource = nullptr;
m_currentMusic = "";
m_musicEnabled = false;
m_soundEnabled = false;
alcMakeContextCurrent(NULL);
if(m_context) {
alcDestroyContext(m_context);
m_context = nullptr;
}
if(m_device) {
alcCloseDevice(m_device);
m_device = nullptr;
}
}
void SoundManager::audioThread()
{
m_run = true;
while(m_run) {
//TODO: use condition variable
g_clock.sleep(30);
update();
}
}
void SoundManager::update()
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
static ticks_t lastUpdate = 0;
ticks_t now = g_clock.asyncTicks();
if(now - lastUpdate < 300)
return;
lastUpdate = now;
for(auto it = m_sources.begin(); it != m_sources.end();) {
SoundSourcePtr source = *it;
source->update();
if(!source->isPlaying())
it = m_sources.erase(it);
else
++it;
}
if(m_musicSource) {
m_musicSource->update();
if(!m_musicSource->isPlaying())
m_musicSource = nullptr;
}
if(m_context) {
alcProcessContext(m_context);
}
}
void SoundManager::preload(const std::string& filename)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto it = m_buffers.find(filename);
if(it != m_buffers.end())
return;
SoundFilePtr soundFile = SoundFile::loadSoundFile(filename);
// only keep small files
if(soundFile->getSize() > MAX_CACHE_SIZE)
return;
SoundBufferPtr buffer = SoundBufferPtr(new SoundBuffer);
if(buffer->loadSoundFile(soundFile))
m_buffers[filename] = buffer;
}
void SoundManager::enableSound(bool enable)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(!isAudioEnabled())
return;
}
void SoundManager::play(const std::string& filename)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(!m_soundEnabled)
return;
SoundSourcePtr soundSource = createSoundSource(filename);
if(!soundSource) {
logError("unable to play '", filename, "'");
return;
}
soundSource->setRelative(true);
soundSource->play();
m_sources.push_back(soundSource);
}
void SoundManager::enableMusic(bool enable)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(!isAudioEnabled())
return;
m_musicEnabled = enable;
if(enable)
playMusic(m_currentMusic);
else
m_musicSource = nullptr;
}
void SoundManager::playMusic(const std::string& filename, bool fade)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(m_currentMusic == filename && m_musicSource)
return;
m_currentMusic = filename;
if(!m_musicEnabled)
return;
if(filename.empty()) {
m_musicSource = nullptr;
return;
}
}
void SoundManager::stopMusic(float fadetime)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
}
SoundSourcePtr SoundManager::createSoundSource(const std::string& filename)
{
SoundSourcePtr soundSource;
auto it = m_buffers.find(filename);
if(it != m_buffers.end()) {
soundSource = SoundSourcePtr(new SoundSource);
soundSource->setBuffer(it->second);
} else {
SoundFilePtr soundFile = SoundFile::loadSoundFile(filename);
if(!soundFile)
return nullptr;
if(soundFile->getSize() <= MAX_CACHE_SIZE) {
soundSource = SoundSourcePtr(new SoundSource);
SoundBufferPtr buffer = SoundBufferPtr(new SoundBuffer);
buffer->loadSoundFile(soundFile);
soundSource->setBuffer(buffer);
m_buffers[filename] = buffer;
logWarning("uncached sound '", filename, "' requested to be played");
} else {
StreamSoundSourcePtr streamSoundSource(new StreamSoundSource);
streamSoundSource->setSoundFile(soundFile);
soundSource = streamSoundSource;
}
}
return soundSource;
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H
#include "declarations.h"
class SoundManager
{
enum {
MAX_CACHE_SIZE = 10000000
};
public:
void init();
void terminate();
void audioThread();
void update();
void preload(const std::string& filename);
void enableSound(bool enable);
void play(const std::string& filename);
void enableMusic(bool enable);
void playMusic(const std::string& filename, bool fade = false);
void stopMusic(float fadetime = 0);
bool isMusicEnabled() { return m_musicEnabled; }
bool isSoundEnabled() { return m_soundEnabled; }
bool isAudioEnabled() { return m_device && m_context; }
std::string getCurrentMusic() { return m_currentMusic; }
private:
SoundSourcePtr createSoundSource(const std::string& filename);
ALuint loadFileIntoBuffer(const SoundFilePtr& soundFile);
std::map<std::string, SoundBufferPtr> m_buffers;
std::vector<SoundSourcePtr> m_sources;
StreamSoundSourcePtr m_musicSource;
ALCdevice *m_device;
ALCcontext *m_context;
std::thread m_thread;
std::atomic<bool> m_run;
std::recursive_mutex m_mutex;
Boolean<false> m_musicEnabled;
Boolean<false> m_soundEnabled;
std::string m_currentMusic;
};
extern SoundManager g_sounds;
#endif

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundsource.h"
#include "soundbuffer.h"
SoundSource::SoundSource()
{
m_sourceId = 0;
alGenSources(1, &m_sourceId);
assert(alGetError() == AL_NO_ERROR);
setReferenceDistance(128);
}
SoundSource::~SoundSource()
{
stop();
alDeleteSources(1, &m_sourceId);
assert(alGetError() == AL_NO_ERROR);
}
void SoundSource::play()
{
alSourcePlay(m_sourceId);
assert(alGetError() == AL_NO_ERROR);
}
void SoundSource::stop()
{
alSourceStop(m_sourceId);
assert(alGetError() == AL_NO_ERROR);
if(m_buffer) {
alSourcei(m_sourceId, AL_BUFFER, AL_NONE);
assert(alGetError() == AL_NO_ERROR);
m_buffer = nullptr;
}
}
bool SoundSource::isPlaying()
{
ALint state = AL_PLAYING;
alGetSourcei(m_sourceId, AL_SOURCE_STATE, &state);
return state != AL_STOPPED;
}
void SoundSource::setBuffer(const SoundBufferPtr& buffer)
{
alSourcei(m_sourceId, AL_BUFFER, buffer->getBufferId());
assert(alGetError() == AL_NO_ERROR);
m_buffer = buffer;
}
void SoundSource::setLooping(bool looping)
{
alSourcei(m_sourceId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
}
void SoundSource::setRelative(bool relative)
{
alSourcei(m_sourceId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
}
void SoundSource::setReferenceDistance(float distance)
{
alSourcef(m_sourceId, AL_REFERENCE_DISTANCE, distance);
}
void SoundSource::setGain(float gain)
{
alSourcef(m_sourceId, AL_GAIN, gain);
}
void SoundSource::setPitch(float pitch)
{
alSourcef(m_sourceId, AL_PITCH, pitch);
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDSOURCE_H
#define SOUNDSOURCE_H
#include "declarations.h"
class SoundSource
{
public:
SoundSource();
virtual ~SoundSource();
void play();
void stop();
bool isPlaying();
void setBuffer(const SoundBufferPtr& buffer);
virtual void setLooping(bool looping);
void setRelative(bool relative);
void setReferenceDistance(float distance);
void setGain(float gain);
void setPitch(float pitch);
// TODO: velocity, position
protected:
virtual void update() { }
friend class SoundManager;
ALuint m_sourceId;
SoundBufferPtr m_buffer;
};
#endif

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "streamsoundsource.h"
#include "soundbuffer.h"
#include "soundfile.h"
#include <framework/util/databuffer.h>
#include <framework/core/clock.h>
StreamSoundSource::StreamSoundSource()
{
for(auto& buffer : m_buffers)
buffer = SoundBufferPtr(new SoundBuffer);
m_fadeState = NoFading;
}
StreamSoundSource::~StreamSoundSource()
{
stop();
ALint queued;
alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queued);
for(int i = 0; i < queued; ++i) {
ALuint buffer;
alSourceUnqueueBuffers(m_sourceId, 1, &buffer);
}
m_buffers.fill(nullptr);
}
void StreamSoundSource::setSoundFile(const SoundFilePtr& soundFile)
{
m_soundFile = soundFile;
ALint queued;
alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queued);
for(int i = 0; i < STREAM_FRAGMENTS - queued; ++i) {
if(!fillBufferAndQueue(m_buffers[i]->getBufferId()))
break;
}
}
void StreamSoundSource::update()
{
ALint processed = 0;
alGetSourcei(m_sourceId, AL_BUFFERS_PROCESSED, &processed);
for(ALint i = 0; i < processed; ++i) {
ALuint buffer;
alSourceUnqueueBuffers(m_sourceId, 1, &buffer);
//SoundManager::check_al_error("Couldn't unqueue audio buffer: ");
if(!fillBufferAndQueue(buffer))
break;
}
if(!isPlaying()) {
if(processed == 0 || !m_looping)
return;
// we might have to restart the source if we had a buffer underrun
//log_info << "Restarting audio source because of buffer underrun" << std::endl;
//play();
}
float realTime = g_clock.asyncTime();
if(m_fadeState == FadingOn) {
float time = realTime - m_fadeStartTime;
if(time >= m_fadeTime) {
setGain(1.0);
m_fadeState = NoFading;
} else {
setGain(time / m_fadeTime);
}
} else if(m_fadeState == FadingOff) {
float time = realTime - m_fadeStartTime;
if(time >= m_fadeTime) {
stop();
m_fadeState = NoFading;
} else {
setGain((m_fadeTime - time) / m_fadeTime);
}
}
}
bool StreamSoundSource::fillBufferAndQueue(ALuint buffer)
{
// fill buffer
DataBuffer<char> bufferData(STREAM_FRAGMENT_SIZE);
int bytesRead = 0;
do {
bytesRead += m_soundFile->read(&bufferData[bytesRead], STREAM_FRAGMENT_SIZE - bytesRead);
// end of sound file
if(bytesRead < STREAM_FRAGMENT_SIZE) {
if(m_looping)
m_soundFile->reset();
else
break;
}
} while(bytesRead < STREAM_FRAGMENT_SIZE);
if(bytesRead > 0) {
ALenum format = m_soundFile->getSampleFormat();
alBufferData(buffer, format, &bufferData[0], bytesRead, m_soundFile->getRate());
ALenum err = alGetError();
if(err != AL_NO_ERROR)
logError("unable to refill audio buffer for '", m_soundFile->getName(), "': ", alGetString(err));
alSourceQueueBuffers(m_sourceId, 1, &buffer);
err = alGetError();
if(err != AL_NO_ERROR)
logError("unable to queue audio buffer for '", m_soundFile->getName(), "': ", alGetString(err));
}
// return false if there aren't more buffers to fill
return bytesRead >= STREAM_FRAGMENT_SIZE;
}
void StreamSoundSource::setFading(FadeState state, float fadeTime)
{
m_fadeState = state;
m_fadeTime = fadeTime;
m_fadeStartTime = g_clock.asyncTime();
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef STREAMSOUNDSOURCE_H
#define STREAMSOUNDSOURCE_H
#include "soundsource.h"
class StreamSoundSource : public SoundSource
{
enum {
STREAM_BUFFER_SIZE = 1024 * 500,
STREAM_FRAGMENTS = 5,
STREAM_FRAGMENT_SIZE = STREAM_BUFFER_SIZE / STREAM_FRAGMENTS
};
public:
enum FadeState { NoFading, FadingOn, FadingOff };
StreamSoundSource();
virtual ~StreamSoundSource();
void setSoundFile(const SoundFilePtr& soundFile);
void setFading(FadeState state, float fadetime);
FadeState getFadeState() { return m_fadeState; }
void update();
private:
bool fillBufferAndQueue(ALuint buffer);
SoundFilePtr m_soundFile;
std::array<SoundBufferPtr,STREAM_FRAGMENTS> m_buffers;
FadeState m_fadeState;
float m_fadeStartTime;
float m_fadeTime;
Boolean<false> m_looping;
};
#endif

View File

@ -79,7 +79,7 @@ void OTClient::init(const std::vector<std::string>& args)
logInfo("Startup options:", startupOptions); logInfo("Startup options:", startupOptions);
g_logger.setLogFile(Fw::formatString("%s.txt", Otc::AppCompactName)); g_logger.setLogFile(Fw::formatString("%s.txt", Otc::AppCompactName));
Application::init(args, Fw::AppEnableAll); Application::init(args);
g_modules.discoverModules(); g_modules.discoverModules();