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(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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
DraggingState = 2048,
|
||||||
LastWidgetState = 4096
|
LastWidgetState = 4096
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AppicationFlags {
|
|
||||||
AppEnableModules = 1,
|
|
||||||
AppEnableGraphics = 2,
|
|
||||||
AppEnableConfigs = 4,
|
|
||||||
AppEnableAll = AppEnableModules | AppEnableGraphics | AppEnableConfigs
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue