diff --git a/.travis.yml b/.travis.yml index f6a76843..f5347c9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,7 +68,7 @@ matrix: - export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle install: - sudo apt-get purge cmake - - curl -o cmake_install.sh https://cmake.org/files/v3.11/cmake-3.11.1-Linux-x86_64.sh + - curl -o cmake_install.sh https://cmake.org/files/v3.12/cmake-3.12.2-Linux-x86_64.sh - sudo chmod +x ./cmake_install.sh - sudo ./cmake_install.sh --prefix=/opt/ --exclude-subdir --skip-license - export PATH=/opt/bin/:$PATH diff --git a/REGoth-Android/build.gradle b/REGoth-Android/build.gradle index c2eea8e2..6b694eb9 100644 --- a/REGoth-Android/build.gradle +++ b/REGoth-Android/build.gradle @@ -2,10 +2,11 @@ buildscript { repositories { + google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -14,10 +15,11 @@ buildscript { allprojects { repositories { + google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir -} +} \ No newline at end of file diff --git a/REGoth-Android/gradle/wrapper/gradle-wrapper.properties b/REGoth-Android/gradle/wrapper/gradle-wrapper.properties index 46bae989..05d26653 100644 --- a/REGoth-Android/gradle/wrapper/gradle-wrapper.properties +++ b/REGoth-Android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/bin/archive_android.sh b/bin/archive_android.sh index cd707011..602bb4c6 100755 --- a/bin/archive_android.sh +++ b/bin/archive_android.sh @@ -7,4 +7,4 @@ if [ -z "$PLATFORM" ]; then PLATFORM=$TRAVIS_OS_NAME fi FILENAME=REGoth-${TRAVIS_BRANCH}-${PLATFORM}-debug.apk -cp REGoth-Android/app/build/outputs/apk/app-arm-debug.apk dist/${FILENAME} +cp REGoth-Android/app/build/outputs/apk/arm/debug/app-arm-debug.apk dist/${FILENAME} diff --git a/src/audio/AudioEngine.cpp b/src/audio/AudioEngine.cpp deleted file mode 100644 index 422a9a08..00000000 --- a/src/audio/AudioEngine.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include - -#include -#include - -#include - -#include "AudioEngine.h" - -namespace Audio -{ - void AudioEngine::enumerateDevices(std::vector& enumerated) - { - if (alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) - { - size_t len = 0; - const ALCchar* devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER); - const ALCchar *device = devices, *next = devices + 1; - - while (device && *device != '\0' && next && *next != '\0') - { - enumerated.push_back(device); - - len = strlen(device); - device += (len + 1); - next += (len + 2); - } - } - - if (enumerated.empty()) - enumerated.push_back(std::string()); // empty string is default device - } - - const char* AudioEngine::getErrorString(size_t errorCode) - { - switch (errorCode) - { - case AL_NO_ERROR: - return "AL_NO_ERROR"; - case AL_INVALID_NAME: - return "AL_INVALID_NAME"; - case AL_INVALID_ENUM: - return "AL_INVALID_ENUM"; - case AL_INVALID_VALUE: - return "AL_INVALID_VALUE"; - case AL_INVALID_OPERATION: - return "AL_INVALID_OPERATION"; - case AL_OUT_OF_MEMORY: - return "AL_OUT_OF_MEMORY"; - } - return "UNKNOWN"; - } - - AudioEngine::AudioEngine(const std::string& name) - { - m_Device = alcOpenDevice(name.empty() ? NULL : name.c_str()); - if (!m_Device) - { - LogWarn() << "Could not open audio device '" << (name.empty() ? "default" : name) << "': " - << getErrorString(alGetError()) << ", sound disabled"; - return; - } - } - - AudioEngine::~AudioEngine() - { - if (m_Device) - alcCloseDevice(m_Device); - } -} diff --git a/src/audio/AudioEngine.h b/src/audio/AudioEngine.h index 172da85e..6107e2f6 100644 --- a/src/audio/AudioEngine.h +++ b/src/audio/AudioEngine.h @@ -1,66 +1,177 @@ #pragma once -#include +#include +#include +#include +#include #include -#include - -typedef struct ALCdevice_struct ALCdevice; +#include namespace Audio { - class AudioWorld; + enum class State { + Stopped, + Paused, + Playing + }; - /** The AudioEngine represents the target OS sound system. - * - * The engine class matches the OpenAL device level. + enum class Format { + Mono, + Stereo + }; + + /** + * @brief Sounds represent audio sources. + * + * All of the information about a sound should be re-set before it is played + * after it had been stopped. + * + */ + class Sound { + public: + virtual ~Sound() = default; + + /** + * @brief Set sound pitch, must be positive + * + * 1 means original pitch, values < 1 mean lower, > 1 mean higher. + * + * @return float + */ + virtual void pitch(float) = 0; + + /** + * @brief Set sound gain + * + * 1 means original volume, <1 means lower gain, >1 means higher. + * + * @return float + */ + virtual void gain(float) = 0; + + /** + * @brief Set the maximum distance from which this sound can be heard. + * Must be positive. + * + */ + virtual void maxDistance(float) = 0; + + /** + * @brief Set the coordinates of the sound's position. + * + */ + virtual void position(const Math::float3&) = 0; + + /** + * @brief Set the components of the sound's velocity. + * + */ + virtual void velocity(const Math::float3&) = 0; + + /** + * @brief Set the componenets of the sound's direction. + * + */ + virtual void direction(const Math::float3&) = 0; + + /** + * @brief Set whether the sound's position is relative to the listener. + * + */ + virtual void relative(bool) = 0; + + /** + * @brief Set wheter the sound should loop back to the beginning once it + * finishes playing. + * + * This is ignored in the case of streming sounds + */ + virtual void looping(bool) = 0; + + /** + * @brief Get the playback state of this sound. + * + * @return State + */ + virtual State state() = 0; + + /** + * @brief Starts sound playback. + * + * This method is idempotent: if the sound was already playing, + * nothing happens. + */ + virtual void play() = 0; + /** + * @brief Pauses sound playback. + * + * This method is idempotent: if the sound was already paused, + * nothing happens. + */ + virtual void pause() = 0; + /** + * @brief Stops sound playback. + * + * Playback will resume from the beginning. + * This method is idempotent: if the sound was already stopped, + * nothing happens. + */ + virtual void stop() = 0; + }; + + using SoundPtr = std::shared_ptr; + using SoundStream = std::function; + + struct Orientation { + Math::float3 at; + Math::float3 up; + }; + + /** An AudioEngine is the interface to the system's audio driver * */ - class AudioEngine final + class AudioEngine { public: - /** Initializes the AudioEngine. - * - * The @p device param specifies the device to use and can be determined - * using enumerateDevices(). - * - * @param device_name The name of the device to use. - * - * @see AudioEngine::enumerateDevices() - * - */ - AudioEngine(const std::string& device_name = std::string()); - - /** Deinitializes the AudioEngine. - */ - ~AudioEngine(); - - /** Returns the OpenAL device used by this engine. - * - * @return The OpenAL device or nullptr when initialization has failed. - */ - ALCdevice* getDevice() const { return m_Device; } - /** Enumerates the audio devices available on the current machine. - * - * Note that not all OpenAL implementations support enumeration. In this case you'll - * retrieve a single element list with an empty device name, indicating that it is - * the default device. - * - * @param[out] devices A list of available devices. - * - */ - static void enumerateDevices(std::vector& devices); - - /** Returns a text representation of an error code. - * - * @param The error code returned by alGetError() or alcGetError(). - * - * @return The error text. - * - */ - static const char* getErrorString(size_t errorCode); - - private: - ALCdevice* m_Device = nullptr; + /** + * @brief Set master listener gain, must be positive + * + * 1 means original volume, <1 means lower gain, >1 means higher. + * + */ + virtual void gain(float) = 0; + + /** + * @brief Set coordinates of listener's position + * + */ + virtual void position(const Math::float3&) = 0; + + /** + * @brief Set components of listener's velocity + * + */ + virtual void velocity(const Math::float3&) = 0; + + /** + * @brief Set listener's orientation + * + */ + virtual void orientation(const Orientation&) = 0; + + /** + * @brief Creates a sound object from an audio buffer + * + * @return SoundPtr + */ + virtual SoundPtr createSound(const std::int16_t* buf, std::size_t len, Format, std::size_t samplingFreq) = 0; + + /** + * @brief Creates a sound object from an audio stream + * + * @return SoundPtr + */ + virtual SoundPtr createSound(SoundStream, Format, std::size_t samplingFreq) = 0; }; } diff --git a/src/audio/AudioWorld.cpp b/src/audio/AudioWorld.cpp index 058b0da2..a8c18266 100644 --- a/src/audio/AudioWorld.cpp +++ b/src/audio/AudioWorld.cpp @@ -1,19 +1,13 @@ #include +#include #include #include -#include - -#ifdef RE_USE_SOUND -#include -#include -#include #include #ifndef DMUSIC_DLS_PLAYER #define DMUSIC_DLS_PLAYER 1 #endif #include -#endif #include @@ -32,15 +26,17 @@ using namespace Audio; -static DirectMusic::SegmentTiming getTiming(std::uint32_t v) { - switch (v) { - case Daedalus::GEngineClasses::TRANSITION_SUB_TYPE_BEAT: - return DirectMusic::SegmentTiming::Beat; - case Daedalus::GEngineClasses::TRANSITION_SUB_TYPE_MEASURE: - return DirectMusic::SegmentTiming::Measure; +static DirectMusic::SegmentTiming getTiming(std::uint32_t v) +{ + switch (v) + { + case Daedalus::GEngineClasses::TRANSITION_SUB_TYPE_BEAT: + return DirectMusic::SegmentTiming::Beat; + case Daedalus::GEngineClasses::TRANSITION_SUB_TYPE_MEASURE: + return DirectMusic::SegmentTiming::Measure; - default: - return DirectMusic::SegmentTiming::Immediate; + default: + return DirectMusic::SegmentTiming::Immediate; } } @@ -51,43 +47,21 @@ namespace World , m_VDFSIndex(vdfidx) , m_exiting(false) { -#ifdef RE_USE_SOUND - if (!audio_engine.getDevice()) - return; - - m_Context = alcCreateContext(audio_engine.getDevice(), nullptr); - if (!m_Context) - { - LogWarn() << "Could not create OpenAL context: " - << AudioEngine::getErrorString(alcGetError(audio_engine.getDevice())); - return; - } - - alcMakeContextCurrent(m_Context); - - alListener3f(AL_POSITION, 0, 0, 0.0f); - // check for errors - alListener3f(AL_VELOCITY, 0, 0, 0); - // check for errors - ALfloat listenerOri[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}; - alListenerfv(AL_ORIENTATION, listenerOri); - - // Need this for AL_MAX_DISTANCE to work - alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); + audio_engine.position({0.0f, 0.0f, 0.0f}); + audio_engine.velocity({0.0f, 0.0f, 0.0f}); + audio_engine.orientation({{0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}); createSounds(); - initializeMusic(); -#endif } -#ifdef RE_USE_SOUND void AudioWorld::initializeMusic() { std::string datPath = "/_work/data/Scripts/_compiled/MUSIC.DAT"; std::string datFile = Utils::getCaseSensitivePath(datPath, m_Engine.getEngineArgs().gameBaseDirectory); - if (!Utils::fileExists(datFile)) { + if (!Utils::fileExists(datFile)) + { LogError() << "Failed to find MUSIC.DAT at: " << datFile; return; } @@ -106,15 +80,18 @@ namespace World // DirectMusic initialization std::string baseDir = m_Engine.getEngineArgs().gameBaseDirectory; std::string musicPath = Utils::getCaseSensitivePath("/_work/data/Music", baseDir); - try { + try + { const auto sfFactory = DirectMusic::DlsPlayer::createFactory(); m_musicContext = std::make_unique(44100, 2, sfFactory); auto loader = [musicPath, baseDir](const std::string& name) { const auto search = Utils::lowered(Utils::stripFilePath(name)); - for (const auto& file : Utils::getFilesInDirectory(musicPath)) { + for (const auto& file : Utils::getFilesInDirectory(musicPath)) + { const auto lowercaseName = Utils::lowered(Utils::stripFilePath(file)); - if (lowercaseName == search) { + if (lowercaseName == search) + { return Utils::readBinaryFileContents(file); } } @@ -123,7 +100,8 @@ namespace World m_musicContext->provideLoader(loader); - for (const auto& segment : Utils::getFilesInDirectory(musicPath, "sgt")) { + for (const auto& segment : Utils::getFilesInDirectory(musicPath, "sgt")) + { const auto lowercaseName = Utils::lowered(Utils::stripFilePath(segment)); const auto segm = m_musicContext->loadSegment(segment); LogInfo() << "Loading " + segment; @@ -131,110 +109,34 @@ namespace World } LogInfo() << "All segments loaded."; - alGenBuffers(RE_NUM_MUSIC_BUFFERS, m_musicBuffers); - alGenSources(1, &m_musicSource); - - // Set the default volume - alSourcef(m_musicSource, AL_GAIN, 1); + m_MusicSource = m_Engine.getAudioEngine().createSound( + [&](auto buf, auto len) -> int { + if (m_exiting) return -1; - // Set the default position of the sound - alSource3f(m_musicSource, AL_POSITION, 0, 0, 0); - - m_musicRenderThread = std::thread(&AudioWorld::musicRenderFunction, this); - } catch (const std::exception& exc) { - LogError() << "Couldn't initialize music system: " << exc.what(); + m_musicContext->renderBlock(buf, len); + return len; + }, + Audio::Format::Stereo, 44100); + m_MusicSource->gain(1.0f); + m_MusicSource->position({0.0f, 0.0f, 0.0f}); } - } - - void AudioWorld::musicRenderFunction() - { - ALenum error; - std::int16_t buf[RE_MUSIC_BUFFER_LEN]; - for (int i = 0; i < RE_MUSIC_BUFFER_LEN; i++) buf[i] = 0; - - for (int i = 0; i < RE_NUM_MUSIC_BUFFERS; i++) + catch (const std::exception& exc) { - alBufferData(m_musicBuffers[i], AL_FORMAT_STEREO16, buf, RE_MUSIC_BUFFER_LEN * 2, 44100); - } - - alSourceQueueBuffers(m_musicSource, RE_NUM_MUSIC_BUFFERS, m_musicBuffers); - alSourcePlay(m_musicSource); - error = alGetError(); - if (error != AL_NO_ERROR) - { - LogError() << "Cannot start playing music: " << AudioEngine::getErrorString(error); - return; - } - - m_musicContext->renderBlock(buf, RE_MUSIC_BUFFER_LEN); - - while (!m_exiting) - { - ALint val; - int n = 0; - alGetSourcei(m_musicSource, AL_BUFFERS_PROCESSED, &val); - if (val <= 0) - { - continue; - } - - for(int i = 0; i < val; i++) - { - ALuint buffer; - alSourceUnqueueBuffers(m_musicSource, 1, &buffer); - alBufferData(buffer, AL_FORMAT_STEREO16, buf, RE_MUSIC_BUFFER_LEN * 2, 44100); - alSourceQueueBuffers(m_musicSource, 1, &buffer); - error = alGetError(); - if (error != AL_NO_ERROR) - { - LogError() << "Error while buffering: " << AudioEngine::getErrorString(error); - return; - } - m_musicContext->renderBlock(buf, RE_MUSIC_BUFFER_LEN); - } - - alGetSourcei(m_musicSource, AL_SOURCE_STATE, &val); - if (val != AL_PLAYING) - alSourcePlay(m_musicSource); + LogError() << "Couldn't initialize music system: " << exc.what(); } } -#endif AudioWorld::~AudioWorld() { -#ifdef RE_USE_SOUND m_exiting = true; - m_musicRenderThread.join(); - - alDeleteBuffers(RE_NUM_MUSIC_BUFFERS, m_musicBuffers); - alDeleteSources(1, &m_musicSource); - - for (int i = 0; i < Config::MAX_NUM_LEVEL_AUDIO_FILES; i++) - { - Sound& snd = m_Allocator.getElements()[i]; - if (snd.m_Handle != 0) - alDeleteBuffers(1, &snd.m_Handle); - } - - for (Source& src : m_Sources) - alDeleteSources(1, &src.m_Handle); - - if (m_Context) - alcDestroyContext(m_Context); delete m_SoundVM; delete m_MusicVM; -#endif } Handle::SfxHandle AudioWorld::loadAudioVDF(const VDFS::FileIndex& idx, const std::string& name) { -#ifdef RE_USE_SOUND - if (!m_Context) - return Handle::SfxHandle::makeInvalidHandle(); - - std::string ucname = name; - std::transform(ucname.begin(), ucname.end(), ucname.begin(), ::toupper); + std::string ucname = Utils::uppered(name); // m_SoundMap contains all the sounds with C_SFX script definitions Sound* snd = nullptr; @@ -251,7 +153,7 @@ namespace World else { snd = &m_Allocator.getElement(h); - if (snd->m_Handle) // already loaded + if (snd->sound) // already loaded return h; } @@ -266,30 +168,21 @@ namespace World if (!wav.open() || !wav.read()) return Handle::SfxHandle::makeInvalidHandle(); - alGenBuffers(1, &snd->m_Handle); - - ALenum error = alGetError(); - if (error != AL_NO_ERROR) + try { - static bool warned = false; - if (!warned) - { - LogWarn() << "Could not create OpenAL buffer: " - << AudioEngine::getErrorString(error); - return Handle::SfxHandle::makeInvalidHandle(); - } - return Handle::SfxHandle::makeInvalidHandle(); + auto dataPtr = reinterpret_cast(wav.getData()); + auto format = wav.getChannels() == 1 ? Audio::Format::Mono : Audio::Format::Stereo; + auto sound = m_Engine.getAudioEngine().createSound(dataPtr, wav.getDataSize(), format, wav.getRate()); + snd->sound = sound; + m_Sources.push_back(sound); } - - alBufferData(snd->m_Handle, AL_FORMAT_MONO16, wav.getData(), wav.getDataSize(), wav.getRate()); - error = alGetError(); - if (error != AL_NO_ERROR) + catch (const std::runtime_error& error) { static bool warned = false; if (!warned) { - LogWarn() << "Could not set OpenAL buffer data: " - << AudioEngine::getErrorString(error); + LogWarn() << "Could not create sound: " << error.what(); + warned = true; } return Handle::SfxHandle::makeInvalidHandle(); } @@ -300,9 +193,6 @@ namespace World m_SoundMap[name] = h; return h; -#else - return Handle::SfxHandle::makeInvalidHandle(); -#endif } Handle::SfxHandle AudioWorld::loadAudioVDF(const std::string& name) @@ -310,129 +200,44 @@ namespace World return loadAudioVDF(m_VDFSIndex, name); } - Utils::Ticket AudioWorld::playSound(Handle::SfxHandle h, const Math::float3& position, bool relative, float maxDist) + Audio::SoundPtr AudioWorld::playSound(Handle::SfxHandle h, const Math::float3& position, bool relative, float maxDist) { -#ifdef RE_USE_SOUND - - if (!m_Context) - return Utils::Ticket(); - - alcMakeContextCurrent(m_Context); - Sound& snd = m_Allocator.getElement(h); - // Get a cached source object - Source s = getFreeSource(); - - //LogInfo() << "play sound " << snd.sfx.file << " vol " << snd.sfx.vol; - - alSourcef(s.m_Handle, AL_PITCH, m_Engine.getGameClock().getGameEngineSpeedFactor()); - alSourcef(s.m_Handle, AL_GAIN, snd.sfx.vol / 127.0f); - alSource3f(s.m_Handle, AL_POSITION, position.x, position.y, position.z); - alSource3f(s.m_Handle, AL_VELOCITY, 0, 0, 0); - alSourcef(s.m_Handle, AL_MAX_DISTANCE, maxDist); - - // Relative for sources directly attached to the listener - alSourcei(s.m_Handle, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE); - - // TODO: proper looping would require slicing and queueing multiple buffers - // and setting the source to loop when the non-looping buffer was played. - // start and end don't seem to be used, thoug? - alSourcei(s.m_Handle, AL_LOOPING, snd.sfx.loop ? AL_TRUE : AL_FALSE); - - alSourcei(s.m_Handle, AL_BUFFER, snd.m_Handle); - ALenum error = alGetError(); - if (error != AL_NO_ERROR) - { - static bool warned = false; - if (!warned) - { - LogWarn() << "Could not attach buffer to source: " << AudioEngine::getErrorString(error); - warned = true; - } - return Utils::Ticket(); - } - - alSourcePlay(s.m_Handle); - error = alGetError(); - if (error != AL_NO_ERROR) - { - static bool warned = false; - if (!warned) - { - LogWarn() << "Could not start source!" << AudioEngine::getErrorString(error); - warned = true; - } - return Utils::Ticket(); - } - return s.soundTicket; -#else - return Utils::Ticket(); -#endif - } - -#ifdef RE_USE_SOUND - AudioWorld::Source AudioWorld::getFreeSource() - { - if (!m_Context) - return AudioWorld::Source(); - - alcMakeContextCurrent(m_Context); - - // Check if we could re-use one - for (Source& s : m_Sources) + try { - ALint state; - alGetSourcei(s.m_Handle, AL_SOURCE_STATE, &state); - - if (state != AL_PLAYING && state != AL_PAUSED) - { - // reusing old source, give new ticket to it - s.soundTicket = Utils::Ticket(); - return s; - } + snd.sound->pitch(m_Engine.getGameClock().getGameEngineSpeedFactor()); + snd.sound->gain(snd.sfx.vol / 127.0f); + snd.sound->position(position); + snd.sound->velocity({0.0f, 0.0f, 0.0f}); + snd.sound->maxDistance(maxDist); + snd.sound->relative(relative); + snd.sound->looping(snd.sfx.loop); + // TODO: proper looping would require slicing and queueing multiple buffers + // and setting the source to loop when the non-looping buffer was played. + // start and end don't seem to be used, thoug? + snd.sound->play(); } - - ALuint source; - alGenSources(1, &source); - - ALenum error = alGetError(); - if (error != AL_NO_ERROR) + catch (const std::runtime_error& err) { static bool warned = false; if (!warned) { - LogWarn() << "Could not allocate AL source!"; + LogWarn() << "Error while playing sound: " << err.what(); warned = true; } - return AudioWorld::Source(); + return nullptr; } - alSourcef(source, AL_PITCH, 1); - // check for errors - alSourcef(source, AL_GAIN, 1); - // check for errors - alSource3f(source, AL_POSITION, 0, 0, 0); - // check for errors - alSource3f(source, AL_VELOCITY, 0, 0, 0); - // check for errors - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Nothing to re-use available, make a new entry - m_Sources.emplace_back(); - m_Sources.back().m_Handle = source; - return m_Sources.back(); + return snd.sound; } Handle::SfxHandle AudioWorld::allocateSound(const std::string& name, const Daedalus::GEngineClasses::C_SFX& sfx) { - /*LogInfo() << "alloc sound " << name << " file " << sfx.file << " vol: " << sfx.vol - << " loop: " << sfx.loop << " loop start: " << sfx.loopStartOffset << " loop end: " << sfx.loopEndOffset; - */ Handle::SfxHandle h = m_Allocator.createObject(); Sound& snd = m_Allocator.getElement(h); snd.sfx = sfx; - snd.m_Handle = 0; + snd.sound = nullptr; snd.name = name; m_SoundMap[name] = h; @@ -460,7 +265,6 @@ namespace World size_t count = 0; m_SoundVM->getDATFile().iterateSymbolsOfClass("C_SFX", [&](size_t i, Daedalus::PARSymbol& s) { - Daedalus::GameState::SfxHandle h = m_SoundVM->getGameState().createSfx(); Daedalus::GEngineClasses::C_SFX& sfx = m_SoundVM->getGameState().getSfx(h); m_SoundVM->initializeInstance(ZMemory::toBigHandle(h), i, Daedalus::IC_Sfx); @@ -473,101 +277,25 @@ namespace World LogInfo() << "created " << count << " sounds"; } -#endif - void AudioWorld::stopSounds() { -#ifdef RE_USE_SOUND - if (!m_Context) - return; - - alcMakeContextCurrent(m_Context); - - for (Source& s : m_Sources) - alSourceStop(s.m_Handle); -#endif - } - - void AudioWorld::stopSound(Utils::Ticket ticket) - { -#ifdef RE_USE_SOUND - if (!m_Context) - return; - - alcMakeContextCurrent(m_Context); - - for (Source& s : m_Sources) - { - if (s.soundTicket == ticket) - { - alSourceStop(s.m_Handle); - return; - } - } -#endif - } - - bool AudioWorld::soundIsPlaying(Utils::Ticket ticket) - { -#ifdef RE_USE_SOUND - if (!m_Context) - return false; - - alcMakeContextCurrent(m_Context); - - for (Source& s : m_Sources) - { - if (s.soundTicket == ticket) - { - ALint state; - alGetSourcei(s.m_Handle, AL_SOURCE_STATE, &state); - return state == AL_PLAYING || state == AL_PAUSED; - } - } - return false; -#else - return false; -#endif + for (auto& s : m_Sources) s->stop(); } void AudioWorld::pauseSounds() { -#ifdef RE_USE_SOUND - if (!m_Context) - return; - - alcMakeContextCurrent(m_Context); - - for (Source& s : m_Sources) - { - ALint state; - alGetSourcei(s.m_Handle, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) - { - alSourcePause(s.m_Handle); - } - } -#endif + for (auto& s : m_Sources) s->pause(); } void AudioWorld::continueSounds() { -#ifdef RE_USE_SOUND - if (!m_Context) - return; - - alcMakeContextCurrent(m_Context); - - for (Source& s : m_Sources) + for (auto& s : m_Sources) { - ALint state; - alGetSourcei(s.m_Handle, AL_SOURCE_STATE, &state); - if (state == AL_PAUSED) + if (s->state() == Audio::State::Paused) { - alSourcePlay(s.m_Handle); + s->play(); } } -#endif } void AudioWorld::loadVariants(Handle::SfxHandle sfx) @@ -592,28 +320,12 @@ namespace World } while (v.isValid()); } - void AudioWorld::setListenerPosition(const Math::float3& position) - { - alListener3f(AL_POSITION, position.x, position.y, position.z); - } - - void AudioWorld::setListenerVelocity(const Math::float3& velocity) - { - alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z); - } - - void AudioWorld::setListenerOrientation(const Math::float3& at, const Math::float3& up) - { - float v[6] = {at.x, at.y, at.z, up.x, up.y, up.z}; - alListenerfv(AL_ORIENTATION, v); - } - - Utils::Ticket AudioWorld::playSound(Handle::SfxHandle h) + Audio::SoundPtr AudioWorld::playSound(Handle::SfxHandle h) { return playSound(h, Math::float3(0, 0, 0), true); } - Utils::Ticket AudioWorld::playSound(const std::string& name) + Audio::SoundPtr AudioWorld::playSound(const std::string& name) { // Check if that sound has already been loaded. If not, load it now. Handle::SfxHandle h = loadAudioVDF(name); @@ -621,18 +333,18 @@ namespace World // Check if loading was successfull, if so, play it if (!h.isValid()) { - return Utils::Ticket(); + return nullptr; } return playSound(h, Math::float3(0, 0, 0), true); } - Utils::Ticket AudioWorld::playSound(Handle::SfxHandle h, const Math::float3& position, float maxDist) + Audio::SoundPtr AudioWorld::playSound(Handle::SfxHandle h, const Math::float3& position, float maxDist) { return playSound(h, position, false, maxDist); } - Utils::Ticket AudioWorld::playSound(const std::string& name, const Math::float3& position, float maxDist) + Audio::SoundPtr AudioWorld::playSound(const std::string& name, const Math::float3& position, float maxDist) { // Check if that sound has already been loaded. If not, load it now. Handle::SfxHandle h = loadAudioVDF(name); @@ -640,13 +352,13 @@ namespace World // Check if loading was successfull, if so, play it if (!h.isValid()) { - return Utils::Ticket(); + return nullptr; } return playSound(h, position, false, maxDist); } - Utils::Ticket AudioWorld::playSoundVariantRandom(const std::string& name, const Math::float3& position, float maxDist) + Audio::SoundPtr AudioWorld::playSoundVariantRandom(const std::string& name, const Math::float3& position, float maxDist) { // Check if that sound has already been loaded. If not, load it now. Handle::SfxHandle h = loadAudioVDF(name); @@ -654,20 +366,20 @@ namespace World // Check if loading was successfull, if so, play it if (!h.isValid()) { - return Utils::Ticket(); + return nullptr; } return playSoundVariantRandom(h, position, maxDist); } - Utils::Ticket AudioWorld::playSoundVariantRandom(Handle::SfxHandle h, const Math::float3& position, float maxDist) + Audio::SoundPtr AudioWorld::playSoundVariantRandom(Handle::SfxHandle h, const Math::float3& position, float maxDist) { Sound& snd = m_Allocator.getElement(h); return playSound(snd.variants[rand() % snd.variants.size()], position, false, maxDist); } - Utils::Ticket AudioWorld::playSoundVariantRandom(const std::string& name) + Audio::SoundPtr AudioWorld::playSoundVariantRandom(const std::string& name) { // Check if that sound has already been loaded. If not, load it now. Handle::SfxHandle h = loadAudioVDF(name); @@ -675,82 +387,62 @@ namespace World // Check if loading was successfull, if so, play it if (!h.isValid()) { - return Utils::Ticket(); + return nullptr; } return playSoundVariantRandom(h); } - Utils::Ticket AudioWorld::playSoundVariantRandom(Handle::SfxHandle h) + Audio::SoundPtr AudioWorld::playSoundVariantRandom(Handle::SfxHandle h) { Sound& snd = m_Allocator.getElement(h); return playSound(snd.variants[rand() % snd.variants.size()]); } - void AudioWorld::setListenerGain(float gain) - { - alListenerf(AL_GAIN, gain); - } - - void AudioWorld::setSoundMaxDistance(Utils::Ticket sound, float maxDist) - { - for (Source& s : m_Sources) - { - if (s.soundTicket == sound) - { - alSourcef(s.m_Handle, AL_MAX_DISTANCE, maxDist); - return; - } - } - } - bool AudioWorld::playSegment(const std::string& name, DirectMusic::SegmentTiming timing) { -#ifdef RE_USE_SOUND std::string loweredName = Utils::lowered(name); if (m_musicContext == nullptr || m_Segments.find(loweredName) == m_Segments.end()) { return false; } - else + + if (m_playingSegment != loweredName) { - if (m_playingSegment != loweredName) - { - m_musicContext->playSegment(m_Segments.at(loweredName), timing); - m_playingSegment = loweredName; - } - return true; + m_musicContext->playSegment(m_Segments.at(loweredName), timing); + m_playingSegment = loweredName; } -#endif - return false; + m_CurrentMusicTheme = ""; + return true; } bool AudioWorld::playMusicTheme(const std::string& name) { -#ifdef RE_USE_SOUND - if (m_MusicVM->getDATFile().hasSymbolName(Utils::uppered(name))) + auto uname = Utils::uppered(name); + if (m_CurrentMusicTheme == uname) return true; + + if (m_MusicVM->getDATFile().hasSymbolName(uname)) { - size_t i = m_MusicVM->getDATFile().getSymbolIndexByName(Utils::uppered(name)); + size_t i = m_MusicVM->getDATFile().getSymbolIndexByName(uname); Daedalus::GameState::MusicThemeHandle h = m_MusicVM->getGameState().createMusicTheme(); Daedalus::GEngineClasses::C_MusicTheme& mt = m_MusicVM->getGameState().getMusicTheme(h); m_MusicVM->initializeInstance(ZMemory::toBigHandle(h), i, Daedalus::IC_MusicTheme); - return playSegment(mt.file, getTiming(mt.transSubType)); + bool result = playSegment(mt.file, getTiming(mt.transSubType)); + if (result) m_CurrentMusicTheme = uname; + return result; } -#endif return false; } - const std::vector AudioWorld::getLoadedSegments() const + std::vector AudioWorld::getLoadedSegments() const { std::vector vect; -#ifdef RE_USE_SOUND for (const auto& kvp : m_Segments) { vect.push_back(kvp.first); } -#endif return vect; } -} +} // namespace World diff --git a/src/audio/AudioWorld.h b/src/audio/AudioWorld.h index 9c9a1ea4..4d371ef5 100644 --- a/src/audio/AudioWorld.h +++ b/src/audio/AudioWorld.h @@ -6,8 +6,9 @@ #include -#include +#include