implement basic sound engine using OpenAL
This commit is contained in:
parent
9b4115a7e5
commit
c4525059ce
|
@ -35,6 +35,8 @@ FIND_PACKAGE(Lua REQUIRED)
|
|||
FIND_PACKAGE(PhysFS REQUIRED)
|
||||
FIND_PACKAGE(GMP REQUIRED)
|
||||
FIND_PACKAGE(ZLIB REQUIRED)
|
||||
FIND_PACKAGE(OpenAL REQUIRED)
|
||||
FIND_PACKAGE(VorbisFile REQUIRED)
|
||||
|
||||
# setup compiler options
|
||||
SET(CXX_WARNS "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-but-set-variable")
|
||||
|
@ -114,24 +116,27 @@ ENDIF()
|
|||
INCLUDE_DIRECTORIES(
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${OPENGL_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${LUA_INCLUDE_DIR}
|
||||
${PHYSFS_INCLUDE_DIR}
|
||||
${GMP_INCLUDE_DIR}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${VORBISFILE_INCLUDE_DIR}
|
||||
"${CMAKE_CURRENT_LIST_DIR}/.."
|
||||
)
|
||||
|
||||
SET(framework_LIBRARIES
|
||||
${Boost_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
${OPENAL_LIBRARY}
|
||||
${LUA_LIBRARIES}
|
||||
${PHYSFS_LIBRARY}
|
||||
${GMP_LIBRARY}
|
||||
${ZLIB_LIBRARY}
|
||||
${VORBISFILE_LIBRARY}
|
||||
${ADDITIONAL_LIBRARIES}
|
||||
)
|
||||
|
||||
|
||||
SET(framework_SOURCES ${framework_SOURCES}
|
||||
# framework
|
||||
${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/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
|
||||
${CMAKE_CURRENT_LIST_DIR}/otml/otmldocument.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/otml/otmlemitter.cpp
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <framework/graphics/graphics.h>
|
||||
#include <framework/graphics/particlemanager.h>
|
||||
#include <framework/graphics/painter.h>
|
||||
#include <framework/sound/soundmanager.h>
|
||||
#include <framework/luascript/luainterface.h>
|
||||
#include <framework/platform/crashhandler.h>
|
||||
|
||||
|
@ -64,10 +65,8 @@ Application::~Application()
|
|||
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
|
||||
signal(SIGTERM, exitSignalHandler);
|
||||
signal(SIGINT, exitSignalHandler);
|
||||
|
@ -83,7 +82,6 @@ void Application::init(const std::vector<std::string>& args, int appFlags)
|
|||
// initialize resources
|
||||
g_resources.init(args[0].c_str());
|
||||
|
||||
if(m_appFlags & Fw::AppEnableConfigs) {
|
||||
// setup configs write directory
|
||||
if(!g_resources.setupWriteDir(m_appName))
|
||||
logError("Could not setup write directory");
|
||||
|
@ -91,10 +89,8 @@ void Application::init(const std::vector<std::string>& args, int appFlags)
|
|||
// load configs
|
||||
if(!g_configs.load("/config.otml"))
|
||||
logInfo("Using default configurations.");
|
||||
}
|
||||
|
||||
// setup platform window
|
||||
if(m_appFlags & Fw::AppEnableGraphics) {
|
||||
g_ui.init();
|
||||
|
||||
g_window.init();
|
||||
|
@ -106,14 +102,15 @@ void Application::init(const std::vector<std::string>& args, int appFlags)
|
|||
// initialize graphics
|
||||
g_graphics.init();
|
||||
|
||||
// initialize sound
|
||||
g_sounds.init();
|
||||
|
||||
// fire first resize
|
||||
resize(g_window.getSize());
|
||||
|
||||
// display window when the application starts running
|
||||
//g_eventDispatcher.addEvent([]{ g_window.show(); });
|
||||
}
|
||||
|
||||
if(m_appFlags & Fw::AppEnableModules)
|
||||
g_modules.discoverModulesPath();
|
||||
|
||||
m_initialized = true;
|
||||
|
@ -127,11 +124,9 @@ void Application::terminate()
|
|||
g_lua.callGlobalField("g_app", "onTerminate");
|
||||
|
||||
// hide the window because there is no render anymore
|
||||
if(m_appFlags & Fw::AppEnableGraphics)
|
||||
g_window.hide();
|
||||
|
||||
// run modules unload events
|
||||
if(m_appFlags & Fw::AppEnableModules)
|
||||
g_modules.unloadModules();
|
||||
|
||||
// release remaining lua object references
|
||||
|
@ -144,16 +139,16 @@ void Application::terminate()
|
|||
Connection::terminate();
|
||||
|
||||
// terminate graphics
|
||||
if(m_appFlags & Fw::AppEnableGraphics) {
|
||||
g_ui.terminate();
|
||||
g_window.terminate();
|
||||
}
|
||||
|
||||
// terminate sound
|
||||
g_sounds.terminate();
|
||||
|
||||
// flush remaining dispatcher events
|
||||
g_eventDispatcher.flush();
|
||||
|
||||
// save configurations
|
||||
if(m_appFlags & Fw::AppEnableConfigs)
|
||||
g_configs.save();
|
||||
|
||||
// release resources
|
||||
|
@ -186,7 +181,7 @@ void Application::run()
|
|||
// poll all events before rendering
|
||||
poll();
|
||||
|
||||
if(m_appFlags & Fw::AppEnableGraphics && g_window.isVisible()) {
|
||||
if(g_window.isVisible()) {
|
||||
g_graphics.beginRender();
|
||||
render();
|
||||
g_graphics.endRender();
|
||||
|
@ -214,11 +209,9 @@ void Application::exit()
|
|||
|
||||
void Application::poll()
|
||||
{
|
||||
if(m_appFlags & Fw::AppEnableGraphics) {
|
||||
// poll input events
|
||||
g_window.poll();
|
||||
//g_particleManager.update();
|
||||
}
|
||||
|
||||
Connection::poll();
|
||||
g_eventDispatcher.poll();
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
Application(const std::string& appName);
|
||||
~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 terminate();
|
||||
virtual void run();
|
||||
|
@ -64,7 +64,6 @@ protected:
|
|||
std::string m_appName;
|
||||
std::string m_appVersion;
|
||||
std::string m_appBuildDate;
|
||||
int m_appFlags;
|
||||
int m_frameSleep;
|
||||
Boolean<false> m_initialized;
|
||||
Boolean<false> m_running;
|
||||
|
|
|
@ -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)
|
|
@ -264,13 +264,6 @@ namespace Fw
|
|||
DraggingState = 2048,
|
||||
LastWidgetState = 4096
|
||||
};
|
||||
|
||||
enum AppicationFlags {
|
||||
AppEnableModules = 1,
|
||||
AppEnableGraphics = 2,
|
||||
AppEnableConfigs = 4,
|
||||
AppEnableAll = AppEnableModules | AppEnableGraphics | AppEnableConfigs
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,15 +32,20 @@ Clock::Clock()
|
|||
m_startupTime = std::chrono::high_resolution_clock::now();
|
||||
m_currentTicks = 0;
|
||||
}
|
||||
|
||||
ticks_t Clock::updateTicks()
|
||||
{
|
||||
auto timeNow = std::chrono::high_resolution_clock::now();
|
||||
m_currentTicks = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow - m_startupTime).count();
|
||||
m_currentTicks = asyncTicks();
|
||||
m_currentTime = m_currentTicks/1000.0f;
|
||||
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)
|
||||
{
|
||||
usleep(ms * 1000);
|
||||
|
|
|
@ -33,10 +33,12 @@ public:
|
|||
ticks_t updateTicks();
|
||||
void sleep(int ms);
|
||||
|
||||
ticks_t asyncTicks();
|
||||
ticks_t ticks() { return m_currentTicks; }
|
||||
ticks_t ticksElapsed(long prevTicks) { return m_currentTicks - prevTicks; }
|
||||
ticks_t ticksFor(int delay) { return m_currentTicks + delay; }
|
||||
|
||||
float asyncTime() { return asyncTicks()/1000.0f; }
|
||||
float time() { return m_currentTime; }
|
||||
float timeElapsed(float prevTime) { return m_currentTime - prevTime; }
|
||||
float timeFor(float delay) { return m_currentTime + delay; }
|
||||
|
|
|
@ -35,38 +35,53 @@ FileStream::~FileStream()
|
|||
close();
|
||||
}
|
||||
|
||||
void FileStream::close()
|
||||
bool FileStream::close()
|
||||
{
|
||||
if(m_fileHandle) {
|
||||
if(PHYSFS_isInit() && PHYSFS_close(m_fileHandle) == 0)
|
||||
logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError());
|
||||
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int FileStream::size()
|
||||
|
@ -74,6 +89,11 @@ int FileStream::size()
|
|||
return PHYSFS_fileLength(m_fileHandle);
|
||||
}
|
||||
|
||||
int FileStream::tell()
|
||||
{
|
||||
return PHYSFS_tell(m_fileHandle);
|
||||
}
|
||||
|
||||
uint8 FileStream::getU8()
|
||||
{
|
||||
uint8 v = 0;
|
||||
|
|
|
@ -37,12 +37,13 @@ protected:
|
|||
public:
|
||||
~FileStream();
|
||||
|
||||
void close();
|
||||
void flush();
|
||||
void write(void *buffer, uint count);
|
||||
void read(void *buffer, uint count);
|
||||
void seek(uint pos);
|
||||
bool close();
|
||||
bool flush();
|
||||
bool write(void *buffer, int count);
|
||||
int read(void *buffer, int size, int nmemb = 1);
|
||||
bool seek(int pos);
|
||||
int size();
|
||||
int tell();
|
||||
std::string name() { return m_name; }
|
||||
|
||||
std::string readAll();
|
||||
|
|
|
@ -55,6 +55,9 @@
|
|||
#include <unordered_map>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
// boost utilities
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
|
|
@ -600,19 +600,26 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
|
|||
switch(wParam) {
|
||||
case SIZE_MAXIMIZED:
|
||||
m_maximized = true;
|
||||
m_visible = true;
|
||||
break;
|
||||
case SIZE_RESTORED:
|
||||
m_maximized = false;
|
||||
m_visible = true;
|
||||
break;
|
||||
case SIZE_MINIMIZED:
|
||||
m_visible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
m_visible = !(wParam == SIZE_MINIMIZED);
|
||||
if(m_visible) {
|
||||
m_size.setWidth(LOWORD(lParam));
|
||||
m_size.setHeight(HIWORD(lParam));
|
||||
Size size;
|
||||
size.setWidth(std::max(std::min((int)LOWORD(lParam), 7680), m_minimumSize.width()));
|
||||
size.setHeight(std::max(std::min((int)HIWORD(lParam), 4320), m_minimumSize.height()));
|
||||
|
||||
if(m_visible && m_size != size) {
|
||||
m_size = size;
|
||||
m_onResize(m_size);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -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
|
|
@ -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, §ion);
|
||||
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();
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -79,7 +79,7 @@ void OTClient::init(const std::vector<std::string>& args)
|
|||
logInfo("Startup options:", startupOptions);
|
||||
|
||||
g_logger.setLogFile(Fw::formatString("%s.txt", Otc::AppCompactName));
|
||||
Application::init(args, Fw::AppEnableAll);
|
||||
Application::init(args);
|
||||
|
||||
g_modules.discoverModules();
|
||||
|
||||
|
|
Loading…
Reference in New Issue