mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-04 21:10:32 +00:00
Merge branch 'cmake_adjustments' of github.com:Ragora/Torque3D into cmake_adjustments
This commit is contained in:
commit
790cb17435
236 changed files with 30902 additions and 7540 deletions
|
|
@ -121,7 +121,11 @@ setupVersionNumbers()
|
|||
if(NOT TORQUE_TEMPLATE)
|
||||
set(TORQUE_TEMPLATE "BaseGame" CACHE STRING "the template to use")
|
||||
endif()
|
||||
installTemplate(${TORQUE_TEMPLATE})
|
||||
|
||||
if (NOT TORQUE_INSTALLED_TEMPLATE)
|
||||
installTemplate(${TORQUE_TEMPLATE})
|
||||
set(TORQUE_INSTALLED_TEMPLATE TRUE CACHE BOOL "Whether or not the game template is installed. This may be reset to false (or removed) to force a reinstall.")
|
||||
endif(NOT TORQUE_INSTALLED_TEMPLATE)
|
||||
|
||||
# Generate torqueConfig.h in our temp directory
|
||||
CONFIGURE_FILE("${CMAKE_SOURCE_DIR}/Tools/CMake/torqueConfig.h.in" "${CMAKE_BINARY_DIR}/temp/torqueConfig.h")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
# Ask CMake to perform builds in a temporary directory for all of these.
|
||||
# We also use EXCLUDE_FROM_ALL to ensure we only build and install what we want
|
||||
set(BUILD_SHARED_LIBS off CACHE STRING "")
|
||||
add_subdirectory(assimp ${CMAKE_BINARY_DIR}/temp/assimp EXCLUDE_FROM_ALL)
|
||||
|
||||
set(SDL_SHARED on CACHE BOOL "" FORCE)
|
||||
add_subdirectory(sdl ${CMAKE_BINARY_DIR}/temp/sdl2 EXCLUDE_FROM_ALL)
|
||||
|
||||
|
|
@ -16,6 +13,14 @@ set(PNG_BUILD_ZLIB on CACHE BOOL "" FORCE)
|
|||
get_filename_component(ZLIB_ROOT "zlib" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(ZLIB_ROOT "${ZLIB_ROOT}" CACHE STRING "ZLib root location" FORCE)
|
||||
|
||||
# Assimp depends on zlib
|
||||
set(BUILD_SHARED_LIBS off CACHE STRING "")
|
||||
set(ASSIMP_BUILD_ZLIB on CACHE STRING "")
|
||||
set(ASSIMP_HUNTER_ENABLED off CACHE STRING "")
|
||||
add_subdirectory(assimp ${CMAKE_BINARY_DIR}/temp/assimp EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
|
||||
add_subdirectory(lpng ${CMAKE_BINARY_DIR}/temp/lpng EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(ljpeg ${CMAKE_BINARY_DIR}/temp/ljpeg EXCLUDE_FROM_ALL)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
file(GLOB CONVEX_DECOMP_SOURCES "*.cpp")
|
||||
add_library(convexDecomp STATIC ${CONVEX_DECOMP_SOURCES})
|
||||
target_include_directories(convexDecomp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_compile_definitions(convexDecomp PUBLIC LINUX)
|
||||
endif (UNIX AND NOT APPLE)
|
||||
66
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
Normal file
66
Engine/lib/openal-soft/.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
name: CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{matrix.config.name}}
|
||||
runs-on: ${{matrix.config.os}}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {
|
||||
name: "Visual Studio 64-bit",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
name: "macOS",
|
||||
os: macos-latest,
|
||||
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
name: "Linux",
|
||||
os: ubuntu-latest,
|
||||
cmake_opts: "-DALSOFT_REQUIRE_RTKIT=ON \
|
||||
-DALSOFT_REQUIRE_ALSA=ON \
|
||||
-DALSOFT_REQUIRE_OSS=ON \
|
||||
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_JACK=ON",
|
||||
deps_cmdline: "sudo apt update && sudo apt-get install -qq \
|
||||
libpulse-dev \
|
||||
portaudio19-dev \
|
||||
libasound2-dev \
|
||||
libjack-dev \
|
||||
qtbase5-dev \
|
||||
libdbus-1-dev",
|
||||
build_type: "Release"
|
||||
}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ ! -z "${{matrix.config.deps_cmdline}}" ]]; then
|
||||
eval ${{matrix.config.deps_cmdline}}
|
||||
fi
|
||||
|
||||
- name: Configure
|
||||
shell: bash
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ${{matrix.config.cmake_opts}} .
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build build --config ${{matrix.config.build_type}}
|
||||
|
|
@ -26,7 +26,8 @@ install:
|
|||
portaudio19-dev \
|
||||
libasound2-dev \
|
||||
libjack-dev \
|
||||
qtbase5-dev
|
||||
qtbase5-dev \
|
||||
libdbus-1-dev
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
|
|
|
|||
|
|
@ -1,282 +0,0 @@
|
|||
#ifndef ALCONTEXT_H
|
||||
#define ALCONTEXT_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "al/listener.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alu.h"
|
||||
#include "atomic.h"
|
||||
#include "inprogext.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "threads.h"
|
||||
#include "vecmat.h"
|
||||
#include "vector.h"
|
||||
|
||||
struct ALeffectslot;
|
||||
struct ALsource;
|
||||
struct EffectSlot;
|
||||
struct EffectSlotProps;
|
||||
struct RingBuffer;
|
||||
struct Voice;
|
||||
struct VoiceChange;
|
||||
struct VoicePropsItem;
|
||||
|
||||
|
||||
enum class DistanceModel : unsigned char {
|
||||
Disable,
|
||||
Inverse, InverseClamped,
|
||||
Linear, LinearClamped,
|
||||
Exponent, ExponentClamped,
|
||||
|
||||
Default = InverseClamped
|
||||
};
|
||||
|
||||
|
||||
struct WetBuffer {
|
||||
bool mInUse;
|
||||
al::FlexArray<FloatBufferLine, 16> mBuffer;
|
||||
|
||||
WetBuffer(size_t count) : mBuffer{count} { }
|
||||
|
||||
DEF_FAM_NEWDEL(WetBuffer, mBuffer)
|
||||
};
|
||||
using WetBufferPtr = std::unique_ptr<WetBuffer>;
|
||||
|
||||
|
||||
struct ContextProps {
|
||||
float DopplerFactor;
|
||||
float DopplerVelocity;
|
||||
float SpeedOfSound;
|
||||
bool SourceDistanceModel;
|
||||
DistanceModel mDistanceModel;
|
||||
|
||||
std::atomic<ContextProps*> next;
|
||||
|
||||
DEF_NEWDEL(ContextProps)
|
||||
};
|
||||
|
||||
struct ListenerProps {
|
||||
std::array<float,3> Position;
|
||||
std::array<float,3> Velocity;
|
||||
std::array<float,3> OrientAt;
|
||||
std::array<float,3> OrientUp;
|
||||
float Gain;
|
||||
float MetersPerUnit;
|
||||
|
||||
std::atomic<ListenerProps*> next;
|
||||
|
||||
DEF_NEWDEL(ListenerProps)
|
||||
};
|
||||
|
||||
struct ContextParams {
|
||||
/* Pointer to the most recent property values that are awaiting an update. */
|
||||
std::atomic<ContextProps*> ContextUpdate{nullptr};
|
||||
std::atomic<ListenerProps*> ListenerUpdate{nullptr};
|
||||
|
||||
alu::Matrix Matrix{alu::Matrix::Identity()};
|
||||
alu::Vector Velocity{};
|
||||
|
||||
float Gain{1.0f};
|
||||
float MetersPerUnit{1.0f};
|
||||
|
||||
float DopplerFactor{1.0f};
|
||||
float SpeedOfSound{343.3f}; /* in units per sec! */
|
||||
|
||||
bool SourceDistanceModel{false};
|
||||
DistanceModel mDistanceModel{};
|
||||
};
|
||||
|
||||
|
||||
struct SourceSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALsource *Sources{nullptr}; /* 64 */
|
||||
|
||||
SourceSubList() noexcept = default;
|
||||
SourceSubList(const SourceSubList&) = delete;
|
||||
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
|
||||
~SourceSubList();
|
||||
|
||||
SourceSubList& operator=(const SourceSubList&) = delete;
|
||||
SourceSubList& operator=(SourceSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
|
||||
};
|
||||
|
||||
struct EffectSlotSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALeffectslot *EffectSlots{nullptr}; /* 64 */
|
||||
|
||||
EffectSlotSubList() noexcept = default;
|
||||
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||
~EffectSlotSubList();
|
||||
|
||||
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||
};
|
||||
|
||||
struct ALCcontext : public al::intrusive_ref<ALCcontext> {
|
||||
const al::intrusive_ptr<ALCdevice> mDevice;
|
||||
|
||||
/* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit
|
||||
* indicates if updates are currently happening).
|
||||
*/
|
||||
RefCount mUpdateCount{0u};
|
||||
std::atomic<bool> mHoldUpdates{false};
|
||||
|
||||
float mGainBoost{1.0f};
|
||||
|
||||
/* Linked lists of unused property containers, free to use for future
|
||||
* updates.
|
||||
*/
|
||||
std::atomic<ContextProps*> mFreeContextProps{nullptr};
|
||||
std::atomic<ListenerProps*> mFreeListenerProps{nullptr};
|
||||
std::atomic<VoicePropsItem*> mFreeVoiceProps{nullptr};
|
||||
std::atomic<EffectSlotProps*> mFreeEffectslotProps{nullptr};
|
||||
|
||||
/* The voice change tail is the beginning of the "free" elements, up to and
|
||||
* *excluding* the current. If tail==current, there's no free elements and
|
||||
* new ones need to be allocated. The current voice change is the element
|
||||
* last processed, and any after are pending.
|
||||
*/
|
||||
VoiceChange *mVoiceChangeTail{};
|
||||
std::atomic<VoiceChange*> mCurrentVoiceChange{};
|
||||
|
||||
void allocVoiceChanges(size_t addcount);
|
||||
|
||||
|
||||
ContextParams mParams;
|
||||
|
||||
using VoiceArray = al::FlexArray<Voice*>;
|
||||
std::atomic<VoiceArray*> mVoices{};
|
||||
std::atomic<size_t> mActiveVoiceCount{};
|
||||
|
||||
void allocVoices(size_t addcount);
|
||||
al::span<Voice*> getVoicesSpan() const noexcept
|
||||
{
|
||||
return {mVoices.load(std::memory_order_relaxed)->data(),
|
||||
mActiveVoiceCount.load(std::memory_order_relaxed)};
|
||||
}
|
||||
al::span<Voice*> getVoicesSpanAcquired() const noexcept
|
||||
{
|
||||
return {mVoices.load(std::memory_order_acquire)->data(),
|
||||
mActiveVoiceCount.load(std::memory_order_acquire)};
|
||||
}
|
||||
|
||||
|
||||
using EffectSlotArray = al::FlexArray<EffectSlot*>;
|
||||
std::atomic<EffectSlotArray*> mActiveAuxSlots{nullptr};
|
||||
|
||||
std::thread mEventThread;
|
||||
al::semaphore mEventSem;
|
||||
std::unique_ptr<RingBuffer> mAsyncEvents;
|
||||
std::atomic<uint> mEnabledEvts{0u};
|
||||
|
||||
/* Asynchronous voice change actions are processed as a linked list of
|
||||
* VoiceChange objects by the mixer, which is atomically appended to.
|
||||
* However, to avoid allocating each object individually, they're allocated
|
||||
* in clusters that are stored in a vector for easy automatic cleanup.
|
||||
*/
|
||||
using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
|
||||
al::vector<VoiceChangeCluster> mVoiceChangeClusters;
|
||||
|
||||
using VoiceCluster = std::unique_ptr<Voice[]>;
|
||||
al::vector<VoiceCluster> mVoiceClusters;
|
||||
|
||||
/* Wet buffers used by effect slots. */
|
||||
al::vector<WetBufferPtr> mWetBuffers;
|
||||
|
||||
|
||||
std::atomic_flag mPropsClean;
|
||||
std::atomic<bool> mDeferUpdates{false};
|
||||
|
||||
std::mutex mPropLock;
|
||||
|
||||
std::atomic<ALenum> mLastError{AL_NO_ERROR};
|
||||
|
||||
DistanceModel mDistanceModel{DistanceModel::Default};
|
||||
bool mSourceDistanceModel{false};
|
||||
|
||||
float mDopplerFactor{1.0f};
|
||||
float mDopplerVelocity{1.0f};
|
||||
float mSpeedOfSound{SpeedOfSoundMetersPerSec};
|
||||
|
||||
std::mutex mEventCbLock;
|
||||
ALEVENTPROCSOFT mEventCb{};
|
||||
void *mEventParam{nullptr};
|
||||
|
||||
ALlistener mListener{};
|
||||
|
||||
al::vector<SourceSubList> mSourceList;
|
||||
ALuint mNumSources{0};
|
||||
std::mutex mSourceLock;
|
||||
|
||||
al::vector<EffectSlotSubList> mEffectSlotList;
|
||||
ALuint mNumEffectSlots{0u};
|
||||
std::mutex mEffectSlotLock;
|
||||
|
||||
/* Default effect slot */
|
||||
std::unique_ptr<ALeffectslot> mDefaultSlot;
|
||||
|
||||
const char *mExtensionList{nullptr};
|
||||
|
||||
|
||||
ALCcontext(al::intrusive_ptr<ALCdevice> device);
|
||||
ALCcontext(const ALCcontext&) = delete;
|
||||
ALCcontext& operator=(const ALCcontext&) = delete;
|
||||
~ALCcontext();
|
||||
|
||||
void init();
|
||||
/**
|
||||
* Removes the context from its device and removes it from being current on
|
||||
* the running thread or globally. Returns true if other contexts still
|
||||
* exist on the device.
|
||||
*/
|
||||
bool deinit();
|
||||
|
||||
/**
|
||||
* Defers/suspends updates for the given context's listener and sources.
|
||||
* This does *NOT* stop mixing, but rather prevents certain property
|
||||
* changes from taking effect.
|
||||
*/
|
||||
void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); }
|
||||
|
||||
/** Resumes update processing after being deferred. */
|
||||
void processUpdates();
|
||||
|
||||
[[gnu::format(printf,3,4)]] void setError(ALenum errorCode, const char *msg, ...);
|
||||
|
||||
DEF_NEWDEL(ALCcontext)
|
||||
};
|
||||
|
||||
#define SETERR_RETURN(ctx, err, retval, ...) do { \
|
||||
(ctx)->setError((err), __VA_ARGS__); \
|
||||
return retval; \
|
||||
} while(0)
|
||||
|
||||
|
||||
using ContextRef = al::intrusive_ptr<ALCcontext>;
|
||||
|
||||
ContextRef GetContextRef(void);
|
||||
|
||||
void UpdateContextProps(ALCcontext *context);
|
||||
|
||||
|
||||
extern bool TrapALError;
|
||||
|
||||
#endif /* ALCONTEXT_H */
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "oboe.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "alu.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include "oboe/Oboe.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char device_name[] = "Oboe Default";
|
||||
|
||||
|
||||
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
||||
OboePlayback(ALCdevice *device) : BackendBase{device} { }
|
||||
|
||||
oboe::ManagedStream mStream;
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||
int32_t numFrames) override;
|
||||
|
||||
void open(const char *name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
};
|
||||
|
||||
|
||||
oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||
int32_t numFrames)
|
||||
{
|
||||
assert(numFrames > 0);
|
||||
const int32_t numChannels{oboeStream->getChannelCount()};
|
||||
|
||||
if UNLIKELY(numChannels > 2 && mDevice->FmtChans == DevFmtStereo)
|
||||
{
|
||||
/* If the device is only mixing stereo but there's more than two
|
||||
* output channels, there are unused channels that need to be silenced.
|
||||
*/
|
||||
if(mStream->getFormat() == oboe::AudioFormat::Float)
|
||||
memset(audioData, 0, static_cast<uint32_t>(numFrames*numChannels)*sizeof(float));
|
||||
else
|
||||
memset(audioData, 0, static_cast<uint32_t>(numFrames*numChannels)*sizeof(int16_t));
|
||||
}
|
||||
|
||||
mDevice->renderSamples(audioData, static_cast<uint32_t>(numFrames),
|
||||
static_cast<uint32_t>(numChannels));
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
|
||||
void OboePlayback::open(const char *name)
|
||||
{
|
||||
if(!name)
|
||||
name = device_name;
|
||||
else if(std::strcmp(name, device_name) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
/* Open a basic output stream, just to ensure it can work. */
|
||||
oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->openManagedStream(mStream)};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool OboePlayback::reset()
|
||||
{
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Output);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
/* Don't let Oboe convert. We should be able to handle anything it gives
|
||||
* back.
|
||||
*/
|
||||
builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::None);
|
||||
builder.setChannelConversionAllowed(false);
|
||||
builder.setFormatConversionAllowed(false);
|
||||
builder.setCallback(this);
|
||||
|
||||
if(mDevice->Flags.test(FrequencyRequest))
|
||||
builder.setSampleRate(static_cast<int32_t>(mDevice->Frequency));
|
||||
if(mDevice->Flags.test(ChannelsRequest))
|
||||
{
|
||||
/* Only use mono or stereo at user request. There's no telling what
|
||||
* other counts may be inferred as.
|
||||
*/
|
||||
builder.setChannelCount((mDevice->FmtChans==DevFmtMono) ? oboe::ChannelCount::Mono
|
||||
: (mDevice->FmtChans==DevFmtStereo) ? oboe::ChannelCount::Stereo
|
||||
: oboe::ChannelCount::Unspecified);
|
||||
}
|
||||
if(mDevice->Flags.test(SampleTypeRequest))
|
||||
{
|
||||
oboe::AudioFormat format{oboe::AudioFormat::Unspecified};
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtUShort:
|
||||
format = oboe::AudioFormat::I16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
format = oboe::AudioFormat::Float;
|
||||
break;
|
||||
}
|
||||
builder.setFormat(format);
|
||||
}
|
||||
|
||||
oboe::Result result{builder.openManagedStream(mStream)};
|
||||
/* If the format failed, try asking for the defaults. */
|
||||
while(result == oboe::Result::ErrorInvalidFormat)
|
||||
{
|
||||
if(builder.getFormat() != oboe::AudioFormat::Unspecified)
|
||||
builder.setFormat(oboe::AudioFormat::Unspecified);
|
||||
else if(builder.getSampleRate() != oboe::kUnspecified)
|
||||
builder.setSampleRate(oboe::kUnspecified);
|
||||
else if(builder.getChannelCount() != oboe::ChannelCount::Unspecified)
|
||||
builder.setChannelCount(oboe::ChannelCount::Unspecified);
|
||||
else
|
||||
break;
|
||||
result = builder.openManagedStream(mStream);
|
||||
}
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||
|
||||
switch(mStream->getChannelCount())
|
||||
{
|
||||
case oboe::ChannelCount::Mono:
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
break;
|
||||
case oboe::ChannelCount::Stereo:
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
break;
|
||||
/* Other potential configurations. Could be wrong, but better than failing.
|
||||
* Assume WFX channel order.
|
||||
*/
|
||||
case 4:
|
||||
mDevice->FmtChans = DevFmtQuad;
|
||||
break;
|
||||
case 6:
|
||||
mDevice->FmtChans = DevFmtX51Rear;
|
||||
break;
|
||||
case 7:
|
||||
mDevice->FmtChans = DevFmtX61;
|
||||
break;
|
||||
case 8:
|
||||
mDevice->FmtChans = DevFmtX71;
|
||||
break;
|
||||
default:
|
||||
if(mStream->getChannelCount() < 1)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Got unhandled channel count: %d", mStream->getChannelCount()};
|
||||
/* Assume first two channels are front left/right. We can do a stereo
|
||||
* mix and keep the other channels silent.
|
||||
*/
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
break;
|
||||
}
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
switch(mStream->getFormat())
|
||||
{
|
||||
case oboe::AudioFormat::I16:
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
break;
|
||||
case oboe::AudioFormat::Float:
|
||||
mDevice->FmtType = DevFmtFloat;
|
||||
break;
|
||||
case oboe::AudioFormat::Unspecified:
|
||||
case oboe::AudioFormat::Invalid:
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Got unhandled sample type: %s", oboe::convertToText(mStream->getFormat())};
|
||||
}
|
||||
mDevice->Frequency = static_cast<uint32_t>(mStream->getSampleRate());
|
||||
|
||||
/* Ensure the period size is no less than 10ms. It's possible for FramesPerCallback to be 0
|
||||
* indicating variable updates, but OpenAL should have a reasonable minimum update size set.
|
||||
* FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
|
||||
* update size.
|
||||
*/
|
||||
mDevice->UpdateSize = maxu(mDevice->Frequency / 100,
|
||||
static_cast<uint32_t>(mStream->getFramesPerBurst()));
|
||||
mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
|
||||
static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OboePlayback::start()
|
||||
{
|
||||
const oboe::Result result{mStream->start()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
}
|
||||
|
||||
void OboePlayback::stop()
|
||||
{
|
||||
oboe::Result result{mStream->stop()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool OboeBackendFactory::init() { return true; }
|
||||
|
||||
bool OboeBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback; }
|
||||
|
||||
std::string OboeBackendFactory::probe(BackendType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
/* Includes null char. */
|
||||
return std::string{device_name, sizeof(device_name)};
|
||||
case BackendType::Capture:
|
||||
break;
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
BackendPtr OboeBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OboePlayback{device}};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BackendFactory &OboeBackendFactory::getFactory()
|
||||
{
|
||||
static OboeBackendFactory factory{};
|
||||
return factory;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#ifndef AL_COMPAT_H
|
||||
#define AL_COMPAT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
struct PathNamePair { std::string path, fname; };
|
||||
const PathNamePair &GetProcBinary(void);
|
||||
|
||||
#endif /* AL_COMPAT_H */
|
||||
|
|
@ -1,793 +0,0 @@
|
|||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "voice.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "albyte.h"
|
||||
#include "alconfig.h"
|
||||
#include "alcontext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "alu.h"
|
||||
#include "async_event.h"
|
||||
#include "buffer_storage.h"
|
||||
#include "core/cpu_caps.h"
|
||||
#include "core/devformat.h"
|
||||
#include "core/filters/biquad.h"
|
||||
#include "core/filters/nfc.h"
|
||||
#include "core/filters/splitter.h"
|
||||
#include "core/fmt_traits.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "core/mixer/hrtfdefs.h"
|
||||
#include "hrtf.h"
|
||||
#include "inprogext.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
#include "voice_change.h"
|
||||
|
||||
struct CTag;
|
||||
#ifdef HAVE_SSE
|
||||
struct SSETag;
|
||||
#endif
|
||||
#ifdef HAVE_NEON
|
||||
struct NEONTag;
|
||||
#endif
|
||||
struct CopyTag;
|
||||
|
||||
|
||||
Resampler ResamplerDefault{Resampler::Linear};
|
||||
|
||||
MixerFunc MixSamples{Mix_<CTag>};
|
||||
|
||||
namespace {
|
||||
|
||||
using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize,
|
||||
const MixHrtfFilter *hrtfparams, const size_t BufferSize);
|
||||
using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples,
|
||||
const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams,
|
||||
const size_t BufferSize);
|
||||
|
||||
HrtfMixerFunc MixHrtfSamples{MixHrtf_<CTag>};
|
||||
HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_<CTag>};
|
||||
|
||||
inline MixerFunc SelectMixer()
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Mix_<NEONTag>;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_<SSETag>;
|
||||
#endif
|
||||
return Mix_<CTag>;
|
||||
}
|
||||
|
||||
inline HrtfMixerFunc SelectHrtfMixer()
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtf_<NEONTag>;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtf_<SSETag>;
|
||||
#endif
|
||||
return MixHrtf_<CTag>;
|
||||
}
|
||||
|
||||
inline HrtfMixerBlendFunc SelectHrtfBlendMixer()
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtfBlend_<NEONTag>;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtfBlend_<SSETag>;
|
||||
#endif
|
||||
return MixHrtfBlend_<CTag>;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void aluInitMixer()
|
||||
{
|
||||
if(auto resopt = ConfigValueStr(nullptr, nullptr, "resampler"))
|
||||
{
|
||||
struct ResamplerEntry {
|
||||
const char name[16];
|
||||
const Resampler resampler;
|
||||
};
|
||||
constexpr ResamplerEntry ResamplerList[]{
|
||||
{ "none", Resampler::Point },
|
||||
{ "point", Resampler::Point },
|
||||
{ "linear", Resampler::Linear },
|
||||
{ "cubic", Resampler::Cubic },
|
||||
{ "bsinc12", Resampler::BSinc12 },
|
||||
{ "fast_bsinc12", Resampler::FastBSinc12 },
|
||||
{ "bsinc24", Resampler::BSinc24 },
|
||||
{ "fast_bsinc24", Resampler::FastBSinc24 },
|
||||
};
|
||||
|
||||
const char *str{resopt->c_str()};
|
||||
if(al::strcasecmp(str, "bsinc") == 0)
|
||||
{
|
||||
WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
|
||||
str = "bsinc12";
|
||||
}
|
||||
else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0)
|
||||
{
|
||||
WARN("Resampler option \"%s\" is deprecated, using cubic\n", str);
|
||||
str = "cubic";
|
||||
}
|
||||
|
||||
auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList),
|
||||
[str](const ResamplerEntry &entry) -> bool
|
||||
{ return al::strcasecmp(str, entry.name) == 0; });
|
||||
if(iter == std::end(ResamplerList))
|
||||
ERR("Invalid resampler: %s\n", str);
|
||||
else
|
||||
ResamplerDefault = iter->resampler;
|
||||
}
|
||||
|
||||
MixSamples = SelectMixer();
|
||||
MixHrtfBlendSamples = SelectHrtfBlendMixer();
|
||||
MixHrtfSamples = SelectHrtfMixer();
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void SendSourceStoppedEvent(ALCcontext *context, uint id)
|
||||
{
|
||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||
auto evt_vec = ring->getWriteVector();
|
||||
if(evt_vec.first.len < 1) return;
|
||||
|
||||
AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
|
||||
evt->u.srcstate.id = id;
|
||||
evt->u.srcstate.state = VChangeState::Stop;
|
||||
|
||||
ring->writeAdvance(1);
|
||||
}
|
||||
|
||||
|
||||
const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *dst,
|
||||
const al::span<const float> src, int type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
lpfilter.clear();
|
||||
hpfilter.clear();
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
lpfilter.process(src, dst);
|
||||
hpfilter.clear();
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
lpfilter.clear();
|
||||
hpfilter.process(src, dst);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
DualBiquad{lpfilter, hpfilter}.process(src, dst);
|
||||
return dst;
|
||||
}
|
||||
return src.data();
|
||||
}
|
||||
|
||||
|
||||
void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, FmtType srctype,
|
||||
const size_t samples) noexcept
|
||||
{
|
||||
#define HANDLE_FMT(T) case T: al::LoadSampleArray<T>(dst, src, srcstep, samples); break
|
||||
switch(srctype)
|
||||
{
|
||||
HANDLE_FMT(FmtUByte);
|
||||
HANDLE_FMT(FmtShort);
|
||||
HANDLE_FMT(FmtFloat);
|
||||
HANDLE_FMT(FmtDouble);
|
||||
HANDLE_FMT(FmtMulaw);
|
||||
HANDLE_FMT(FmtAlaw);
|
||||
}
|
||||
#undef HANDLE_FMT
|
||||
}
|
||||
|
||||
float *LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *&bufferLoopItem,
|
||||
const size_t numChannels, const FmtType sampleType, const size_t sampleSize, const size_t chan,
|
||||
size_t dataPosInt, al::span<float> srcBuffer)
|
||||
{
|
||||
const uint LoopStart{buffer->mLoopStart};
|
||||
const uint LoopEnd{buffer->mLoopEnd};
|
||||
ASSUME(LoopEnd > LoopStart);
|
||||
|
||||
/* If current pos is beyond the loop range, do not loop */
|
||||
if(!bufferLoopItem || dataPosInt >= LoopEnd)
|
||||
{
|
||||
bufferLoopItem = nullptr;
|
||||
|
||||
/* Load what's left to play from the buffer */
|
||||
const size_t DataRem{minz(srcBuffer.size(), buffer->mSampleLen-dataPosInt)};
|
||||
|
||||
const al::byte *Data{buffer->mSamples + (dataPosInt*numChannels + chan)*sampleSize};
|
||||
LoadSamples(srcBuffer.data(), Data, numChannels, sampleType, DataRem);
|
||||
srcBuffer = srcBuffer.subspan(DataRem);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Load what's left of this loop iteration */
|
||||
const size_t DataRem{minz(srcBuffer.size(), LoopEnd-dataPosInt)};
|
||||
|
||||
const al::byte *Data{buffer->mSamples + (dataPosInt*numChannels + chan)*sampleSize};
|
||||
LoadSamples(srcBuffer.data(), Data, numChannels, sampleType, DataRem);
|
||||
srcBuffer = srcBuffer.subspan(DataRem);
|
||||
|
||||
/* Load any repeats of the loop we can to fill the buffer. */
|
||||
const auto LoopSize = static_cast<size_t>(LoopEnd - LoopStart);
|
||||
while(!srcBuffer.empty())
|
||||
{
|
||||
const size_t DataSize{minz(srcBuffer.size(), LoopSize)};
|
||||
|
||||
Data = buffer->mSamples + (LoopStart*numChannels + chan)*sampleSize;
|
||||
|
||||
LoadSamples(srcBuffer.data(), Data, numChannels, sampleType, DataSize);
|
||||
srcBuffer = srcBuffer.subspan(DataSize);
|
||||
}
|
||||
}
|
||||
return srcBuffer.begin();
|
||||
}
|
||||
|
||||
float *LoadBufferCallback(VoiceBufferItem *buffer, const size_t numChannels,
|
||||
const FmtType sampleType, const size_t sampleSize, const size_t chan,
|
||||
size_t numCallbackSamples, al::span<float> srcBuffer)
|
||||
{
|
||||
/* Load what's left to play from the buffer */
|
||||
const size_t DataRem{minz(srcBuffer.size(), numCallbackSamples)};
|
||||
|
||||
const al::byte *Data{buffer->mSamples + chan*sampleSize};
|
||||
LoadSamples(srcBuffer.data(), Data, numChannels, sampleType, DataRem);
|
||||
srcBuffer = srcBuffer.subspan(DataRem);
|
||||
|
||||
return srcBuffer.begin();
|
||||
}
|
||||
|
||||
float *LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem,
|
||||
const size_t numChannels, const FmtType sampleType, const size_t sampleSize, const size_t chan,
|
||||
size_t dataPosInt, al::span<float> srcBuffer)
|
||||
{
|
||||
/* Crawl the buffer queue to fill in the temp buffer */
|
||||
while(buffer && !srcBuffer.empty())
|
||||
{
|
||||
if(dataPosInt >= buffer->mSampleLen)
|
||||
{
|
||||
dataPosInt -= buffer->mSampleLen;
|
||||
buffer = buffer->mNext.load(std::memory_order_acquire);
|
||||
if(!buffer) buffer = bufferLoopItem;
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t DataSize{minz(srcBuffer.size(), buffer->mSampleLen-dataPosInt)};
|
||||
|
||||
const al::byte *Data{buffer->mSamples + (dataPosInt*numChannels + chan)*sampleSize};
|
||||
LoadSamples(srcBuffer.data(), Data, numChannels, sampleType, DataSize);
|
||||
srcBuffer = srcBuffer.subspan(DataSize);
|
||||
if(srcBuffer.empty()) break;
|
||||
|
||||
dataPosInt = 0;
|
||||
buffer = buffer->mNext.load(std::memory_order_acquire);
|
||||
if(!buffer) buffer = bufferLoopItem;
|
||||
}
|
||||
|
||||
return srcBuffer.begin();
|
||||
}
|
||||
|
||||
|
||||
void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &parms,
|
||||
const float TargetGain, const uint Counter, uint OutPos, const uint IrSize,
|
||||
ALCdevice *Device)
|
||||
{
|
||||
auto &HrtfSamples = Device->HrtfSourceData;
|
||||
/* Source HRTF mixing needs to include the direct delay so it remains
|
||||
* aligned with the direct mix's HRTF filtering.
|
||||
*/
|
||||
float2 *AccumSamples{Device->HrtfAccumData + HrtfDirectDelay};
|
||||
|
||||
/* Copy the HRTF history and new input samples into a temp buffer. */
|
||||
auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(),
|
||||
std::begin(HrtfSamples));
|
||||
std::copy_n(samples, DstBufferSize, src_iter);
|
||||
/* Copy the last used samples back into the history buffer for later. */
|
||||
std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.History.size(),
|
||||
parms.Hrtf.History.begin());
|
||||
|
||||
/* If fading and this is the first mixing pass, fade between the IRs. */
|
||||
uint fademix{0u};
|
||||
if(Counter && OutPos == 0)
|
||||
{
|
||||
fademix = minu(DstBufferSize, Counter);
|
||||
|
||||
float gain{TargetGain};
|
||||
|
||||
/* The new coefficients need to fade in completely since they're
|
||||
* replacing the old ones. To keep the gain fading consistent,
|
||||
* interpolate between the old and new target gains given how much of
|
||||
* the fade time this mix handles.
|
||||
*/
|
||||
if(Counter > fademix)
|
||||
{
|
||||
const float a{static_cast<float>(fademix) / static_cast<float>(Counter)};
|
||||
gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a);
|
||||
}
|
||||
MixHrtfFilter hrtfparams;
|
||||
hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
|
||||
hrtfparams.Delay = parms.Hrtf.Target.Delay;
|
||||
hrtfparams.Gain = 0.0f;
|
||||
hrtfparams.GainStep = gain / static_cast<float>(fademix);
|
||||
|
||||
MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams,
|
||||
fademix);
|
||||
/* Update the old parameters with the result. */
|
||||
parms.Hrtf.Old = parms.Hrtf.Target;
|
||||
parms.Hrtf.Old.Gain = gain;
|
||||
OutPos += fademix;
|
||||
}
|
||||
|
||||
if(fademix < DstBufferSize)
|
||||
{
|
||||
const uint todo{DstBufferSize - fademix};
|
||||
float gain{TargetGain};
|
||||
|
||||
/* Interpolate the target gain if the gain fading lasts longer than
|
||||
* this mix.
|
||||
*/
|
||||
if(Counter > DstBufferSize)
|
||||
{
|
||||
const float a{static_cast<float>(todo) / static_cast<float>(Counter-fademix)};
|
||||
gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a);
|
||||
}
|
||||
|
||||
MixHrtfFilter hrtfparams;
|
||||
hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
|
||||
hrtfparams.Delay = parms.Hrtf.Target.Delay;
|
||||
hrtfparams.Gain = parms.Hrtf.Old.Gain;
|
||||
hrtfparams.GainStep = (gain - parms.Hrtf.Old.Gain) / static_cast<float>(todo);
|
||||
MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo);
|
||||
/* Store the now-current gain for next time. */
|
||||
parms.Hrtf.Old.Gain = gain;
|
||||
}
|
||||
}
|
||||
|
||||
void DoNfcMix(const al::span<const float> samples, FloatBufferLine *OutBuffer, DirectParams &parms,
|
||||
const float *TargetGains, const uint Counter, const uint OutPos, ALCdevice *Device)
|
||||
{
|
||||
using FilterProc = void (NfcFilter::*)(const al::span<const float>, float*);
|
||||
static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{
|
||||
nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3};
|
||||
|
||||
float *CurrentGains{parms.Gains.Current.data()};
|
||||
MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos);
|
||||
++OutBuffer;
|
||||
++CurrentGains;
|
||||
++TargetGains;
|
||||
|
||||
const al::span<float> nfcsamples{Device->NfcSampleData, samples.size()};
|
||||
size_t order{1};
|
||||
while(const size_t chancount{Device->NumChannelsPerOrder[order]})
|
||||
{
|
||||
(parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples.data());
|
||||
MixSamples(nfcsamples, {OutBuffer, chancount}, CurrentGains, TargetGains, Counter, OutPos);
|
||||
OutBuffer += chancount;
|
||||
CurrentGains += chancount;
|
||||
TargetGains += chancount;
|
||||
if(++order == MaxAmbiOrder+1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Voice::mix(const State vstate, ALCcontext *Context, const uint SamplesToDo)
|
||||
{
|
||||
static constexpr std::array<float,MAX_OUTPUT_CHANNELS> SilentTarget{};
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
/* Get voice info */
|
||||
uint DataPosInt{mPosition.load(std::memory_order_relaxed)};
|
||||
uint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)};
|
||||
VoiceBufferItem *BufferListItem{mCurrentBuffer.load(std::memory_order_relaxed)};
|
||||
VoiceBufferItem *BufferLoopItem{mLoopBuffer.load(std::memory_order_relaxed)};
|
||||
const FmtType SampleType{mFmtType};
|
||||
const uint SampleSize{mSampleSize};
|
||||
const uint increment{mStep};
|
||||
if UNLIKELY(increment < 1)
|
||||
{
|
||||
/* If the voice is supposed to be stopping but can't be mixed, just
|
||||
* stop it before bailing.
|
||||
*/
|
||||
if(vstate == Stopping)
|
||||
mPlayState.store(Stopped, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSUME(SampleSize > 0);
|
||||
|
||||
const size_t FrameSize{mChans.size() * SampleSize};
|
||||
ASSUME(FrameSize > 0);
|
||||
|
||||
ALCdevice *Device{Context->mDevice.get()};
|
||||
const uint NumSends{Device->NumAuxSends};
|
||||
const uint IrSize{Device->mIrSize};
|
||||
|
||||
ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) ?
|
||||
Resample_<CopyTag,CTag> : mResampler};
|
||||
|
||||
uint Counter{(mFlags&VoiceIsFading) ? SamplesToDo : 0};
|
||||
if(!Counter)
|
||||
{
|
||||
/* No fading, just overwrite the old/current params. */
|
||||
for(auto &chandata : mChans)
|
||||
{
|
||||
{
|
||||
DirectParams &parms = chandata.mDryParams;
|
||||
if(!(mFlags&VoiceHasHrtf))
|
||||
parms.Gains.Current = parms.Gains.Target;
|
||||
else
|
||||
parms.Hrtf.Old = parms.Hrtf.Target;
|
||||
}
|
||||
for(uint send{0};send < NumSends;++send)
|
||||
{
|
||||
if(mSend[send].Buffer.empty())
|
||||
continue;
|
||||
|
||||
SendParams &parms = chandata.mWetParams[send];
|
||||
parms.Gains.Current = parms.Gains.Target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float fadeCoeff{1.0f}, fadeGain{1.0f};
|
||||
if UNLIKELY(vstate == Stopping)
|
||||
{
|
||||
/* Calculate the multiplier for fading the resampled signal by -60dB
|
||||
* over 1ms.
|
||||
*/
|
||||
fadeCoeff = std::pow(0.001f, 1000.0f/static_cast<float>(Device->Frequency));
|
||||
}
|
||||
|
||||
uint buffers_done{0u};
|
||||
uint OutPos{0u};
|
||||
do {
|
||||
/* Figure out how many buffer samples will be needed */
|
||||
uint DstBufferSize{SamplesToDo - OutPos};
|
||||
uint SrcBufferSize;
|
||||
|
||||
if(increment <= MixerFracOne)
|
||||
{
|
||||
/* Calculate the last written dst sample pos. */
|
||||
uint64_t DataSize64{DstBufferSize - 1};
|
||||
/* Calculate the last read src sample pos. */
|
||||
DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits;
|
||||
/* +1 to get the src sample count, include padding. */
|
||||
DataSize64 += 1 + MaxResamplerPadding;
|
||||
|
||||
/* Result is guaranteed to be <= BufferLineSize+MaxResamplerPadding
|
||||
* since we won't use more src samples than dst samples+padding.
|
||||
*/
|
||||
SrcBufferSize = static_cast<uint>(DataSize64);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t DataSize64{DstBufferSize};
|
||||
/* Calculate the end src sample pos, include padding. */
|
||||
DataSize64 = (DataSize64*increment + DataPosFrac) >> MixerFracBits;
|
||||
DataSize64 += MaxResamplerPadding;
|
||||
|
||||
if(DataSize64 <= BufferLineSize + MaxResamplerPadding)
|
||||
SrcBufferSize = static_cast<uint>(DataSize64);
|
||||
else
|
||||
{
|
||||
/* If the source size got saturated, we can't fill the desired
|
||||
* dst size. Figure out how many samples we can actually mix.
|
||||
*/
|
||||
SrcBufferSize = BufferLineSize + MaxResamplerPadding;
|
||||
|
||||
DataSize64 = SrcBufferSize - MaxResamplerPadding;
|
||||
DataSize64 = ((DataSize64<<MixerFracBits) - DataPosFrac) / increment;
|
||||
if(DataSize64 < DstBufferSize)
|
||||
{
|
||||
/* Some mixers require being 16-byte aligned, so also limit
|
||||
* to a multiple of 4 samples to maintain alignment.
|
||||
*/
|
||||
DstBufferSize = static_cast<uint>(DataSize64) & ~3u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if((mFlags&(VoiceIsCallback|VoiceCallbackStopped)) == VoiceIsCallback && BufferListItem)
|
||||
{
|
||||
/* Exclude resampler pre-padding from the needed size. */
|
||||
const uint toLoad{SrcBufferSize - (MaxResamplerPadding>>1)};
|
||||
if(toLoad > mNumCallbackSamples)
|
||||
{
|
||||
const size_t byteOffset{mNumCallbackSamples*FrameSize};
|
||||
const size_t needBytes{toLoad*FrameSize - byteOffset};
|
||||
|
||||
const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData,
|
||||
&BufferListItem->mSamples[byteOffset], static_cast<int>(needBytes))};
|
||||
if(gotBytes < 1)
|
||||
mFlags |= VoiceCallbackStopped;
|
||||
else if(static_cast<uint>(gotBytes) < needBytes)
|
||||
{
|
||||
mFlags |= VoiceCallbackStopped;
|
||||
mNumCallbackSamples += static_cast<uint>(static_cast<uint>(gotBytes) /
|
||||
FrameSize);
|
||||
}
|
||||
else
|
||||
mNumCallbackSamples = toLoad;
|
||||
}
|
||||
}
|
||||
|
||||
const float fadeVal{fadeGain};
|
||||
const size_t num_chans{mChans.size()};
|
||||
size_t chan_idx{0};
|
||||
ASSUME(DstBufferSize > 0);
|
||||
for(auto &chandata : mChans)
|
||||
{
|
||||
const al::span<float> SrcData{Device->SourceData, SrcBufferSize};
|
||||
|
||||
/* Load the previous samples into the source data first, then load
|
||||
* what we can from the buffer queue.
|
||||
*/
|
||||
auto srciter = std::copy_n(chandata.mPrevSamples.begin(), MaxResamplerPadding>>1,
|
||||
SrcData.begin());
|
||||
|
||||
if UNLIKELY(!BufferListItem)
|
||||
srciter = std::copy(chandata.mPrevSamples.begin()+(MaxResamplerPadding>>1),
|
||||
chandata.mPrevSamples.end(), srciter);
|
||||
else if((mFlags&VoiceIsStatic))
|
||||
srciter = LoadBufferStatic(BufferListItem, BufferLoopItem, num_chans, SampleType,
|
||||
SampleSize, chan_idx, DataPosInt, {srciter, SrcData.end()});
|
||||
else if((mFlags&VoiceIsCallback))
|
||||
srciter = LoadBufferCallback(BufferListItem, num_chans, SampleType, SampleSize,
|
||||
chan_idx, mNumCallbackSamples, {srciter, SrcData.end()});
|
||||
else
|
||||
srciter = LoadBufferQueue(BufferListItem, BufferLoopItem, num_chans, SampleType,
|
||||
SampleSize, chan_idx, DataPosInt, {srciter, SrcData.end()});
|
||||
|
||||
if UNLIKELY(srciter != SrcData.end())
|
||||
{
|
||||
/* If the source buffer wasn't filled, copy the last sample for
|
||||
* the remaining buffer. Ideally it should have ended with
|
||||
* silence, but if not the gain fading should help avoid clicks
|
||||
* from sudden amplitude changes.
|
||||
*/
|
||||
const float sample{*(srciter-1)};
|
||||
std::fill(srciter, SrcData.end(), sample);
|
||||
}
|
||||
|
||||
/* Store the last source samples used for next time. */
|
||||
std::copy_n(&SrcData[(increment*DstBufferSize + DataPosFrac)>>MixerFracBits],
|
||||
chandata.mPrevSamples.size(), chandata.mPrevSamples.begin());
|
||||
|
||||
/* Resample, then apply ambisonic upsampling as needed. */
|
||||
float *ResampledData{Resample(&mResampleState, &SrcData[MaxResamplerPadding>>1],
|
||||
DataPosFrac, increment, {Device->ResampledData, DstBufferSize})};
|
||||
if((mFlags&VoiceIsAmbisonic))
|
||||
chandata.mAmbiSplitter.processHfScale({ResampledData, DstBufferSize},
|
||||
chandata.mAmbiScale);
|
||||
|
||||
if UNLIKELY(vstate == Stopping)
|
||||
{
|
||||
fadeGain = fadeVal;
|
||||
for(float &sample : al::span<float>{ResampledData, DstBufferSize})
|
||||
{
|
||||
fadeGain *= fadeCoeff;
|
||||
sample *= fadeGain;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now filter and mix to the appropriate outputs. */
|
||||
float (&FilterBuf)[BufferLineSize] = Device->FilteredData;
|
||||
{
|
||||
DirectParams &parms = chandata.mDryParams;
|
||||
const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf,
|
||||
{ResampledData, DstBufferSize}, mDirect.FilterType)};
|
||||
|
||||
if((mFlags&VoiceHasHrtf))
|
||||
{
|
||||
const float TargetGain{UNLIKELY(vstate == Stopping) ? 0.0f :
|
||||
parms.Hrtf.Target.Gain};
|
||||
DoHrtfMix(samples, DstBufferSize, parms, TargetGain, Counter, OutPos, IrSize,
|
||||
Device);
|
||||
}
|
||||
else if((mFlags&VoiceHasNfc))
|
||||
{
|
||||
const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data()
|
||||
: parms.Gains.Target.data()};
|
||||
DoNfcMix({samples, DstBufferSize}, mDirect.Buffer.data(), parms, TargetGains,
|
||||
Counter, OutPos, Device);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data()
|
||||
: parms.Gains.Target.data()};
|
||||
MixSamples({samples, DstBufferSize}, mDirect.Buffer,
|
||||
parms.Gains.Current.data(), TargetGains, Counter, OutPos);
|
||||
}
|
||||
}
|
||||
|
||||
for(uint send{0};send < NumSends;++send)
|
||||
{
|
||||
if(mSend[send].Buffer.empty())
|
||||
continue;
|
||||
|
||||
SendParams &parms = chandata.mWetParams[send];
|
||||
const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf,
|
||||
{ResampledData, DstBufferSize}, mSend[send].FilterType)};
|
||||
|
||||
const float *TargetGains{UNLIKELY(vstate == Stopping) ? SilentTarget.data()
|
||||
: parms.Gains.Target.data()};
|
||||
MixSamples({samples, DstBufferSize}, mSend[send].Buffer,
|
||||
parms.Gains.Current.data(), TargetGains, Counter, OutPos);
|
||||
}
|
||||
|
||||
++chan_idx;
|
||||
}
|
||||
/* Update positions */
|
||||
DataPosFrac += increment*DstBufferSize;
|
||||
const uint SrcSamplesDone{DataPosFrac>>MixerFracBits};
|
||||
DataPosInt += SrcSamplesDone;
|
||||
DataPosFrac &= MixerFracMask;
|
||||
|
||||
OutPos += DstBufferSize;
|
||||
Counter = maxu(DstBufferSize, Counter) - DstBufferSize;
|
||||
|
||||
if UNLIKELY(!BufferListItem)
|
||||
{
|
||||
/* Do nothing extra when there's no buffers. */
|
||||
}
|
||||
else if((mFlags&VoiceIsStatic))
|
||||
{
|
||||
if(BufferLoopItem)
|
||||
{
|
||||
/* Handle looping static source */
|
||||
const uint LoopStart{BufferListItem->mLoopStart};
|
||||
const uint LoopEnd{BufferListItem->mLoopEnd};
|
||||
if(DataPosInt >= LoopEnd)
|
||||
{
|
||||
assert(LoopEnd > LoopStart);
|
||||
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Handle non-looping static source */
|
||||
if(DataPosInt >= BufferListItem->mSampleLen)
|
||||
{
|
||||
BufferListItem = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if((mFlags&VoiceIsCallback))
|
||||
{
|
||||
if(SrcSamplesDone < mNumCallbackSamples)
|
||||
{
|
||||
const size_t byteOffset{SrcSamplesDone*FrameSize};
|
||||
const size_t byteEnd{mNumCallbackSamples*FrameSize};
|
||||
al::byte *data{BufferListItem->mSamples};
|
||||
std::copy(data+byteOffset, data+byteEnd, data);
|
||||
mNumCallbackSamples -= SrcSamplesDone;
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferListItem = nullptr;
|
||||
mNumCallbackSamples = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Handle streaming source */
|
||||
do {
|
||||
if(BufferListItem->mSampleLen > DataPosInt)
|
||||
break;
|
||||
|
||||
DataPosInt -= BufferListItem->mSampleLen;
|
||||
|
||||
++buffers_done;
|
||||
BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed);
|
||||
if(!BufferListItem) BufferListItem = BufferLoopItem;
|
||||
} while(BufferListItem);
|
||||
}
|
||||
} while(OutPos < SamplesToDo);
|
||||
|
||||
mFlags |= VoiceIsFading;
|
||||
|
||||
/* Don't update positions and buffers if we were stopping. */
|
||||
if UNLIKELY(vstate == Stopping)
|
||||
{
|
||||
mPlayState.store(Stopped, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Capture the source ID in case it's reset for stopping. */
|
||||
const uint SourceID{mSourceID.load(std::memory_order_relaxed)};
|
||||
|
||||
/* Update voice info */
|
||||
mPosition.store(DataPosInt, std::memory_order_relaxed);
|
||||
mPositionFrac.store(DataPosFrac, std::memory_order_relaxed);
|
||||
mCurrentBuffer.store(BufferListItem, std::memory_order_relaxed);
|
||||
if(!BufferListItem)
|
||||
{
|
||||
mLoopBuffer.store(nullptr, std::memory_order_relaxed);
|
||||
mSourceID.store(0u, std::memory_order_relaxed);
|
||||
}
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
|
||||
/* Send any events now, after the position/buffer info was updated. */
|
||||
const uint enabledevt{Context->mEnabledEvts.load(std::memory_order_acquire)};
|
||||
if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted))
|
||||
{
|
||||
RingBuffer *ring{Context->mAsyncEvents.get()};
|
||||
auto evt_vec = ring->getWriteVector();
|
||||
if(evt_vec.first.len > 0)
|
||||
{
|
||||
AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_BufferCompleted}};
|
||||
evt->u.bufcomp.id = SourceID;
|
||||
evt->u.bufcomp.count = buffers_done;
|
||||
ring->writeAdvance(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(!BufferListItem)
|
||||
{
|
||||
/* If the voice just ended, set it to Stopping so the next render
|
||||
* ensures any residual noise fades to 0 amplitude.
|
||||
*/
|
||||
mPlayState.store(Stopping, std::memory_order_release);
|
||||
if((enabledevt&EventType_SourceStateChange))
|
||||
SendSourceStoppedEvent(Context, SourceID);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
Portions of this software are licensed under the BSD 3-Clause license.
|
||||
|
||||
Copyright (c) 2015, Archontis Politis
|
||||
Copyright (c) 2019, Anis A. Hireche
|
||||
Copyright (c) 2019, Christopher Robinson
|
||||
All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,24 @@
|
|||
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
||||
# The workaround for try_compile failing with code signing
|
||||
# since cmake-3.18.2, not required
|
||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED"
|
||||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
||||
if(APPLE)
|
||||
# The workaround for try_compile failing with code signing
|
||||
# since cmake-3.18.2, not required
|
||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}
|
||||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED"
|
||||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
||||
endif()
|
||||
|
||||
# Fix compile failure with armv7 deployment target >= 11.0, xcode clang will
|
||||
# report:
|
||||
# error: invalid iOS deployment version '--target=armv7-apple-ios13.6',
|
||||
# iOS 10 is the maximum deployment target for 32-bit targets
|
||||
# If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest
|
||||
# deployment target
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
# Fix compile failure with armv7 deployment target >= 11.0, xcode clang
|
||||
# will report:
|
||||
# error: invalid iOS deployment version '--target=armv7-apple-ios13.6',
|
||||
# iOS 10 is the maximum deployment target for 32-bit targets
|
||||
# If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest
|
||||
# deployment target
|
||||
if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*")
|
||||
if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
|
||||
OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0")
|
||||
|
|
@ -69,8 +72,11 @@ include(CheckCXXCompilerFlag)
|
|||
include(CheckCSourceCompiles)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckStructHasMember)
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
|
||||
option(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON)
|
||||
|
||||
|
|
@ -89,6 +95,8 @@ option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)
|
|||
option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
|
||||
option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
|
||||
|
||||
option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
|
||||
|
||||
if(DEFINED SHARE_INSTALL_DIR)
|
||||
message(WARNING "SHARE_INSTALL_DIR is deprecated. Use the variables provided by the GNUInstallDirs module instead")
|
||||
set(CMAKE_INSTALL_DATADIR "${SHARE_INSTALL_DIR}")
|
||||
|
|
@ -115,7 +123,7 @@ set(LINKER_FLAGS )
|
|||
set(EXTRA_LIBS )
|
||||
|
||||
if(WIN32)
|
||||
set(CPP_DEFS ${CPP_DEFS} _WIN32)
|
||||
set(CPP_DEFS ${CPP_DEFS} _WIN32 NOMINMAX)
|
||||
if(MINGW)
|
||||
set(CPP_DEFS ${CPP_DEFS} __USE_MINGW_ANSI_STDIO)
|
||||
endif()
|
||||
|
|
@ -142,7 +150,7 @@ if(NOT LIBTYPE)
|
|||
endif()
|
||||
|
||||
set(LIB_MAJOR_VERSION "1")
|
||||
set(LIB_MINOR_VERSION "21")
|
||||
set(LIB_MINOR_VERSION "22")
|
||||
set(LIB_REVISION "0")
|
||||
set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}")
|
||||
set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
|
||||
|
|
@ -196,20 +204,22 @@ else()
|
|||
endif()
|
||||
unset(OLD_REQUIRED_LIBRARIES)
|
||||
|
||||
# Include liblog for Android logging
|
||||
check_library_exists(log __android_log_print "" HAVE_LIBLOG)
|
||||
if(HAVE_LIBLOG)
|
||||
set(EXTRA_LIBS log ${EXTRA_LIBS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log)
|
||||
if(ANDROID)
|
||||
# Include liblog for Android logging
|
||||
check_library_exists(log __android_log_print "" HAVE_LIBLOG)
|
||||
if(HAVE_LIBLOG)
|
||||
set(EXTRA_LIBS log ${EXTRA_LIBS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} log)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS NOMINMAX)
|
||||
set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS)
|
||||
check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH)
|
||||
if(HAVE_PERMISSIVE_SWITCH)
|
||||
set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:/permissive->)
|
||||
endif()
|
||||
set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4268 /wd4324 /wd5030)
|
||||
set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030)
|
||||
# Remove /W3, which is added by default, since we set /W4. Some build
|
||||
# generators with MSVC complain about both /W3 and /W4 being specified.
|
||||
foreach(flag_var CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
|
||||
|
|
@ -345,17 +355,17 @@ endif()
|
|||
|
||||
set(SSE2_SWITCH "")
|
||||
|
||||
set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
if(NOT MSVC)
|
||||
set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
# Yes GCC, really don't accept command line options you don't support
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Werror")
|
||||
check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH)
|
||||
if(HAVE_MSSE2_SWITCH)
|
||||
set(SSE2_SWITCH "-msse2")
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
|
||||
unset(OLD_REQUIRED_FLAGS)
|
||||
endif()
|
||||
check_c_compiler_flag(-msse2 HAVE_MSSE2_SWITCH)
|
||||
if(HAVE_MSSE2_SWITCH)
|
||||
set(SSE2_SWITCH "-msse2")
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
|
||||
unset(OLD_REQUIRED_FLAGS)
|
||||
|
||||
check_include_file(xmmintrin.h HAVE_XMMINTRIN_H)
|
||||
check_include_file(emmintrin.h HAVE_EMMINTRIN_H)
|
||||
|
|
@ -369,20 +379,25 @@ set(HAVE_SSE3 0)
|
|||
set(HAVE_SSE4_1 0)
|
||||
set(HAVE_NEON 0)
|
||||
|
||||
# Check for SSE+SSE2 support
|
||||
# Check for SSE support
|
||||
option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF)
|
||||
option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
|
||||
if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H)
|
||||
if(HAVE_XMMINTRIN_H)
|
||||
option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON)
|
||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||
if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2)
|
||||
if(ALSOFT_CPUEXT_SSE)
|
||||
set(HAVE_SSE 1)
|
||||
set(HAVE_SSE2 1)
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
||||
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
||||
endif()
|
||||
|
||||
option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
|
||||
if(HAVE_EMMINTRIN_H)
|
||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||
if(HAVE_SSE AND ALSOFT_CPUEXT_SSE2)
|
||||
set(HAVE_SSE2 1)
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
|
||||
message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
|
||||
endif()
|
||||
|
|
@ -435,7 +450,14 @@ set(FPMATH_SET "0")
|
|||
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
||||
option(ALSOFT_ENABLE_SSE2_CODEGEN "Enable SSE2 code generation instead of x87 for 32-bit targets." TRUE)
|
||||
if(ALSOFT_ENABLE_SSE2_CODEGEN)
|
||||
if(SSE2_SWITCH OR NOT MSVC)
|
||||
if(MSVC)
|
||||
check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2)
|
||||
if(HAVE_ARCH_SSE2)
|
||||
set(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2")
|
||||
set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS})
|
||||
set(FPMATH_SET 2)
|
||||
endif()
|
||||
elseif(SSE2_SWITCH)
|
||||
check_c_compiler_flag("${SSE2_SWITCH} -mfpmath=sse" HAVE_MFPMATH_SSE_2)
|
||||
if(HAVE_MFPMATH_SSE_2)
|
||||
set(SSE_FLAGS ${SSE_FLAGS} ${SSE2_SWITCH} -mfpmath=sse)
|
||||
|
|
@ -447,13 +469,6 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
|||
# OSs don't guarantee this on 32-bit, so externally-callable
|
||||
# functions need to ensure an aligned stack.
|
||||
set(EXPORT_DECL "${EXPORT_DECL} __attribute__((force_align_arg_pointer))")
|
||||
elseif(MSVC)
|
||||
check_c_compiler_flag("/arch:SSE2" HAVE_ARCH_SSE2)
|
||||
if(HAVE_ARCH_SSE2)
|
||||
set(SSE_FLAGS ${SSE_FLAGS} "/arch:SSE2")
|
||||
set(C_FLAGS ${C_FLAGS} ${SSE_FLAGS})
|
||||
set(FPMATH_SET 2)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -551,51 +566,11 @@ if(NOT WIN32)
|
|||
check_symbol_exists(pthread_setname_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SETNAME_NP)
|
||||
if(NOT HAVE_PTHREAD_SETNAME_NP)
|
||||
check_symbol_exists(pthread_set_name_np "pthread.h;pthread_np.h" HAVE_PTHREAD_SET_NAME_NP)
|
||||
else()
|
||||
check_c_source_compiles("
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
int main()
|
||||
{
|
||||
pthread_setname_np(\"testname\");
|
||||
return 0;
|
||||
}"
|
||||
PTHREAD_SETNAME_NP_ONE_PARAM
|
||||
)
|
||||
check_c_source_compiles("
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
int main()
|
||||
{
|
||||
pthread_setname_np(pthread_self(), \"%s\", \"testname\");
|
||||
return 0;
|
||||
}"
|
||||
PTHREAD_SETNAME_NP_THREE_PARAMS
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
check_symbol_exists(pthread_setname_np pthread.h HAVE_PTHREAD_SETNAME_NP)
|
||||
if(NOT HAVE_PTHREAD_SETNAME_NP)
|
||||
check_symbol_exists(pthread_set_name_np pthread.h HAVE_PTHREAD_SET_NAME_NP)
|
||||
else()
|
||||
check_c_source_compiles("
|
||||
#include <pthread.h>
|
||||
int main()
|
||||
{
|
||||
pthread_setname_np(\"testname\");
|
||||
return 0;
|
||||
}"
|
||||
PTHREAD_SETNAME_NP_ONE_PARAM
|
||||
)
|
||||
check_c_source_compiles("
|
||||
#include <pthread.h>
|
||||
int main()
|
||||
{
|
||||
pthread_setname_np(pthread_self(), \"%s\", \"testname\");
|
||||
return 0;
|
||||
}"
|
||||
PTHREAD_SETNAME_NP_THREE_PARAMS
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -615,17 +590,19 @@ set(COMMON_OBJS
|
|||
common/alfstream.h
|
||||
common/almalloc.cpp
|
||||
common/almalloc.h
|
||||
common/alnumbers.h
|
||||
common/alnumeric.h
|
||||
common/aloptional.h
|
||||
common/alspan.h
|
||||
common/alstring.cpp
|
||||
common/alstring.h
|
||||
common/atomic.h
|
||||
common/comptr.h
|
||||
common/dynload.cpp
|
||||
common/dynload.h
|
||||
common/intrusive_ptr.h
|
||||
common/math_defs.h
|
||||
common/opthelpers.h
|
||||
common/phase_shifter.h
|
||||
common/polyphase_resampler.cpp
|
||||
common/polyphase_resampler.h
|
||||
common/pragmadefs.h
|
||||
|
|
@ -642,17 +619,32 @@ set(COMMON_OBJS
|
|||
set(CORE_OBJS
|
||||
core/ambdec.cpp
|
||||
core/ambdec.h
|
||||
core/ambidefs.cpp
|
||||
core/ambidefs.h
|
||||
core/async_event.h
|
||||
core/bformatdec.cpp
|
||||
core/bformatdec.h
|
||||
core/bs2b.cpp
|
||||
core/bs2b.h
|
||||
core/bsinc_defs.h
|
||||
core/bsinc_tables.cpp
|
||||
core/bsinc_tables.h
|
||||
core/bufferline.h
|
||||
core/buffer_storage.cpp
|
||||
core/buffer_storage.h
|
||||
core/context.cpp
|
||||
core/context.h
|
||||
core/converter.cpp
|
||||
core/converter.h
|
||||
core/cpu_caps.cpp
|
||||
core/cpu_caps.h
|
||||
core/devformat.cpp
|
||||
core/devformat.h
|
||||
core/device.cpp
|
||||
core/device.h
|
||||
core/effects/base.h
|
||||
core/effectslot.cpp
|
||||
core/effectslot.h
|
||||
core/except.cpp
|
||||
core/except.h
|
||||
core/filters/biquad.h
|
||||
|
|
@ -665,12 +657,57 @@ set(CORE_OBJS
|
|||
core/fmt_traits.h
|
||||
core/fpu_ctrl.cpp
|
||||
core/fpu_ctrl.h
|
||||
core/front_stablizer.h
|
||||
core/helpers.cpp
|
||||
core/helpers.h
|
||||
core/hrtf.cpp
|
||||
core/hrtf.h
|
||||
core/logging.cpp
|
||||
core/logging.h
|
||||
core/mastering.cpp
|
||||
core/mastering.h
|
||||
core/mixer.cpp
|
||||
core/mixer.h
|
||||
core/resampler_limits.h
|
||||
core/uhjfilter.cpp
|
||||
core/uhjfilter.h
|
||||
core/uiddefs.cpp
|
||||
core/voice.cpp
|
||||
core/voice.h
|
||||
core/voice_change.h)
|
||||
|
||||
set(HAVE_RTKIT 0)
|
||||
option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE)
|
||||
find_package(DBus1 QUIET)
|
||||
if(DBus1_FOUND)
|
||||
option(ALSOFT_RTKIT "Enable RTKit support" ON)
|
||||
if(ALSOFT_RTKIT)
|
||||
set(HAVE_RTKIT 1)
|
||||
set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h)
|
||||
if(WIN32 OR HAVE_DLFCN_H)
|
||||
set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS})
|
||||
set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS})
|
||||
else()
|
||||
set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(MISSING_VARS "")
|
||||
if(NOT DBus1_INCLUDE_DIRS)
|
||||
set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS")
|
||||
endif()
|
||||
if(NOT DBus1_LIBRARIES)
|
||||
set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES")
|
||||
endif()
|
||||
message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})")
|
||||
unset(MISSING_VARS)
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
|
||||
message(FATAL_ERROR "Failed to enabled required RTKit support")
|
||||
endif()
|
||||
|
||||
# Default mixers, always available
|
||||
set(CORE_OBJS ${CORE_OBJS}
|
||||
core/mixer/defs.h
|
||||
core/mixer/hrtfbase.h
|
||||
core/mixer/hrtfdefs.h
|
||||
|
|
@ -691,6 +728,7 @@ set(OPENAL_OBJS
|
|||
al/effects/dedicated.cpp
|
||||
al/effects/distortion.cpp
|
||||
al/effects/echo.cpp
|
||||
al/effects/effects.cpp
|
||||
al/effects/effects.h
|
||||
al/effects/equalizer.cpp
|
||||
al/effects/fshifter.cpp
|
||||
|
|
@ -714,22 +752,14 @@ set(OPENAL_OBJS
|
|||
# ALC and related routines
|
||||
set(ALC_OBJS
|
||||
alc/alc.cpp
|
||||
alc/alcmain.h
|
||||
alc/alu.cpp
|
||||
alc/alu.h
|
||||
alc/alconfig.cpp
|
||||
alc/alconfig.h
|
||||
alc/alcontext.h
|
||||
alc/async_event.h
|
||||
alc/bformatdec.cpp
|
||||
alc/bformatdec.h
|
||||
alc/buffer_storage.cpp
|
||||
alc/buffer_storage.h
|
||||
alc/compat.h
|
||||
alc/converter.cpp
|
||||
alc/converter.h
|
||||
alc/effectslot.cpp
|
||||
alc/effectslot.h
|
||||
alc/context.cpp
|
||||
alc/context.h
|
||||
alc/device.cpp
|
||||
alc/device.h
|
||||
alc/effects/base.h
|
||||
alc/effects/autowah.cpp
|
||||
alc/effects/chorus.cpp
|
||||
|
|
@ -745,22 +775,42 @@ set(ALC_OBJS
|
|||
alc/effects/pshifter.cpp
|
||||
alc/effects/reverb.cpp
|
||||
alc/effects/vmorpher.cpp
|
||||
alc/front_stablizer.h
|
||||
alc/helpers.cpp
|
||||
alc/hrtf.cpp
|
||||
alc/hrtf.h
|
||||
alc/inprogext.h
|
||||
alc/panning.cpp
|
||||
alc/uiddefs.cpp
|
||||
alc/voice.cpp
|
||||
alc/voice.h
|
||||
alc/voice_change.h)
|
||||
alc/panning.cpp)
|
||||
|
||||
if (ALSOFT_EAX)
|
||||
set(OPENAL_OBJS
|
||||
${OPENAL_OBJS}
|
||||
al/eax_api.cpp
|
||||
al/eax_api.h
|
||||
al/eax_eax_call.cpp
|
||||
al/eax_eax_call.h
|
||||
al/eax_effect.cpp
|
||||
al/eax_effect.h
|
||||
al/eax_exception.cpp
|
||||
al/eax_exception.h
|
||||
al/eax_fx_slot_index.cpp
|
||||
al/eax_fx_slot_index.h
|
||||
al/eax_fx_slots.cpp
|
||||
al/eax_fx_slots.h
|
||||
al/eax_globals.cpp
|
||||
al/eax_globals.h
|
||||
al/eax_utils.cpp
|
||||
al/eax_utils.h
|
||||
al/eax_x_ram.cpp
|
||||
al/eax_x_ram.h
|
||||
)
|
||||
endif ()
|
||||
|
||||
# Include SIMD mixers
|
||||
set(CPU_EXTS "Default")
|
||||
if(HAVE_SSE)
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp)
|
||||
set(CPU_EXTS "${CPU_EXTS}, SSE")
|
||||
endif()
|
||||
if(HAVE_SSE2)
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp)
|
||||
set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2")
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse2.cpp)
|
||||
set(CPU_EXTS "${CPU_EXTS}, SSE2")
|
||||
endif()
|
||||
if(HAVE_SSE3)
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp)
|
||||
|
|
@ -778,6 +828,7 @@ endif()
|
|||
|
||||
set(HAVE_ALSA 0)
|
||||
set(HAVE_OSS 0)
|
||||
set(HAVE_PIPEWIRE 0)
|
||||
set(HAVE_SOLARIS 0)
|
||||
set(HAVE_SNDIO 0)
|
||||
set(HAVE_DSOUND 0)
|
||||
|
|
@ -849,6 +900,25 @@ if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
|
|||
message(FATAL_ERROR "Failed to enabled required OSS backend")
|
||||
endif()
|
||||
|
||||
# Check PipeWire backend
|
||||
option(ALSOFT_REQUIRE_PIPEWIRE "Require PipeWire backend" OFF)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(PIPEWIRE libpipewire-0.3)
|
||||
if(PIPEWIRE_FOUND)
|
||||
option(ALSOFT_BACKEND_PIPEWIRE "Enable PipeWire backend" ON)
|
||||
if(ALSOFT_BACKEND_PIPEWIRE)
|
||||
set(HAVE_PIPEWIRE 1)
|
||||
set(BACKENDS "${BACKENDS} PipeWire${IS_LINKED},")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/pipewire.cpp alc/backends/pipewire.h)
|
||||
add_backend_libs(${PIPEWIRE_LIBRARIES})
|
||||
set(INC_PATHS ${INC_PATHS} ${PIPEWIRE_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
|
||||
message(FATAL_ERROR "Failed to enabled required PipeWire backend")
|
||||
endif()
|
||||
|
||||
# Check Solaris backend
|
||||
option(ALSOFT_REQUIRE_SOLARIS "Require Solaris backend" OFF)
|
||||
find_package(AudioIO)
|
||||
|
|
@ -1148,10 +1218,16 @@ if(ALSOFT_EMBED_HRTF_DATA)
|
|||
endif()
|
||||
|
||||
|
||||
if(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL)
|
||||
find_package(Qt5Widgets)
|
||||
if(ALSOFT_UTILS)
|
||||
find_package(MySOFA)
|
||||
if(NOT ALSOFT_NO_CONFIG_UTIL)
|
||||
find_package(Qt5Widgets QUIET)
|
||||
if(NOT Qt5Widgets_FOUND)
|
||||
message(STATUS "Could NOT find Qt5Widgets")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_EXAMPLES)
|
||||
if(ALSOFT_UTILS OR ALSOFT_EXAMPLES)
|
||||
find_package(SndFile)
|
||||
find_package(SDL2)
|
||||
if(SDL2_FOUND)
|
||||
|
|
@ -1212,6 +1288,7 @@ if(LIBTYPE STREQUAL "STATIC")
|
|||
add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS})
|
||||
target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC)
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
||||
|
||||
if(WIN32)
|
||||
# This option is for static linking OpenAL Soft into another project
|
||||
# that already defines the IDs. It is up to that project to ensure all
|
||||
|
|
@ -1232,13 +1309,14 @@ else()
|
|||
router/al.cpp
|
||||
)
|
||||
target_compile_definitions(OpenAL
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS})
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
|
||||
"AL_API=${EXPORT_DECL}" ${CPP_DEFS})
|
||||
target_compile_options(OpenAL PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
|
||||
target_include_directories(OpenAL
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
PRIVATE
|
||||
${OpenAL_SOURCE_DIR}/common
|
||||
${OpenAL_BINARY_DIR}
|
||||
|
|
@ -1303,12 +1381,14 @@ endif()
|
|||
target_include_directories(${IMPL_TARGET}
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include/AL>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/AL>
|
||||
PRIVATE
|
||||
${INC_PATHS}
|
||||
${OpenAL_BINARY_DIR}
|
||||
${OpenAL_SOURCE_DIR}
|
||||
${OpenAL_SOURCE_DIR}/alc
|
||||
${OpenAL_SOURCE_DIR}/common
|
||||
)
|
||||
|
||||
|
|
@ -1317,7 +1397,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME}
|
|||
SOVERSION ${LIB_MAJOR_VERSION}
|
||||
)
|
||||
target_compile_definitions(${IMPL_TARGET}
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS})
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
|
||||
${CPP_DEFS})
|
||||
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
||||
|
||||
if(TARGET build_version)
|
||||
|
|
@ -1351,25 +1432,35 @@ if(HAS_ROUTER)
|
|||
message(STATUS "Building DLL router")
|
||||
endif()
|
||||
|
||||
message(STATUS "
|
||||
Building OpenAL with support for the following backends:
|
||||
${BACKENDS}
|
||||
|
||||
Building with support for CPU extensions:
|
||||
${CPU_EXTS}
|
||||
")
|
||||
message(STATUS "")
|
||||
message(STATUS "Building OpenAL with support for the following backends:")
|
||||
message(STATUS " ${BACKENDS}")
|
||||
message(STATUS "")
|
||||
message(STATUS "Building with support for CPU extensions:")
|
||||
message(STATUS " ${CPU_EXTS}")
|
||||
message(STATUS "")
|
||||
if(FPMATH_SET)
|
||||
message(STATUS "Building with SSE${FPMATH_SET} codegen
|
||||
")
|
||||
message(STATUS "Building with SSE${FPMATH_SET} codegen")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(ALSOFT_EAX)
|
||||
message(STATUS "Building with legacy EAX extension support")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(ALSOFT_EMBED_HRTF_DATA)
|
||||
message(STATUS "Embedding HRTF datasets
|
||||
")
|
||||
message(STATUS "Embedding HRTF datasets")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
# An alias for sub-project builds
|
||||
add_library(OpenAL::OpenAL ALIAS OpenAL)
|
||||
|
||||
# Install main library
|
||||
if(ALSOFT_INSTALL)
|
||||
configure_package_config_file(OpenALConfig.cmake.in OpenALConfig.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL)
|
||||
install(TARGETS OpenAL EXPORT OpenAL
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
|
|
@ -1378,15 +1469,17 @@ if(ALSOFT_INSTALL)
|
|||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/AL)
|
||||
export(TARGETS OpenAL
|
||||
NAMESPACE OpenAL::
|
||||
FILE OpenALConfig.cmake)
|
||||
FILE OpenALTargets.cmake)
|
||||
install(EXPORT OpenAL
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL
|
||||
NAMESPACE OpenAL::
|
||||
FILE OpenALConfig.cmake)
|
||||
FILE OpenALTargets.cmake)
|
||||
install(DIRECTORY include/AL
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(FILES "${OpenAL_BINARY_DIR}/openal.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
install(FILES "${OpenAL_BINARY_DIR}/OpenALConfig.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/OpenAL")
|
||||
if(TARGET soft_oal)
|
||||
install(TARGETS soft_oal
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
|
@ -1429,7 +1522,24 @@ if(ALSOFT_UTILS)
|
|||
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
|
||||
endif()
|
||||
|
||||
find_package(MySOFA)
|
||||
if(SNDFILE_FOUND)
|
||||
add_executable(uhjdecoder utils/uhjdecoder.cpp)
|
||||
target_compile_definitions(uhjdecoder PRIVATE ${CPP_DEFS})
|
||||
target_include_directories(uhjdecoder
|
||||
PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(uhjdecoder PUBLIC common
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||
|
||||
add_executable(uhjencoder utils/uhjencoder.cpp)
|
||||
target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS})
|
||||
target_include_directories(uhjencoder
|
||||
PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(uhjencoder PUBLIC common
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||
endif()
|
||||
|
||||
if(MYSOFA_FOUND)
|
||||
set(SOFA_SUPPORT_SRCS
|
||||
utils/sofa-support.cpp
|
||||
|
|
|
|||
|
|
@ -1,3 +1,57 @@
|
|||
openal-soft-1.22.0:
|
||||
|
||||
Implemented the ALC_SOFT_reopen_device extension. This allows for moving
|
||||
devices to different outputs without losing object state.
|
||||
|
||||
Implemented the ALC_SOFT_output_mode extension.
|
||||
|
||||
Implemented the AL_SOFT_callback_buffer extension.
|
||||
|
||||
Implemented the AL_SOFT_UHJ extension. This supports native UHJ buffer
|
||||
formats and Super Stereo processing.
|
||||
|
||||
Implemented the legacy EAX extensions. Enabled by default only on Windows.
|
||||
|
||||
Improved sound positioning stability when a source is near the listener.
|
||||
|
||||
Improved the default 5.1 output decoder.
|
||||
|
||||
Improved the high frequency response for the HRTF second-order ambisonic
|
||||
decoder.
|
||||
|
||||
Improved SoundIO capture behavior.
|
||||
|
||||
Fixed UHJ output on NEON-capable CPUs.
|
||||
|
||||
Fixed redundant effect updates when setting an effect property to the
|
||||
current value.
|
||||
|
||||
Fixed WASAPI capture using really low sample rates, and sources with very
|
||||
high pitch shifts when using a bsinc resampler.
|
||||
|
||||
Added a PipeWire backend.
|
||||
|
||||
Added enumeration for the JACK and CoreAudio backends.
|
||||
|
||||
Added optional support for RTKit to get real-time priority. Only used as a
|
||||
backup when pthread_setschedparam fails.
|
||||
|
||||
Added an option for JACK playback to render directly in the real-time
|
||||
processing callback. For lower playback latency, on by default.
|
||||
|
||||
Added an option for custom JACK devices.
|
||||
|
||||
Added utilities to encode and decode UHJ audio files. Files are decoded to
|
||||
the .amb format, and are encoded from libsndfile-compatible formats.
|
||||
|
||||
Added an in-progress extension to hold sources in a playing state when a
|
||||
device disconnects. Allows devices to be reset or reopened and have sources
|
||||
resume from where they left off.
|
||||
|
||||
Lowered the priority of the JACK backend. To avoid it getting picked when
|
||||
PipeWire is providing JACK compatibility, since the JACK backend is less
|
||||
robust with auto-configuration.
|
||||
|
||||
openal-soft-1.21.1:
|
||||
|
||||
Improved alext.h's detection of standard types.
|
||||
|
|
@ -5,6 +59,8 @@ openal-soft-1.21.1:
|
|||
Improved slightly the local source position when the listener and source
|
||||
are near each other.
|
||||
|
||||
Improved click/pop prevention for sounds that stop prematurely.
|
||||
|
||||
Fixed compilation for Windows ARM targets with MSVC.
|
||||
|
||||
Fixed ARM NEON detection on Windows.
|
||||
|
|
@ -19,6 +75,8 @@ openal-soft-1.21.1:
|
|||
|
||||
Fixed missing source stop events when stopping a paused source.
|
||||
|
||||
Added capture support to the experimental Oboe backend.
|
||||
|
||||
openal-soft-1.21.0:
|
||||
|
||||
Updated library codebase to C++14.
|
||||
|
|
|
|||
9
Engine/lib/openal-soft/OpenALConfig.cmake.in
Normal file
9
Engine/lib/openal-soft/OpenALConfig.cmake.in
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
|
||||
|
||||
set(OPENAL_FOUND ON)
|
||||
set(OPENAL_INCLUDE_DIR $<TARGET_PROPERTY:OpenAL::OpenAL,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
set(OPENAL_LIBRARY $<LINK_ONLY:OpenAL::OpenAL>)
|
||||
set(OPENAL_DEFINITIONS $<TARGET_PROPERTY:OpenAL::OpenAL,INTERFACE_COMPILE_DEFINITIONS>)
|
||||
set(OPENAL_VERSION_STRING @PACKAGE_VERSION@)
|
||||
|
|
@ -7,5 +7,10 @@
|
|||
# cmake .. -DANDROID_STL=c++_shared \
|
||||
# -DCMAKE_TOOLCHAIN_FILE=${ndk_root}/build/cmake/android.toolchain.cmake
|
||||
#
|
||||
# Certain NDK versions may also need to use the lld linker to avoid errors
|
||||
# about missing liblog.so and libOpenSLES.so. That can be done by:
|
||||
# cmake .. -DANDROID_LD=lld \
|
||||
# -DCMAKE_TOOLCHAIN_FILE=${ndk_root}/build/cmake/android.toolchain.cmake
|
||||
#
|
||||
|
||||
MESSAGE(FATAL_ERROR "Use the toolchain provided by the Android NDK")
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,14 +8,22 @@
|
|||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "effectslot.h"
|
||||
#include "effects/base.h"
|
||||
#include "core/effectslot.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <memory>
|
||||
|
||||
#include "eax_eax_call.h"
|
||||
#include "eax_effect.h"
|
||||
#include "eax_fx_slot_index.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffect;
|
||||
struct WetBuffer;
|
||||
|
|
@ -40,7 +48,7 @@ struct ALeffectslot {
|
|||
al::intrusive_ptr<EffectState> State;
|
||||
} Effect;
|
||||
|
||||
std::atomic_flag PropsClean;
|
||||
bool mPropsDirty{true};
|
||||
|
||||
SlotState mState{SlotState::Initial};
|
||||
|
||||
|
|
@ -56,13 +64,214 @@ struct ALeffectslot {
|
|||
ALeffectslot& operator=(const ALeffectslot&) = delete;
|
||||
~ALeffectslot();
|
||||
|
||||
ALenum initEffect(ALeffect *effect, ALCcontext *context);
|
||||
ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
|
||||
void updateProps(ALCcontext *context);
|
||||
|
||||
/* This can be new'd for the context's default effect slot. */
|
||||
DEF_NEWDEL(ALeffectslot)
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
void eax_initialize(
|
||||
ALCcontext& al_context,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept;
|
||||
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_dispatch(const EaxEaxCall& eax_call)
|
||||
{ return eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call); }
|
||||
|
||||
|
||||
void eax_unlock_legacy() noexcept;
|
||||
|
||||
void eax_commit() { eax_apply_deferred(); }
|
||||
|
||||
private:
|
||||
ALCcontext* eax_al_context_{};
|
||||
|
||||
EaxFxSlotIndexValue eax_fx_slot_index_{};
|
||||
|
||||
EAX50FXSLOTPROPERTIES eax_eax_fx_slot_{};
|
||||
|
||||
EaxEffectUPtr eax_effect_{};
|
||||
bool eax_is_locked_{};
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
static void eax_fail(
|
||||
const char* message);
|
||||
|
||||
|
||||
GUID eax_get_eax_default_effect_guid() const noexcept;
|
||||
long eax_get_eax_default_lock() const noexcept;
|
||||
|
||||
void eax_set_eax_fx_slot_defaults();
|
||||
|
||||
void eax_initialize_eax();
|
||||
|
||||
void eax_initialize_lock();
|
||||
|
||||
|
||||
void eax_initialize_effects();
|
||||
|
||||
|
||||
void eax_get_fx_slot_all(
|
||||
const EaxEaxCall& eax_call) const;
|
||||
|
||||
void eax_get_fx_slot(
|
||||
const EaxEaxCall& eax_call) const;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_get(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
void eax_set_fx_slot_effect(
|
||||
ALenum effect_type);
|
||||
|
||||
void eax_set_fx_slot_effect();
|
||||
|
||||
|
||||
void eax_set_efx_effect_slot_gain();
|
||||
|
||||
void eax_set_fx_slot_volume();
|
||||
|
||||
|
||||
void eax_set_effect_slot_send_auto();
|
||||
|
||||
void eax_set_fx_slot_flags();
|
||||
|
||||
|
||||
void eax_ensure_is_unlocked() const;
|
||||
|
||||
|
||||
void eax_validate_fx_slot_effect(
|
||||
const GUID& eax_effect_id);
|
||||
|
||||
void eax_validate_fx_slot_volume(
|
||||
long eax_volume);
|
||||
|
||||
void eax_validate_fx_slot_lock(
|
||||
long eax_lock);
|
||||
|
||||
void eax_validate_fx_slot_flags(
|
||||
unsigned long eax_flags,
|
||||
int eax_version);
|
||||
|
||||
void eax_validate_fx_slot_occlusion(
|
||||
long eax_occlusion);
|
||||
|
||||
void eax_validate_fx_slot_occlusion_lf_ratio(
|
||||
float eax_occlusion_lf_ratio);
|
||||
|
||||
void eax_validate_fx_slot_all(
|
||||
const EAX40FXSLOTPROPERTIES& fx_slot,
|
||||
int eax_version);
|
||||
|
||||
void eax_validate_fx_slot_all(
|
||||
const EAX50FXSLOTPROPERTIES& fx_slot,
|
||||
int eax_version);
|
||||
|
||||
|
||||
void eax_set_fx_slot_effect(
|
||||
const GUID& eax_effect_id);
|
||||
|
||||
void eax_set_fx_slot_volume(
|
||||
long eax_volume);
|
||||
|
||||
void eax_set_fx_slot_lock(
|
||||
long eax_lock);
|
||||
|
||||
void eax_set_fx_slot_flags(
|
||||
unsigned long eax_flags);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion(
|
||||
long eax_occlusion);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion_lf_ratio(
|
||||
float eax_occlusion_lf_ratio);
|
||||
|
||||
void eax_set_fx_slot_all(
|
||||
const EAX40FXSLOTPROPERTIES& eax_fx_slot);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_all(
|
||||
const EAX50FXSLOTPROPERTIES& eax_fx_slot);
|
||||
|
||||
|
||||
void eax_set_fx_slot_effect(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_set_fx_slot_volume(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_set_fx_slot_lock(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_set_fx_slot_flags(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_occlusion_lf_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set_fx_slot_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
bool eax_set_fx_slot(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_apply_deferred();
|
||||
|
||||
// [[nodiscard]]
|
||||
bool eax_set(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
void eax_dispatch_effect(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
// `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_EFFECT, effect)`
|
||||
void eax_set_effect_slot_effect(EaxEffect &effect);
|
||||
|
||||
// `alAuxiliaryEffectSloti(effect_slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, value)`
|
||||
void eax_set_effect_slot_send_auto(bool is_send_auto);
|
||||
|
||||
// `alAuxiliaryEffectSlotf(effect_slot, AL_EFFECTSLOT_GAIN, gain)`
|
||||
void eax_set_effect_slot_gain(ALfloat gain);
|
||||
|
||||
public:
|
||||
class EaxDeleter {
|
||||
public:
|
||||
void operator()(ALeffectslot *effect_slot);
|
||||
}; // EaxAlEffectSlotDeleter
|
||||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
void UpdateAllEffectSlotProps(ALCcontext *context);
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
using EaxAlEffectSlotUPtr = std::unique_ptr<ALeffectslot, ALeffectslot::EaxDeleter>;
|
||||
|
||||
|
||||
EaxAlEffectSlotUPtr eax_create_al_effect_slot(
|
||||
ALCcontext& context);
|
||||
|
||||
void eax_delete_al_effect_slot(
|
||||
ALCcontext& context,
|
||||
ALeffectslot& effect_slot);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
|
@ -43,16 +44,23 @@
|
|||
|
||||
#include "albit.h"
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax_globals.h"
|
||||
#include "eax_x_ram.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -110,10 +118,10 @@ void DecodeIMA4Block(int16_t *dst, const al::byte *src, size_t numchans, size_t
|
|||
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
sample[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
|
||||
sample[c] = src[0] | (src[1]<<8);
|
||||
sample[c] = (sample[c]^0x8000) - 32768;
|
||||
src += 2;
|
||||
index[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
|
||||
index[c] = src[0] | (src[1]<<8);
|
||||
index[c] = clampi((index[c]^0x8000) - 32768, 0, 88);
|
||||
src += 2;
|
||||
|
||||
|
|
@ -126,8 +134,8 @@ void DecodeIMA4Block(int16_t *dst, const al::byte *src, size_t numchans, size_t
|
|||
{
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
code[c] = al::to_integer<ALuint>(src[0]) | (al::to_integer<ALuint>(src[1])<< 8) |
|
||||
(al::to_integer<ALuint>(src[2])<<16) | (al::to_integer<ALuint>(src[3])<<24);
|
||||
code[c] = ALuint{src[0]} | (ALuint{src[1]}<< 8) | (ALuint{src[2]}<<16)
|
||||
| (ALuint{src[3]}<<24);
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
|
@ -156,25 +164,23 @@ void DecodeMSADPCMBlock(int16_t *dst, const al::byte *src, size_t numchans, size
|
|||
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
blockpred[c] = std::min<ALubyte>(al::to_integer<ALubyte>(src[0]), 6);
|
||||
blockpred[c] = std::min<ALubyte>(src[0], 6);
|
||||
++src;
|
||||
}
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
delta[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
|
||||
delta[c] = src[0] | (src[1]<<8);
|
||||
delta[c] = (delta[c]^0x8000) - 32768;
|
||||
src += 2;
|
||||
}
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
samples[c][0] = static_cast<ALshort>(al::to_integer<int>(src[0]) |
|
||||
(al::to_integer<int>(src[1])<<8));
|
||||
samples[c][0] = static_cast<ALshort>(src[0] | (src[1]<<8));
|
||||
src += 2;
|
||||
}
|
||||
for(size_t c{0};c < numchans;c++)
|
||||
{
|
||||
samples[c][1] = static_cast<ALshort>(al::to_integer<int>(src[0]) |
|
||||
(al::to_integer<int>(src[1])<<8));
|
||||
samples[c][1] = static_cast<ALshort>(src[0] | (src[1]<<8));
|
||||
src += 2;
|
||||
}
|
||||
|
||||
|
|
@ -198,13 +204,13 @@ void DecodeMSADPCMBlock(int16_t *dst, const al::byte *src, size_t numchans, size
|
|||
|
||||
int pred{(samples[c][0]*MSADPCMAdaptionCoeff[blockpred[c]][0] +
|
||||
samples[c][1]*MSADPCMAdaptionCoeff[blockpred[c]][1]) / 256};
|
||||
pred += (al::to_integer<int>(nibble^0x08) - 0x08) * delta[c];
|
||||
pred += ((nibble^0x08) - 0x08) * delta[c];
|
||||
pred = clampi(pred, -32768, 32767);
|
||||
|
||||
samples[c][1] = samples[c][0];
|
||||
samples[c][0] = static_cast<int16_t>(pred);
|
||||
|
||||
delta[c] = (MSADPCMAdaption[al::to_integer<ALubyte>(nibble)] * delta[c]) / 256;
|
||||
delta[c] = (MSADPCMAdaption[nibble] * delta[c]) / 256;
|
||||
delta[c] = maxi(16, delta[c]);
|
||||
|
||||
*(dst++) = static_cast<int16_t>(pred);
|
||||
|
|
@ -271,6 +277,9 @@ ALuint ChannelsFromUserFmt(UserFmtChannels chans, ALuint ambiorder) noexcept
|
|||
case UserFmtX71: return 8;
|
||||
case UserFmtBFormat2D: return (ambiorder*2) + 1;
|
||||
case UserFmtBFormat3D: return (ambiorder+1) * (ambiorder+1);
|
||||
case UserFmtUHJ2: return 2;
|
||||
case UserFmtUHJ3: return 3;
|
||||
case UserFmtUHJ4: return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -310,11 +319,82 @@ ALenum EnumFromAmbiScaling(AmbiScaling scale)
|
|||
{
|
||||
case AmbiScaling::FuMa: return AL_FUMA_SOFT;
|
||||
case AmbiScaling::SN3D: return AL_SN3D_SOFT;
|
||||
case AmbiScaling::N3D: return AL_SN3D_SOFT;
|
||||
case AmbiScaling::N3D: return AL_N3D_SOFT;
|
||||
case AmbiScaling::UHJ: break;
|
||||
}
|
||||
throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))};
|
||||
}
|
||||
|
||||
al::optional<FmtChannels> FmtFromUserFmt(UserFmtChannels chans)
|
||||
{
|
||||
switch(chans)
|
||||
{
|
||||
case UserFmtMono: return al::make_optional(FmtMono);
|
||||
case UserFmtStereo: return al::make_optional(FmtStereo);
|
||||
case UserFmtRear: return al::make_optional(FmtRear);
|
||||
case UserFmtQuad: return al::make_optional(FmtQuad);
|
||||
case UserFmtX51: return al::make_optional(FmtX51);
|
||||
case UserFmtX61: return al::make_optional(FmtX61);
|
||||
case UserFmtX71: return al::make_optional(FmtX71);
|
||||
case UserFmtBFormat2D: return al::make_optional(FmtBFormat2D);
|
||||
case UserFmtBFormat3D: return al::make_optional(FmtBFormat3D);
|
||||
case UserFmtUHJ2: return al::make_optional(FmtUHJ2);
|
||||
case UserFmtUHJ3: return al::make_optional(FmtUHJ3);
|
||||
case UserFmtUHJ4: return al::make_optional(FmtUHJ4);
|
||||
}
|
||||
return al::nullopt;
|
||||
}
|
||||
al::optional<FmtType> FmtFromUserFmt(UserFmtType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case UserFmtUByte: return al::make_optional(FmtUByte);
|
||||
case UserFmtShort: return al::make_optional(FmtShort);
|
||||
case UserFmtFloat: return al::make_optional(FmtFloat);
|
||||
case UserFmtDouble: return al::make_optional(FmtDouble);
|
||||
case UserFmtMulaw: return al::make_optional(FmtMulaw);
|
||||
case UserFmtAlaw: return al::make_optional(FmtAlaw);
|
||||
/* ADPCM not handled here. */
|
||||
case UserFmtIMA4: break;
|
||||
case UserFmtMSADPCM: break;
|
||||
}
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer,
|
||||
const ALuint newsize) noexcept
|
||||
{
|
||||
ALuint freemem{device.eax_x_ram_free_size};
|
||||
/* If the buffer is currently in "hardware", add its memory to the free
|
||||
* pool since it'll be "replaced".
|
||||
*/
|
||||
if(buffer.eax_x_ram_is_hardware)
|
||||
freemem += buffer.OriginalSize;
|
||||
return freemem >= newsize;
|
||||
}
|
||||
|
||||
void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept
|
||||
{
|
||||
if(buffer.eax_x_ram_is_hardware)
|
||||
return;
|
||||
|
||||
if(device.eax_x_ram_free_size >= buffer.OriginalSize)
|
||||
{
|
||||
device.eax_x_ram_free_size -= buffer.OriginalSize;
|
||||
buffer.eax_x_ram_is_hardware = true;
|
||||
}
|
||||
}
|
||||
|
||||
void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer)
|
||||
{
|
||||
if(al_buffer.eax_x_ram_is_hardware)
|
||||
al_device.eax_x_ram_free_size += al_buffer.OriginalSize;
|
||||
al_buffer.eax_x_ram_is_hardware = false;
|
||||
}
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
|
||||
AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
|
||||
|
|
@ -352,13 +432,12 @@ ALbuffer *AllocBuffer(ALCdevice *device)
|
|||
{
|
||||
auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
|
||||
[](const BufferSubList &entry) noexcept -> bool
|
||||
{ return entry.FreeMask != 0; }
|
||||
);
|
||||
|
||||
{ return entry.FreeMask != 0; });
|
||||
auto lidx = static_cast<ALuint>(std::distance(device->BufferList.begin(), sublist));
|
||||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALbuffer *buffer{::new (sublist->Buffers + slidx) ALbuffer{}};
|
||||
ALbuffer *buffer{al::construct_at(sublist->Buffers + slidx)};
|
||||
|
||||
/* Add 1 to avoid buffer ID 0. */
|
||||
buffer->id = ((lidx<<6) | slidx) + 1;
|
||||
|
|
@ -370,6 +449,10 @@ ALbuffer *AllocBuffer(ALCdevice *device)
|
|||
|
||||
void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
|
||||
{
|
||||
#ifdef ALSOFT_EAX
|
||||
eax_x_ram_clear(*device, *buffer);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
const ALuint id{buffer->id - 1};
|
||||
const size_t lidx{id >> 6};
|
||||
const ALuint slidx{id & 0x3f};
|
||||
|
|
@ -453,46 +536,26 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
ALBuf->id);
|
||||
|
||||
/* Currently no channel configurations need to be converted. */
|
||||
FmtChannels DstChannels{FmtMono};
|
||||
switch(SrcChannels)
|
||||
{
|
||||
case UserFmtMono: DstChannels = FmtMono; break;
|
||||
case UserFmtStereo: DstChannels = FmtStereo; break;
|
||||
case UserFmtRear: DstChannels = FmtRear; break;
|
||||
case UserFmtQuad: DstChannels = FmtQuad; break;
|
||||
case UserFmtX51: DstChannels = FmtX51; break;
|
||||
case UserFmtX61: DstChannels = FmtX61; break;
|
||||
case UserFmtX71: DstChannels = FmtX71; break;
|
||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
||||
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
||||
}
|
||||
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
||||
auto DstChannels = FmtFromUserFmt(SrcChannels);
|
||||
if UNLIKELY(!DstChannels)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
|
||||
|
||||
/* IMA4 and MSADPCM convert to 16-bit short. */
|
||||
FmtType DstType{FmtUByte};
|
||||
switch(SrcType)
|
||||
{
|
||||
case UserFmtUByte: DstType = FmtUByte; break;
|
||||
case UserFmtShort: DstType = FmtShort; break;
|
||||
case UserFmtFloat: DstType = FmtFloat; break;
|
||||
case UserFmtDouble: DstType = FmtDouble; break;
|
||||
case UserFmtAlaw: DstType = FmtAlaw; break;
|
||||
case UserFmtMulaw: DstType = FmtMulaw; break;
|
||||
case UserFmtIMA4: DstType = FmtShort; break;
|
||||
case UserFmtMSADPCM: DstType = FmtShort; break;
|
||||
}
|
||||
|
||||
/* TODO: Currently we can only map samples when they're not converted. To
|
||||
/* IMA4 and MSADPCM convert to 16-bit short.
|
||||
*
|
||||
* TODO: Currently we can only map samples when they're not converted. To
|
||||
* allow it would need some kind of double-buffering to hold onto a copy of
|
||||
* the original data.
|
||||
*/
|
||||
if((access&MAP_READ_WRITE_FLAGS))
|
||||
{
|
||||
if UNLIKELY(static_cast<long>(SrcType) != static_cast<long>(DstType))
|
||||
if UNLIKELY(SrcType == UserFmtIMA4 || SrcType == UserFmtMSADPCM)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "%s samples cannot be mapped",
|
||||
NameFromUserFmtType(SrcType));
|
||||
}
|
||||
auto DstType = (SrcType == UserFmtIMA4 || SrcType == UserFmtMSADPCM)
|
||||
? al::make_optional(FmtShort) : FmtFromUserFmt(SrcType);
|
||||
if UNLIKELY(!DstType)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
|
||||
|
||||
const ALuint unpackalign{ALBuf->UnpackAlign};
|
||||
const ALuint align{SanitizeAlignment(SrcType, unpackalign)};
|
||||
|
|
@ -500,13 +563,13 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid unpack alignment %u for %s samples",
|
||||
unpackalign, NameFromUserFmtType(SrcType));
|
||||
|
||||
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
||||
ALBuf->UnpackAmbiOrder : 0};
|
||||
const ALuint ambiorder{IsBFormat(*DstChannels) ? ALBuf->UnpackAmbiOrder :
|
||||
(IsUHJ(*DstChannels) ? 1 : 0)};
|
||||
|
||||
if((access&AL_PRESERVE_DATA_BIT_SOFT))
|
||||
{
|
||||
/* Can only preserve data with the same format and alignment. */
|
||||
if UNLIKELY(ALBuf->mChannels != DstChannels || ALBuf->OriginalType != SrcType)
|
||||
if UNLIKELY(ALBuf->mChannels != *DstChannels || ALBuf->OriginalType != SrcType)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format");
|
||||
if UNLIKELY(ALBuf->OriginalAlign != align)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment");
|
||||
|
|
@ -534,13 +597,23 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
/* Convert the sample frames to the number of bytes needed for internal
|
||||
* storage.
|
||||
*/
|
||||
ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
|
||||
ALuint FrameSize{NumChannels * BytesFromFmt(DstType)};
|
||||
ALuint NumChannels{ChannelsFromFmt(*DstChannels, ambiorder)};
|
||||
ALuint FrameSize{NumChannels * BytesFromFmt(*DstType)};
|
||||
if UNLIKELY(frames > std::numeric_limits<size_t>::max()/FrameSize)
|
||||
SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
|
||||
"Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize);
|
||||
size_t newsize{static_cast<size_t>(frames) * FrameSize};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
if(ALBuf->eax_x_ram_mode == AL_STORAGE_HARDWARE)
|
||||
{
|
||||
ALCdevice &device = *context->mALDevice;
|
||||
if(!eax_x_ram_check_availability(device, *ALBuf, size))
|
||||
SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
|
||||
"Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Round up to the next 16-byte multiple. This could reallocate only when
|
||||
* increasing or the new size is less than half the current, but then the
|
||||
* buffer's AL_SIZE would not be very reliable for accounting buffer memory
|
||||
|
|
@ -561,7 +634,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
|
||||
if(SrcType == UserFmtIMA4)
|
||||
{
|
||||
assert(DstType == FmtShort);
|
||||
assert(*DstType == FmtShort);
|
||||
if(SrcData != nullptr && !ALBuf->mData.empty())
|
||||
Convert_int16_ima4(reinterpret_cast<int16_t*>(ALBuf->mData.data()), SrcData,
|
||||
NumChannels, frames, align);
|
||||
|
|
@ -569,7 +642,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
}
|
||||
else if(SrcType == UserFmtMSADPCM)
|
||||
{
|
||||
assert(DstType == FmtShort);
|
||||
assert(*DstType == FmtShort);
|
||||
if(SrcData != nullptr && !ALBuf->mData.empty())
|
||||
Convert_int16_msadpcm(reinterpret_cast<int16_t*>(ALBuf->mData.data()), SrcData,
|
||||
NumChannels, frames, align);
|
||||
|
|
@ -577,7 +650,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
}
|
||||
else
|
||||
{
|
||||
assert(static_cast<long>(SrcType) == static_cast<long>(DstType));
|
||||
assert(DstType.has_value());
|
||||
if(SrcData != nullptr && !ALBuf->mData.empty())
|
||||
std::copy_n(SrcData, frames*FrameSize, ALBuf->mData.begin());
|
||||
ALBuf->OriginalAlign = 1;
|
||||
|
|
@ -588,8 +661,8 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
ALBuf->Access = access;
|
||||
|
||||
ALBuf->mSampleRate = static_cast<ALuint>(freq);
|
||||
ALBuf->mChannels = DstChannels;
|
||||
ALBuf->mType = DstType;
|
||||
ALBuf->mChannels = *DstChannels;
|
||||
ALBuf->mType = *DstType;
|
||||
ALBuf->mAmbiOrder = ambiorder;
|
||||
|
||||
ALBuf->mCallback = nullptr;
|
||||
|
|
@ -598,11 +671,16 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
ALBuf->mSampleLen = frames;
|
||||
ALBuf->mLoopStart = 0;
|
||||
ALBuf->mLoopEnd = ALBuf->mSampleLen;
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
if(eax_g_is_enabled && ALBuf->eax_x_ram_mode != AL_STORAGE_ACCESSIBLE)
|
||||
eax_x_ram_apply(*context->mALDevice, *ALBuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Prepares the buffer to use the specified callback, using the specified format. */
|
||||
void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
||||
UserFmtChannels SrcChannels, UserFmtType SrcType, LPALBUFFERCALLBACKTYPESOFT callback,
|
||||
UserFmtChannels SrcChannels, UserFmtType SrcType, ALBUFFERCALLBACKTYPESOFT callback,
|
||||
void *userptr)
|
||||
{
|
||||
if UNLIKELY(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0)
|
||||
|
|
@ -610,43 +688,25 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
|||
ALBuf->id);
|
||||
|
||||
/* Currently no channel configurations need to be converted. */
|
||||
FmtChannels DstChannels{FmtMono};
|
||||
switch(SrcChannels)
|
||||
{
|
||||
case UserFmtMono: DstChannels = FmtMono; break;
|
||||
case UserFmtStereo: DstChannels = FmtStereo; break;
|
||||
case UserFmtRear: DstChannels = FmtRear; break;
|
||||
case UserFmtQuad: DstChannels = FmtQuad; break;
|
||||
case UserFmtX51: DstChannels = FmtX51; break;
|
||||
case UserFmtX61: DstChannels = FmtX61; break;
|
||||
case UserFmtX71: DstChannels = FmtX71; break;
|
||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
||||
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
||||
}
|
||||
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
||||
auto DstChannels = FmtFromUserFmt(SrcChannels);
|
||||
if UNLIKELY(!DstChannels)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
|
||||
|
||||
/* IMA4 and MSADPCM convert to 16-bit short. Not supported with callbacks. */
|
||||
FmtType DstType{FmtUByte};
|
||||
switch(SrcType)
|
||||
{
|
||||
case UserFmtUByte: DstType = FmtUByte; break;
|
||||
case UserFmtShort: DstType = FmtShort; break;
|
||||
case UserFmtFloat: DstType = FmtFloat; break;
|
||||
case UserFmtDouble: DstType = FmtDouble; break;
|
||||
case UserFmtAlaw: DstType = FmtAlaw; break;
|
||||
case UserFmtMulaw: DstType = FmtMulaw; break;
|
||||
case UserFmtIMA4: DstType = FmtShort; break;
|
||||
case UserFmtMSADPCM: DstType = FmtShort; break;
|
||||
}
|
||||
if UNLIKELY(static_cast<long>(SrcType) != static_cast<long>(DstType))
|
||||
auto DstType = FmtFromUserFmt(SrcType);
|
||||
if UNLIKELY(!DstType)
|
||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format");
|
||||
|
||||
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
||||
ALBuf->UnpackAmbiOrder : 0};
|
||||
const ALuint ambiorder{IsBFormat(*DstChannels) ? ALBuf->UnpackAmbiOrder :
|
||||
(IsUHJ(*DstChannels) ? 1 : 0)};
|
||||
|
||||
al::vector<al::byte,16>(FrameSizeFromFmt(DstChannels, DstType, ambiorder) *
|
||||
size_t{BufferLineSize + (MaxResamplerPadding>>1)}).swap(ALBuf->mData);
|
||||
static constexpr uint line_size{BufferLineSize + MaxPostVoiceLoad};
|
||||
al::vector<al::byte,16>(FrameSizeFromFmt(*DstChannels, *DstType, ambiorder) *
|
||||
size_t{line_size}).swap(ALBuf->mData);
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
eax_x_ram_clear(*context->mALDevice, *ALBuf);
|
||||
#endif
|
||||
|
||||
ALBuf->mCallback = callback;
|
||||
ALBuf->mUserData = userptr;
|
||||
|
|
@ -657,8 +717,8 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
|||
ALBuf->Access = 0;
|
||||
|
||||
ALBuf->mSampleRate = static_cast<ALuint>(freq);
|
||||
ALBuf->mChannels = DstChannels;
|
||||
ALBuf->mType = DstType;
|
||||
ALBuf->mChannels = *DstChannels;
|
||||
ALBuf->mType = *DstType;
|
||||
ALBuf->mAmbiOrder = ambiorder;
|
||||
|
||||
ALBuf->mSampleLen = 0;
|
||||
|
|
@ -675,7 +735,7 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
|||
UserFmtChannels channels;
|
||||
UserFmtType type;
|
||||
};
|
||||
static const std::array<FormatMap,46> UserFmtList{{
|
||||
static const std::array<FormatMap,55> UserFmtList{{
|
||||
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
|
||||
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
|
||||
{ AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
|
||||
|
|
@ -731,6 +791,18 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
|||
{ AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
|
||||
{ AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
|
||||
{ AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
|
||||
|
||||
{ AL_FORMAT_UHJ2CHN8_SOFT, UserFmtUHJ2, UserFmtUByte },
|
||||
{ AL_FORMAT_UHJ2CHN16_SOFT, UserFmtUHJ2, UserFmtShort },
|
||||
{ AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, UserFmtUHJ2, UserFmtFloat },
|
||||
|
||||
{ AL_FORMAT_UHJ3CHN8_SOFT, UserFmtUHJ3, UserFmtUByte },
|
||||
{ AL_FORMAT_UHJ3CHN16_SOFT, UserFmtUHJ3, UserFmtShort },
|
||||
{ AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, UserFmtUHJ3, UserFmtFloat },
|
||||
|
||||
{ AL_FORMAT_UHJ4CHN8_SOFT, UserFmtUHJ4, UserFmtUByte },
|
||||
{ AL_FORMAT_UHJ4CHN16_SOFT, UserFmtUHJ4, UserFmtShort },
|
||||
{ AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, UserFmtUHJ4, UserFmtFloat },
|
||||
}};
|
||||
|
||||
for(const auto &fmt : UserFmtList)
|
||||
|
|
@ -754,7 +826,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if(!EnsureBuffers(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
|
|
@ -794,7 +866,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
/* First try to find any buffers that are invalid or in-use. */
|
||||
|
|
@ -834,7 +906,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if(!buffer || LookupBuffer(device, buffer))
|
||||
return AL_TRUE;
|
||||
|
|
@ -855,7 +927,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -877,8 +949,10 @@ START_API_FUNC
|
|||
if UNLIKELY(!usrfmt)
|
||||
context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
|
||||
else
|
||||
{
|
||||
LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
|
||||
usrfmt->type, static_cast<const al::byte*>(data), flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
@ -889,7 +963,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return nullptr;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -942,7 +1016,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -965,7 +1039,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -997,7 +1071,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1127,7 +1201,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1147,7 +1221,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1166,7 +1240,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1188,7 +1262,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1250,7 +1324,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1283,7 +1357,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1321,7 +1395,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1343,7 +1417,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1371,7 +1445,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
|
@ -1393,7 +1467,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
|
@ -1450,7 +1524,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
@ -1488,7 +1562,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
|
@ -1510,13 +1584,13 @@ END_API_FUNC
|
|||
|
||||
|
||||
AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq,
|
||||
LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags)
|
||||
ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
|
@ -1526,8 +1600,6 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
|
||||
else if UNLIKELY(callback == nullptr)
|
||||
context->setError(AL_INVALID_VALUE, "NULL callback");
|
||||
else if UNLIKELY(flags != 0)
|
||||
context->setError(AL_INVALID_VALUE, "Invalid callback flags 0x%x", flags);
|
||||
else
|
||||
{
|
||||
auto usrfmt = DecomposeUserFormat(format);
|
||||
|
|
@ -1546,7 +1618,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
|
@ -1574,7 +1646,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
@ -1602,7 +1674,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
@ -1630,3 +1702,161 @@ BufferSubList::~BufferSubList()
|
|||
al_free(Buffers);
|
||||
Buffers = nullptr;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint* buffers, ALint value)
|
||||
START_API_FUNC
|
||||
{
|
||||
#define EAX_PREFIX "[EAXSetBufferMode] "
|
||||
|
||||
const auto context = ContextRef{GetContextRef()};
|
||||
if(!context)
|
||||
{
|
||||
ERR(EAX_PREFIX "%s\n", "No current context.");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!eax_g_is_enabled)
|
||||
{
|
||||
context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
switch(value)
|
||||
{
|
||||
case AL_STORAGE_AUTOMATIC:
|
||||
case AL_STORAGE_HARDWARE:
|
||||
case AL_STORAGE_ACCESSIBLE:
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(n == 0)
|
||||
return ALC_TRUE;
|
||||
|
||||
if(n < 0)
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(!buffers)
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
auto device = context->mALDevice.get();
|
||||
std::lock_guard<std::mutex> device_lock{device->BufferLock};
|
||||
size_t total_needed{0};
|
||||
|
||||
// Validate the buffers.
|
||||
//
|
||||
for(auto i = 0;i < n;++i)
|
||||
{
|
||||
const auto buffer = buffers[i];
|
||||
if(buffer == AL_NONE)
|
||||
continue;
|
||||
|
||||
const auto al_buffer = LookupBuffer(device, buffer);
|
||||
if (!al_buffer)
|
||||
{
|
||||
ERR(EAX_PREFIX "Invalid buffer ID %u.\n", buffer);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
/* TODO: Is the store location allowed to change for in-use buffers, or
|
||||
* only when not set/queued on a source?
|
||||
*/
|
||||
|
||||
if(value == AL_STORAGE_HARDWARE && !al_buffer->eax_x_ram_is_hardware)
|
||||
{
|
||||
/* FIXME: This doesn't account for duplicate buffers. When the same
|
||||
* buffer ID is specified multiple times in the provided list, it
|
||||
* counts each instance as more memory that needs to fit in X-RAM.
|
||||
*/
|
||||
if(unlikely(std::numeric_limits<size_t>::max()-al_buffer->OriginalSize < total_needed))
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Buffer size overflow (%u + %zu)\n",
|
||||
al_buffer->OriginalSize, total_needed);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
total_needed += al_buffer->OriginalSize;
|
||||
}
|
||||
}
|
||||
if(total_needed > device->eax_x_ram_free_size)
|
||||
{
|
||||
context->setError(AL_INVALID_ENUM, EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)",
|
||||
total_needed, device->eax_x_ram_free_size);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
// Update the mode.
|
||||
//
|
||||
for(auto i = 0;i < n;++i)
|
||||
{
|
||||
const auto buffer = buffers[i];
|
||||
if(buffer == AL_NONE)
|
||||
continue;
|
||||
|
||||
const auto al_buffer = LookupBuffer(device, buffer);
|
||||
assert(al_buffer);
|
||||
|
||||
if(value != AL_STORAGE_ACCESSIBLE)
|
||||
eax_x_ram_apply(*device, *al_buffer);
|
||||
else
|
||||
eax_x_ram_clear(*device, *al_buffer);
|
||||
al_buffer->eax_x_ram_mode = value;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
|
||||
#undef EAX_PREFIX
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint* pReserved)
|
||||
START_API_FUNC
|
||||
{
|
||||
#define EAX_PREFIX "[EAXGetBufferMode] "
|
||||
|
||||
const auto context = ContextRef{GetContextRef()};
|
||||
if(!context)
|
||||
{
|
||||
ERR(EAX_PREFIX "%s\n", "No current context.");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
if(!eax_g_is_enabled)
|
||||
{
|
||||
context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
if(pReserved)
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
auto device = context->mALDevice.get();
|
||||
std::lock_guard<std::mutex> device_lock{device->BufferLock};
|
||||
|
||||
const auto al_buffer = LookupBuffer(device, buffer);
|
||||
if(!al_buffer)
|
||||
{
|
||||
context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer);
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
return al_buffer->eax_x_ram_mode;
|
||||
|
||||
#undef EAX_PREFIX
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@
|
|||
#include "AL/al.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "buffer_storage.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/buffer_storage.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax_x_ram.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
/* User formats */
|
||||
enum UserFmtType : unsigned char {
|
||||
|
|
@ -35,6 +38,9 @@ enum UserFmtChannels : unsigned char {
|
|||
UserFmtX71 = FmtX71,
|
||||
UserFmtBFormat2D = FmtBFormat2D,
|
||||
UserFmtBFormat3D = FmtBFormat3D,
|
||||
UserFmtUHJ2 = FmtUHJ2,
|
||||
UserFmtUHJ3 = FmtUHJ3,
|
||||
UserFmtUHJ4 = FmtUHJ4,
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -65,6 +71,11 @@ struct ALbuffer : public BufferStorage {
|
|||
ALuint id{0};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
ALenum eax_x_ram_mode{AL_STORAGE_AUTOMATIC};
|
||||
bool eax_x_ram_is_hardware{};
|
||||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
1213
Engine/lib/openal-soft/al/eax_api.cpp
Normal file
1213
Engine/lib/openal-soft/al/eax_api.cpp
Normal file
File diff suppressed because it is too large
Load diff
1557
Engine/lib/openal-soft/al/eax_api.h
Normal file
1557
Engine/lib/openal-soft/al/eax_api.h
Normal file
File diff suppressed because it is too large
Load diff
324
Engine/lib/openal-soft/al/eax_eax_call.cpp
Normal file
324
Engine/lib/openal-soft/al/eax_eax_call.cpp
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "al/eax_eax_call.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto deferred_flag = 0x80000000U;
|
||||
|
||||
class EaxEaxCallException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxEaxCallException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_EAX_CALL", message}
|
||||
{
|
||||
}
|
||||
}; // EaxEaxCallException
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
EaxEaxCall::EaxEaxCall(
|
||||
bool is_get,
|
||||
const GUID& property_set_guid,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
: is_get_{is_get}, version_{0}, property_set_id_{EaxEaxCallPropertySetId::none}
|
||||
, property_id_{property_id & ~deferred_flag}, property_source_id_{property_source_id}
|
||||
, property_buffer_{property_buffer}, property_size_{property_size}
|
||||
{
|
||||
if (false)
|
||||
{
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_Context)
|
||||
{
|
||||
version_ = 4;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::context;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_Context)
|
||||
{
|
||||
version_ = 5;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::context;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX20_ListenerProperties)
|
||||
{
|
||||
version_ = 2;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
property_id_ = convert_eax_v2_0_listener_property_id(property_id_);
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX30_ListenerProperties)
|
||||
{
|
||||
version_ = 3;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot0)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot0)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot1)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 1u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot1)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 1u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot2)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 2u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot2)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 2u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_FXSlot3)
|
||||
{
|
||||
version_ = 4;
|
||||
fx_slot_index_ = 3u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_FXSlot3)
|
||||
{
|
||||
version_ = 5;
|
||||
fx_slot_index_ = 3u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX20_BufferProperties)
|
||||
{
|
||||
version_ = 2;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
property_id_ = convert_eax_v2_0_buffer_property_id(property_id_);
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX30_BufferProperties)
|
||||
{
|
||||
version_ = 3;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX40_Source)
|
||||
{
|
||||
version_ = 4;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == EAXPROPERTYID_EAX50_Source)
|
||||
{
|
||||
version_ = 5;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAX_ReverbProperties)
|
||||
{
|
||||
version_ = 1;
|
||||
fx_slot_index_ = 0u;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
else if (property_set_guid == DSPROPSETID_EAXBUFFER_ReverbProperties)
|
||||
{
|
||||
version_ = 1;
|
||||
property_set_id_ = EaxEaxCallPropertySetId::source;
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Unsupported property set id.");
|
||||
}
|
||||
|
||||
if (version_ < 1 || version_ > 5)
|
||||
{
|
||||
fail("EAX version out of range.");
|
||||
}
|
||||
|
||||
if(!(property_id&deferred_flag))
|
||||
{
|
||||
if(property_set_id_ != EaxEaxCallPropertySetId::fx_slot && property_id_ != 0)
|
||||
{
|
||||
if (!property_buffer)
|
||||
{
|
||||
fail("Null property buffer.");
|
||||
}
|
||||
|
||||
if (property_size == 0)
|
||||
{
|
||||
fail("Empty property.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(property_set_id_ == EaxEaxCallPropertySetId::source && property_source_id_ == 0)
|
||||
{
|
||||
fail("Null AL source id.");
|
||||
}
|
||||
|
||||
if (property_set_id_ == EaxEaxCallPropertySetId::fx_slot)
|
||||
{
|
||||
if (property_id_ < EAXFXSLOT_NONE)
|
||||
{
|
||||
property_set_id_ = EaxEaxCallPropertySetId::fx_slot_effect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void EaxEaxCall::fail(
|
||||
const char* message)
|
||||
{
|
||||
throw EaxEaxCallException{message};
|
||||
}
|
||||
|
||||
ALuint EaxEaxCall::convert_eax_v2_0_listener_property_id(
|
||||
ALuint property_id)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
case DSPROPERTY_EAX20LISTENER_NONE:
|
||||
return EAXREVERB_NONE;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
|
||||
return EAXREVERB_ALLPARAMETERS;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ROOM:
|
||||
return EAXREVERB_ROOM;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ROOMHF:
|
||||
return EAXREVERB_ROOMHF;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
|
||||
return EAXREVERB_ROOMROLLOFFFACTOR;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_DECAYTIME:
|
||||
return EAXREVERB_DECAYTIME;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
|
||||
return EAXREVERB_DECAYHFRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
|
||||
return EAXREVERB_REFLECTIONS;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
|
||||
return EAXREVERB_REFLECTIONSDELAY;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REVERB:
|
||||
return EAXREVERB_REVERB;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
|
||||
return EAXREVERB_REVERBDELAY;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
|
||||
return EAXREVERB_ENVIRONMENT;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
|
||||
return EAXREVERB_ENVIRONMENTSIZE;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
|
||||
return EAXREVERB_ENVIRONMENTDIFFUSION;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
|
||||
return EAXREVERB_AIRABSORPTIONHF;
|
||||
|
||||
case DSPROPERTY_EAX20LISTENER_FLAGS:
|
||||
return EAXREVERB_FLAGS;
|
||||
|
||||
default:
|
||||
fail("Unsupported EAX 2.0 listener property id.");
|
||||
}
|
||||
}
|
||||
|
||||
ALuint EaxEaxCall::convert_eax_v2_0_buffer_property_id(
|
||||
ALuint property_id)
|
||||
{
|
||||
switch (property_id)
|
||||
{
|
||||
case DSPROPERTY_EAX20BUFFER_NONE:
|
||||
return EAXSOURCE_NONE;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS:
|
||||
return EAXSOURCE_ALLPARAMETERS;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_DIRECT:
|
||||
return EAXSOURCE_DIRECT;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_DIRECTHF:
|
||||
return EAXSOURCE_DIRECTHF;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ROOM:
|
||||
return EAXSOURCE_ROOM;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ROOMHF:
|
||||
return EAXSOURCE_ROOMHF;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR:
|
||||
return EAXSOURCE_ROOMROLLOFFFACTOR;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OBSTRUCTION:
|
||||
return EAXSOURCE_OBSTRUCTION;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO:
|
||||
return EAXSOURCE_OBSTRUCTIONLFRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OCCLUSION:
|
||||
return EAXSOURCE_OCCLUSION;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO:
|
||||
return EAXSOURCE_OCCLUSIONLFRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO:
|
||||
return EAXSOURCE_OCCLUSIONROOMRATIO;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF:
|
||||
return EAXSOURCE_OUTSIDEVOLUMEHF;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR:
|
||||
return EAXSOURCE_AIRABSORPTIONFACTOR;
|
||||
|
||||
case DSPROPERTY_EAX20BUFFER_FLAGS:
|
||||
return EAXSOURCE_FLAGS;
|
||||
|
||||
default:
|
||||
fail("Unsupported EAX 2.0 buffer property id.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EaxEaxCall create_eax_call(
|
||||
bool is_get,
|
||||
const GUID* property_set_id,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
{
|
||||
if(!property_set_id)
|
||||
throw EaxEaxCallException{"Null property set ID."};
|
||||
|
||||
return EaxEaxCall{
|
||||
is_get,
|
||||
*property_set_id,
|
||||
property_id,
|
||||
property_source_id,
|
||||
property_buffer,
|
||||
property_size
|
||||
};
|
||||
}
|
||||
117
Engine/lib/openal-soft/al/eax_eax_call.h
Normal file
117
Engine/lib/openal-soft/al/eax_eax_call.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#ifndef EAX_EAX_CALL_INCLUDED
|
||||
#define EAX_EAX_CALL_INCLUDED
|
||||
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alspan.h"
|
||||
|
||||
#include "eax_api.h"
|
||||
#include "eax_fx_slot_index.h"
|
||||
|
||||
|
||||
enum class EaxEaxCallPropertySetId
|
||||
{
|
||||
none,
|
||||
|
||||
context,
|
||||
fx_slot,
|
||||
source,
|
||||
fx_slot_effect,
|
||||
}; // EaxEaxCallPropertySetId
|
||||
|
||||
|
||||
class EaxEaxCall
|
||||
{
|
||||
public:
|
||||
EaxEaxCall(
|
||||
bool is_get,
|
||||
const GUID& property_set_guid,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
bool is_get() const noexcept { return is_get_; }
|
||||
int get_version() const noexcept { return version_; }
|
||||
EaxEaxCallPropertySetId get_property_set_id() const noexcept { return property_set_id_; }
|
||||
ALuint get_property_id() const noexcept { return property_id_; }
|
||||
ALuint get_property_al_name() const noexcept { return property_source_id_; }
|
||||
EaxFxSlotIndex get_fx_slot_index() const noexcept { return fx_slot_index_; }
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
TValue& get_value() const
|
||||
{
|
||||
if (property_size_ < static_cast<ALuint>(sizeof(TValue)))
|
||||
{
|
||||
throw TException{"Property buffer too small."};
|
||||
}
|
||||
|
||||
return *static_cast<TValue*>(property_buffer_);
|
||||
}
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
al::span<TValue> get_values() const
|
||||
{
|
||||
if (property_size_ < static_cast<ALuint>(sizeof(TValue)))
|
||||
{
|
||||
throw TException{"Property buffer too small."};
|
||||
}
|
||||
|
||||
const auto count = property_size_ / sizeof(TValue);
|
||||
|
||||
return al::span<TValue>{static_cast<TValue*>(property_buffer_), count};
|
||||
}
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
void set_value(
|
||||
const TValue& value) const
|
||||
{
|
||||
get_value<TException, TValue>() = value;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
const bool is_get_;
|
||||
int version_;
|
||||
EaxFxSlotIndex fx_slot_index_;
|
||||
EaxEaxCallPropertySetId property_set_id_;
|
||||
|
||||
ALuint property_id_;
|
||||
const ALuint property_source_id_;
|
||||
ALvoid*const property_buffer_;
|
||||
const ALuint property_size_;
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
static void fail(
|
||||
const char* message);
|
||||
|
||||
|
||||
static ALuint convert_eax_v2_0_listener_property_id(
|
||||
ALuint property_id);
|
||||
|
||||
static ALuint convert_eax_v2_0_buffer_property_id(
|
||||
ALuint property_id);
|
||||
}; // EaxEaxCall
|
||||
|
||||
|
||||
EaxEaxCall create_eax_call(
|
||||
bool is_get,
|
||||
const GUID* property_set_id,
|
||||
ALuint property_id,
|
||||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
|
||||
#endif // !EAX_EAX_CALL_INCLUDED
|
||||
3
Engine/lib/openal-soft/al/eax_effect.cpp
Normal file
3
Engine/lib/openal-soft/al/eax_effect.cpp
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_effect.h"
|
||||
44
Engine/lib/openal-soft/al/eax_effect.h
Normal file
44
Engine/lib/openal-soft/al/eax_effect.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef EAX_EFFECT_INCLUDED
|
||||
#define EAX_EFFECT_INCLUDED
|
||||
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "eax_eax_call.h"
|
||||
|
||||
class EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxEffect(ALenum type) : al_effect_type_{type} { }
|
||||
virtual ~EaxEffect() = default;
|
||||
|
||||
const ALenum al_effect_type_;
|
||||
EffectProps al_effect_props_{};
|
||||
|
||||
virtual void dispatch(const EaxEaxCall& eax_call) = 0;
|
||||
|
||||
// Returns "true" if any immediated property was changed.
|
||||
// [[nodiscard]]
|
||||
virtual bool apply_deferred() = 0;
|
||||
}; // EaxEffect
|
||||
|
||||
|
||||
using EaxEffectUPtr = std::unique_ptr<EaxEffect>;
|
||||
|
||||
EaxEffectUPtr eax_create_eax_null_effect();
|
||||
EaxEffectUPtr eax_create_eax_chorus_effect();
|
||||
EaxEffectUPtr eax_create_eax_distortion_effect();
|
||||
EaxEffectUPtr eax_create_eax_echo_effect();
|
||||
EaxEffectUPtr eax_create_eax_flanger_effect();
|
||||
EaxEffectUPtr eax_create_eax_frequency_shifter_effect();
|
||||
EaxEffectUPtr eax_create_eax_vocal_morpher_effect();
|
||||
EaxEffectUPtr eax_create_eax_pitch_shifter_effect();
|
||||
EaxEffectUPtr eax_create_eax_ring_modulator_effect();
|
||||
EaxEffectUPtr eax_create_eax_auto_wah_effect();
|
||||
EaxEffectUPtr eax_create_eax_compressor_effect();
|
||||
EaxEffectUPtr eax_create_eax_equalizer_effect();
|
||||
EaxEffectUPtr eax_create_eax_reverb_effect();
|
||||
|
||||
#endif // !EAX_EFFECT_INCLUDED
|
||||
63
Engine/lib/openal-soft/al/eax_exception.cpp
Normal file
63
Engine/lib/openal-soft/al/eax_exception.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_exception.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
EaxException::EaxException(
|
||||
const char* context,
|
||||
const char* message)
|
||||
:
|
||||
std::runtime_error{make_message(context, message)}
|
||||
{
|
||||
}
|
||||
|
||||
std::string EaxException::make_message(
|
||||
const char* context,
|
||||
const char* message)
|
||||
{
|
||||
const auto context_size = (context ? std::string::traits_type::length(context) : 0);
|
||||
const auto has_contex = (context_size > 0);
|
||||
|
||||
const auto message_size = (message ? std::string::traits_type::length(message) : 0);
|
||||
const auto has_message = (message_size > 0);
|
||||
|
||||
if (!has_contex && !has_message)
|
||||
{
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
static constexpr char left_prefix[] = "[";
|
||||
const auto left_prefix_size = std::string::traits_type::length(left_prefix);
|
||||
|
||||
static constexpr char right_prefix[] = "] ";
|
||||
const auto right_prefix_size = std::string::traits_type::length(right_prefix);
|
||||
|
||||
const auto what_size =
|
||||
(
|
||||
has_contex ?
|
||||
left_prefix_size + context_size + right_prefix_size :
|
||||
0) +
|
||||
message_size +
|
||||
1;
|
||||
|
||||
auto what = std::string{};
|
||||
what.reserve(what_size);
|
||||
|
||||
if (has_contex)
|
||||
{
|
||||
what.append(left_prefix, left_prefix_size);
|
||||
what.append(context, context_size);
|
||||
what.append(right_prefix, right_prefix_size);
|
||||
}
|
||||
|
||||
if (has_message)
|
||||
{
|
||||
what.append(message, message_size);
|
||||
}
|
||||
|
||||
return what;
|
||||
}
|
||||
25
Engine/lib/openal-soft/al/eax_exception.h
Normal file
25
Engine/lib/openal-soft/al/eax_exception.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef EAX_EXCEPTION_INCLUDED
|
||||
#define EAX_EXCEPTION_INCLUDED
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
|
||||
class EaxException :
|
||||
public std::runtime_error
|
||||
{
|
||||
public:
|
||||
EaxException(
|
||||
const char* context,
|
||||
const char* message);
|
||||
|
||||
|
||||
private:
|
||||
static std::string make_message(
|
||||
const char* context,
|
||||
const char* message);
|
||||
}; // EaxException
|
||||
|
||||
|
||||
#endif // !EAX_EXCEPTION_INCLUDED
|
||||
71
Engine/lib/openal-soft/al/eax_fx_slot_index.cpp
Normal file
71
Engine/lib/openal-soft/al/eax_fx_slot_index.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_fx_slot_index.h"
|
||||
|
||||
#include "eax_exception.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
class EaxFxSlotIndexException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxFxSlotIndexException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_FX_SLOT_INDEX", message}
|
||||
{
|
||||
}
|
||||
}; // EaxFxSlotIndexException
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void EaxFxSlotIndex::set(EaxFxSlotIndexValue index)
|
||||
{
|
||||
if(index >= EaxFxSlotIndexValue{EAX_MAX_FXSLOTS})
|
||||
fail("Index out of range.");
|
||||
|
||||
emplace(index);
|
||||
}
|
||||
|
||||
void EaxFxSlotIndex::set(const GUID &guid)
|
||||
{
|
||||
if (false)
|
||||
{
|
||||
}
|
||||
else if (guid == EAX_NULL_GUID)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
else if (guid == EAXPROPERTYID_EAX40_FXSlot0 || guid == EAXPROPERTYID_EAX50_FXSlot0)
|
||||
{
|
||||
emplace(0u);
|
||||
}
|
||||
else if (guid == EAXPROPERTYID_EAX40_FXSlot1 || guid == EAXPROPERTYID_EAX50_FXSlot1)
|
||||
{
|
||||
emplace(1u);
|
||||
}
|
||||
else if (guid == EAXPROPERTYID_EAX40_FXSlot2 || guid == EAXPROPERTYID_EAX50_FXSlot2)
|
||||
{
|
||||
emplace(2u);
|
||||
}
|
||||
else if (guid == EAXPROPERTYID_EAX40_FXSlot3 || guid == EAXPROPERTYID_EAX50_FXSlot3)
|
||||
{
|
||||
emplace(3u);
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Unsupported GUID.");
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void EaxFxSlotIndex::fail(const char* message)
|
||||
{
|
||||
throw EaxFxSlotIndexException{message};
|
||||
}
|
||||
41
Engine/lib/openal-soft/al/eax_fx_slot_index.h
Normal file
41
Engine/lib/openal-soft/al/eax_fx_slot_index.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef EAX_FX_SLOT_INDEX_INCLUDED
|
||||
#define EAX_FX_SLOT_INDEX_INCLUDED
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include "eax_api.h"
|
||||
|
||||
|
||||
using EaxFxSlotIndexValue = std::size_t;
|
||||
|
||||
class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
|
||||
{
|
||||
public:
|
||||
using al::optional<EaxFxSlotIndexValue>::optional;
|
||||
|
||||
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
|
||||
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
|
||||
|
||||
void set(EaxFxSlotIndexValue index);
|
||||
void set(const GUID& guid);
|
||||
|
||||
private:
|
||||
[[noreturn]]
|
||||
static void fail(const char *message);
|
||||
}; // EaxFxSlotIndex
|
||||
|
||||
inline bool operator==(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept
|
||||
{
|
||||
if(lhs.has_value() != rhs.has_value())
|
||||
return false;
|
||||
if(lhs.has_value())
|
||||
return *lhs == *rhs;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const EaxFxSlotIndex& lhs, const EaxFxSlotIndex& rhs) noexcept
|
||||
{ return !(lhs == rhs); }
|
||||
|
||||
#endif // !EAX_FX_SLOT_INDEX_INCLUDED
|
||||
84
Engine/lib/openal-soft/al/eax_fx_slots.cpp
Normal file
84
Engine/lib/openal-soft/al/eax_fx_slots.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_fx_slots.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "eax_exception.h"
|
||||
|
||||
#include "eax_api.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
class EaxFxSlotsException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxFxSlotsException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_FX_SLOTS", message}
|
||||
{
|
||||
}
|
||||
}; // EaxFxSlotsException
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void EaxFxSlots::initialize(
|
||||
ALCcontext& al_context)
|
||||
{
|
||||
initialize_fx_slots(al_context);
|
||||
}
|
||||
|
||||
void EaxFxSlots::uninitialize() noexcept
|
||||
{
|
||||
for (auto& fx_slot : fx_slots_)
|
||||
{
|
||||
fx_slot = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index) const
|
||||
{
|
||||
if(!index.has_value())
|
||||
fail("Empty index.");
|
||||
return *fx_slots_[index.value()];
|
||||
}
|
||||
|
||||
ALeffectslot& EaxFxSlots::get(EaxFxSlotIndex index)
|
||||
{
|
||||
if(!index.has_value())
|
||||
fail("Empty index.");
|
||||
return *fx_slots_[index.value()];
|
||||
}
|
||||
|
||||
void EaxFxSlots::unlock_legacy() noexcept
|
||||
{
|
||||
fx_slots_[0]->eax_unlock_legacy();
|
||||
fx_slots_[1]->eax_unlock_legacy();
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void EaxFxSlots::fail(
|
||||
const char* message)
|
||||
{
|
||||
throw EaxFxSlotsException{message};
|
||||
}
|
||||
|
||||
void EaxFxSlots::initialize_fx_slots(
|
||||
ALCcontext& al_context)
|
||||
{
|
||||
auto fx_slot_index = EaxFxSlotIndexValue{};
|
||||
|
||||
for (auto& fx_slot : fx_slots_)
|
||||
{
|
||||
fx_slot = eax_create_al_effect_slot(al_context);
|
||||
fx_slot->eax_initialize(al_context, fx_slot_index);
|
||||
fx_slot_index += 1;
|
||||
}
|
||||
}
|
||||
54
Engine/lib/openal-soft/al/eax_fx_slots.h
Normal file
54
Engine/lib/openal-soft/al/eax_fx_slots.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef EAX_FX_SLOTS_INCLUDED
|
||||
#define EAX_FX_SLOTS_INCLUDED
|
||||
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "al/auxeffectslot.h"
|
||||
|
||||
#include "eax_api.h"
|
||||
|
||||
#include "eax_fx_slot_index.h"
|
||||
|
||||
|
||||
class EaxFxSlots
|
||||
{
|
||||
public:
|
||||
void initialize(
|
||||
ALCcontext& al_context);
|
||||
|
||||
void uninitialize() noexcept;
|
||||
|
||||
void commit()
|
||||
{
|
||||
for(auto& fx_slot : fx_slots_)
|
||||
fx_slot->eax_commit();
|
||||
}
|
||||
|
||||
|
||||
const ALeffectslot& get(
|
||||
EaxFxSlotIndex index) const;
|
||||
|
||||
ALeffectslot& get(
|
||||
EaxFxSlotIndex index);
|
||||
|
||||
void unlock_legacy() noexcept;
|
||||
|
||||
|
||||
private:
|
||||
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
|
||||
|
||||
|
||||
Items fx_slots_{};
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
static void fail(
|
||||
const char* message);
|
||||
|
||||
void initialize_fx_slots(
|
||||
ALCcontext& al_context);
|
||||
}; // EaxFxSlots
|
||||
|
||||
|
||||
#endif // !EAX_FX_SLOTS_INCLUDED
|
||||
21
Engine/lib/openal-soft/al/eax_globals.cpp
Normal file
21
Engine/lib/openal-soft/al/eax_globals.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_globals.h"
|
||||
|
||||
|
||||
bool eax_g_is_enabled = true;
|
||||
|
||||
|
||||
const char eax1_ext_name[] = "EAX";
|
||||
const char eax2_ext_name[] = "EAX2.0";
|
||||
const char eax3_ext_name[] = "EAX3.0";
|
||||
const char eax4_ext_name[] = "EAX4.0";
|
||||
const char eax5_ext_name[] = "EAX5.0";
|
||||
|
||||
const char eax_x_ram_ext_name[] = "EAX-RAM";
|
||||
|
||||
const char eax_eax_set_func_name[] = "EAXSet";
|
||||
const char eax_eax_get_func_name[] = "EAXGet";
|
||||
|
||||
const char eax_eax_set_buffer_mode_func_name[] = "EAXSetBufferMode";
|
||||
const char eax_eax_get_buffer_mode_func_name[] = "EAXGetBufferMode";
|
||||
22
Engine/lib/openal-soft/al/eax_globals.h
Normal file
22
Engine/lib/openal-soft/al/eax_globals.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef EAX_GLOBALS_INCLUDED
|
||||
#define EAX_GLOBALS_INCLUDED
|
||||
|
||||
|
||||
extern bool eax_g_is_enabled;
|
||||
|
||||
|
||||
extern const char eax1_ext_name[];
|
||||
extern const char eax2_ext_name[];
|
||||
extern const char eax3_ext_name[];
|
||||
extern const char eax4_ext_name[];
|
||||
extern const char eax5_ext_name[];
|
||||
|
||||
extern const char eax_x_ram_ext_name[];
|
||||
|
||||
extern const char eax_eax_set_func_name[];
|
||||
extern const char eax_eax_get_func_name[];
|
||||
|
||||
extern const char eax_eax_set_buffer_mode_func_name[];
|
||||
extern const char eax_eax_get_buffer_mode_func_name[];
|
||||
|
||||
#endif // !EAX_GLOBALS_INCLUDED
|
||||
36
Engine/lib/openal-soft/al/eax_utils.cpp
Normal file
36
Engine/lib/openal-soft/al/eax_utils.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
|
||||
void eax_log_exception(
|
||||
const char* message) noexcept
|
||||
{
|
||||
const auto exception_ptr = std::current_exception();
|
||||
|
||||
assert(exception_ptr);
|
||||
|
||||
if (message)
|
||||
{
|
||||
ERR("%s\n", message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(exception_ptr);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
const auto ex_message = ex.what();
|
||||
ERR("%s\n", ex_message);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ERR("%s\n", "Generic exception.");
|
||||
}
|
||||
}
|
||||
132
Engine/lib/openal-soft/al/eax_utils.h
Normal file
132
Engine/lib/openal-soft/al/eax_utils.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#ifndef EAX_UTILS_INCLUDED
|
||||
#define EAX_UTILS_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
struct EaxAlLowPassParam
|
||||
{
|
||||
float gain;
|
||||
float gain_hf;
|
||||
}; // EaxAlLowPassParam
|
||||
|
||||
|
||||
void eax_log_exception(
|
||||
const char* message = nullptr) noexcept;
|
||||
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TValue
|
||||
>
|
||||
void eax_validate_range(
|
||||
const char* value_name,
|
||||
const TValue& value,
|
||||
const TValue& min_value,
|
||||
const TValue& max_value)
|
||||
{
|
||||
if (value >= min_value && value <= max_value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto message =
|
||||
std::string{value_name} +
|
||||
" out of range (value: " +
|
||||
std::to_string(value) + "; min: " +
|
||||
std::to_string(min_value) + "; max: " +
|
||||
std::to_string(max_value) + ").";
|
||||
|
||||
throw TException{message.c_str()};
|
||||
}
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
||||
template<
|
||||
typename T
|
||||
>
|
||||
struct EaxIsBitFieldStruct
|
||||
{
|
||||
private:
|
||||
using yes = std::true_type;
|
||||
using no = std::false_type;
|
||||
|
||||
template<
|
||||
typename U
|
||||
>
|
||||
static auto test(int) -> decltype(std::declval<typename U::EaxIsBitFieldStruct>(), yes{});
|
||||
|
||||
template<
|
||||
typename
|
||||
>
|
||||
static no test(...);
|
||||
|
||||
|
||||
public:
|
||||
static constexpr auto value = std::is_same<decltype(test<T>(0)), yes>::value;
|
||||
}; // EaxIsBitFieldStruct
|
||||
|
||||
|
||||
template<
|
||||
typename T,
|
||||
typename TValue
|
||||
>
|
||||
inline bool eax_bit_fields_are_equal(
|
||||
const T& lhs,
|
||||
const T& rhs) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(TValue), "Invalid type size.");
|
||||
|
||||
return reinterpret_cast<const TValue&>(lhs) == reinterpret_cast<const TValue&>(rhs);
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
template<
|
||||
typename T,
|
||||
std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
|
||||
>
|
||||
inline bool operator==(
|
||||
const T& lhs,
|
||||
const T& rhs) noexcept
|
||||
{
|
||||
using Value = std::conditional_t<
|
||||
sizeof(T) == 1,
|
||||
std::uint8_t,
|
||||
std::conditional_t<
|
||||
sizeof(T) == 2,
|
||||
std::uint16_t,
|
||||
std::conditional_t<
|
||||
sizeof(T) == 4,
|
||||
std::uint32_t,
|
||||
void
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
static_assert(!std::is_same<Value, void>::value, "Unsupported type.");
|
||||
|
||||
return detail::eax_bit_fields_are_equal<T, Value>(lhs, rhs);
|
||||
}
|
||||
|
||||
template<
|
||||
typename T,
|
||||
std::enable_if_t<detail::EaxIsBitFieldStruct<T>::value, int> = 0
|
||||
>
|
||||
inline bool operator!=(
|
||||
const T& lhs,
|
||||
const T& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
#endif // !EAX_UTILS_INCLUDED
|
||||
3
Engine/lib/openal-soft/al/eax_x_ram.cpp
Normal file
3
Engine/lib/openal-soft/al/eax_x_ram.cpp
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "eax_x_ram.h"
|
||||
38
Engine/lib/openal-soft/al/eax_x_ram.h
Normal file
38
Engine/lib/openal-soft/al/eax_x_ram.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef EAX_X_RAM_INCLUDED
|
||||
#define EAX_X_RAM_INCLUDED
|
||||
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
|
||||
constexpr auto eax_x_ram_min_size = ALsizei{};
|
||||
constexpr auto eax_x_ram_max_size = ALsizei{64 * 1'024 * 1'024};
|
||||
|
||||
|
||||
constexpr auto AL_EAX_RAM_SIZE = ALenum{0x202201};
|
||||
constexpr auto AL_EAX_RAM_FREE = ALenum{0x202202};
|
||||
|
||||
constexpr auto AL_STORAGE_AUTOMATIC = ALenum{0x202203};
|
||||
constexpr auto AL_STORAGE_HARDWARE = ALenum{0x202204};
|
||||
constexpr auto AL_STORAGE_ACCESSIBLE = ALenum{0x202205};
|
||||
|
||||
|
||||
constexpr auto AL_EAX_RAM_SIZE_NAME = "AL_EAX_RAM_SIZE";
|
||||
constexpr auto AL_EAX_RAM_FREE_NAME = "AL_EAX_RAM_FREE";
|
||||
|
||||
constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
|
||||
constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
|
||||
constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
|
||||
|
||||
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(
|
||||
ALsizei n,
|
||||
const ALuint* buffers,
|
||||
ALint value);
|
||||
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(
|
||||
ALuint buffer,
|
||||
ALint* pReserved);
|
||||
|
||||
|
||||
#endif // !EAX_X_RAM_INCLUDED
|
||||
|
|
@ -39,17 +39,23 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects/base.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "eax_exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
const EffectList gEffectList[16]{
|
||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||
|
|
@ -181,12 +187,12 @@ ALeffect *AllocEffect(ALCdevice *device)
|
|||
{
|
||||
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
||||
[](const EffectSubList &entry) noexcept -> bool
|
||||
{ return entry.FreeMask != 0; }
|
||||
);
|
||||
{ return entry.FreeMask != 0; });
|
||||
auto lidx = static_cast<ALuint>(std::distance(device->EffectList.begin(), sublist));
|
||||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALeffect *effect{::new (sublist->Effects + slidx) ALeffect{}};
|
||||
ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
|
||||
/* Add 1 to avoid effect ID 0. */
|
||||
|
|
@ -233,7 +239,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
|
|
@ -273,7 +279,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
/* First try to find any effects that are invalid. */
|
||||
|
|
@ -304,7 +310,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
|
|
@ -319,7 +325,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -369,7 +375,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -392,7 +398,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -415,7 +421,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -438,7 +444,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -470,7 +476,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -493,7 +499,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -516,7 +522,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
@ -744,3 +750,16 @@ void LoadReverbPreset(const char *name, ALeffect *effect)
|
|||
|
||||
WARN("Reverb preset '%s' not found\n", name);
|
||||
}
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept
|
||||
{
|
||||
if(type == AL_EFFECT_NULL)
|
||||
return true;
|
||||
|
||||
for(const auto &effect_item : gEffectList)
|
||||
{
|
||||
if(type == effect_item.val && !DisabledEffects[effect_item.type])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,4 +57,6 @@ void InitEffect(ALeffect *effect);
|
|||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,9 +8,17 @@
|
|||
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "effects/base.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
|
||||
|
|
@ -107,3 +115,434 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Autowah);
|
||||
|
||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxAutoWahEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxAutoWahEffectDirtyFlagsValue flAttackTime : 1;
|
||||
EaxAutoWahEffectDirtyFlagsValue flReleaseTime : 1;
|
||||
EaxAutoWahEffectDirtyFlagsValue lResonance : 1;
|
||||
EaxAutoWahEffectDirtyFlagsValue lPeakLevel : 1;
|
||||
}; // EaxAutoWahEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxAutoWahEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxAutoWahEffect();
|
||||
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXAUTOWAHPROPERTIES eax_{};
|
||||
EAXAUTOWAHPROPERTIES eax_d_{};
|
||||
EaxAutoWahEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
|
||||
void set_efx_attack_time();
|
||||
|
||||
void set_efx_release_time();
|
||||
|
||||
void set_efx_resonance();
|
||||
|
||||
void set_efx_peak_gain();
|
||||
|
||||
void set_efx_defaults();
|
||||
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
void validate_attack_time(
|
||||
float flAttackTime);
|
||||
|
||||
void validate_release_time(
|
||||
float flReleaseTime);
|
||||
|
||||
void validate_resonance(
|
||||
long lResonance);
|
||||
|
||||
void validate_peak_level(
|
||||
long lPeakLevel);
|
||||
|
||||
void validate_all(
|
||||
const EAXAUTOWAHPROPERTIES& eax_all);
|
||||
|
||||
|
||||
void defer_attack_time(
|
||||
float flAttackTime);
|
||||
|
||||
void defer_release_time(
|
||||
float flReleaseTime);
|
||||
|
||||
void defer_resonance(
|
||||
long lResonance);
|
||||
|
||||
void defer_peak_level(
|
||||
long lPeakLevel);
|
||||
|
||||
void defer_all(
|
||||
const EAXAUTOWAHPROPERTIES& eax_all);
|
||||
|
||||
|
||||
void defer_attack_time(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void defer_release_time(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void defer_resonance(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void defer_peak_level(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void defer_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxAutoWahEffect
|
||||
|
||||
|
||||
class EaxAutoWahEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxAutoWahEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_AUTO_WAH_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxAutoWahEffectException
|
||||
|
||||
|
||||
EaxAutoWahEffect::EaxAutoWahEffect()
|
||||
: EaxEffect{AL_EFFECT_AUTOWAH}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||
eax_.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||
eax_.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||
eax_.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set_efx_attack_time()
|
||||
{
|
||||
const auto attack_time = clamp(
|
||||
eax_.flAttackTime,
|
||||
AL_AUTOWAH_MIN_ATTACK_TIME,
|
||||
AL_AUTOWAH_MAX_ATTACK_TIME);
|
||||
|
||||
al_effect_props_.Autowah.AttackTime = attack_time;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set_efx_release_time()
|
||||
{
|
||||
const auto release_time = clamp(
|
||||
eax_.flReleaseTime,
|
||||
AL_AUTOWAH_MIN_RELEASE_TIME,
|
||||
AL_AUTOWAH_MAX_RELEASE_TIME);
|
||||
|
||||
al_effect_props_.Autowah.ReleaseTime = release_time;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set_efx_resonance()
|
||||
{
|
||||
const auto resonance = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lResonance)),
|
||||
AL_AUTOWAH_MIN_RESONANCE,
|
||||
AL_AUTOWAH_MAX_RESONANCE);
|
||||
|
||||
al_effect_props_.Autowah.Resonance = resonance;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set_efx_peak_gain()
|
||||
{
|
||||
const auto peak_gain = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lPeakLevel)),
|
||||
AL_AUTOWAH_MIN_PEAK_GAIN,
|
||||
AL_AUTOWAH_MAX_PEAK_GAIN);
|
||||
|
||||
al_effect_props_.Autowah.PeakGain = peak_gain;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_attack_time();
|
||||
set_efx_release_time();
|
||||
set_efx_resonance();
|
||||
set_efx_peak_gain();
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch (eax_call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE:
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_ATTACKTIME:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.flAttackTime);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_RELEASETIME:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.flReleaseTime);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_RESONANCE:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.lResonance);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_PEAKLEVEL:
|
||||
eax_call.set_value<EaxAutoWahEffectException>(eax_.lPeakLevel);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxAutoWahEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_attack_time(
|
||||
float flAttackTime)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Attack Time",
|
||||
flAttackTime,
|
||||
EAXAUTOWAH_MINATTACKTIME,
|
||||
EAXAUTOWAH_MAXATTACKTIME);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_release_time(
|
||||
float flReleaseTime)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Release Time",
|
||||
flReleaseTime,
|
||||
EAXAUTOWAH_MINRELEASETIME,
|
||||
EAXAUTOWAH_MAXRELEASETIME);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_resonance(
|
||||
long lResonance)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Resonance",
|
||||
lResonance,
|
||||
EAXAUTOWAH_MINRESONANCE,
|
||||
EAXAUTOWAH_MAXRESONANCE);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_peak_level(
|
||||
long lPeakLevel)
|
||||
{
|
||||
eax_validate_range<EaxAutoWahEffectException>(
|
||||
"Peak Level",
|
||||
lPeakLevel,
|
||||
EAXAUTOWAH_MINPEAKLEVEL,
|
||||
EAXAUTOWAH_MAXPEAKLEVEL);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::validate_all(
|
||||
const EAXAUTOWAHPROPERTIES& eax_all)
|
||||
{
|
||||
validate_attack_time(eax_all.flAttackTime);
|
||||
validate_release_time(eax_all.flReleaseTime);
|
||||
validate_resonance(eax_all.lResonance);
|
||||
validate_peak_level(eax_all.lPeakLevel);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_attack_time(
|
||||
float flAttackTime)
|
||||
{
|
||||
eax_d_.flAttackTime = flAttackTime;
|
||||
eax_dirty_flags_.flAttackTime = (eax_.flAttackTime != eax_d_.flAttackTime);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_release_time(
|
||||
float flReleaseTime)
|
||||
{
|
||||
eax_d_.flReleaseTime = flReleaseTime;
|
||||
eax_dirty_flags_.flReleaseTime = (eax_.flReleaseTime != eax_d_.flReleaseTime);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_resonance(
|
||||
long lResonance)
|
||||
{
|
||||
eax_d_.lResonance = lResonance;
|
||||
eax_dirty_flags_.lResonance = (eax_.lResonance != eax_d_.lResonance);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_peak_level(
|
||||
long lPeakLevel)
|
||||
{
|
||||
eax_d_.lPeakLevel = lPeakLevel;
|
||||
eax_dirty_flags_.lPeakLevel = (eax_.lPeakLevel != eax_d_.lPeakLevel);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_all(
|
||||
const EAXAUTOWAHPROPERTIES& eax_all)
|
||||
{
|
||||
validate_all(eax_all);
|
||||
|
||||
defer_attack_time(eax_all.flAttackTime);
|
||||
defer_release_time(eax_all.flReleaseTime);
|
||||
defer_resonance(eax_all.lResonance);
|
||||
defer_peak_level(eax_all.lPeakLevel);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_attack_time(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& attack_time =
|
||||
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flAttackTime)>();
|
||||
|
||||
validate_attack_time(attack_time);
|
||||
defer_attack_time(attack_time);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_release_time(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& release_time =
|
||||
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flReleaseTime)>();
|
||||
|
||||
validate_release_time(release_time);
|
||||
defer_release_time(release_time);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_resonance(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& resonance =
|
||||
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lResonance)>();
|
||||
|
||||
validate_resonance(resonance);
|
||||
defer_resonance(resonance);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_peak_level(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& peak_level =
|
||||
eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lPeakLevel)>();
|
||||
|
||||
validate_peak_level(peak_level);
|
||||
defer_peak_level(peak_level);
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxAutoWahEffectException, const EAXAUTOWAHPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxAutoWahEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.flAttackTime)
|
||||
{
|
||||
set_efx_attack_time();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flReleaseTime)
|
||||
{
|
||||
set_efx_release_time();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lResonance)
|
||||
{
|
||||
set_efx_resonance();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lPeakLevel)
|
||||
{
|
||||
set_efx_peak_gain();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxAutoWahEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxAutoWahEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch (eax_call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE:
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_ATTACKTIME:
|
||||
defer_attack_time(eax_call);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_RELEASETIME:
|
||||
defer_release_time(eax_call);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_RESONANCE:
|
||||
defer_resonance(eax_call);
|
||||
break;
|
||||
|
||||
case EAXAUTOWAH_PEAKLEVEL:
|
||||
defer_peak_level(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxAutoWahEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_auto_wah_effect()
|
||||
{
|
||||
return std::make_unique<::EaxAutoWahEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,8 +4,15 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -70,3 +77,220 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Compressor);
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxCompressorEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxCompressorEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxCompressorEffectDirtyFlagsValue ulOnOff : 1;
|
||||
}; // EaxCompressorEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxCompressorEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxCompressorEffect();
|
||||
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXAGCCOMPRESSORPROPERTIES eax_{};
|
||||
EAXAGCCOMPRESSORPROPERTIES eax_d_{};
|
||||
EaxCompressorEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_on_off();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_on_off(unsigned long ulOnOff);
|
||||
void validate_all(const EAXAGCCOMPRESSORPROPERTIES& eax_all);
|
||||
|
||||
void defer_on_off(unsigned long ulOnOff);
|
||||
void defer_all(const EAXAGCCOMPRESSORPROPERTIES& eax_all);
|
||||
|
||||
void defer_on_off(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxCompressorEffect
|
||||
|
||||
|
||||
class EaxCompressorEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxCompressorEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_COMPRESSOR_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxCompressorEffectException
|
||||
|
||||
|
||||
EaxCompressorEffect::EaxCompressorEffect()
|
||||
: EaxEffect{AL_EFFECT_COMPRESSOR}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
void EaxCompressorEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set_efx_on_off()
|
||||
{
|
||||
const auto on_off = clamp(
|
||||
static_cast<ALint>(eax_.ulOnOff),
|
||||
AL_COMPRESSOR_MIN_ONOFF,
|
||||
AL_COMPRESSOR_MAX_ONOFF);
|
||||
|
||||
al_effect_props_.Compressor.OnOff = (on_off != AL_FALSE);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_on_off();
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE:
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxCompressorEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ONOFF:
|
||||
eax_call.set_value<EaxCompressorEffectException>(eax_.ulOnOff);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxCompressorEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::validate_on_off(
|
||||
unsigned long ulOnOff)
|
||||
{
|
||||
eax_validate_range<EaxCompressorEffectException>(
|
||||
"On-Off",
|
||||
ulOnOff,
|
||||
EAXAGCCOMPRESSOR_MINONOFF,
|
||||
EAXAGCCOMPRESSOR_MAXONOFF);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::validate_all(
|
||||
const EAXAGCCOMPRESSORPROPERTIES& eax_all)
|
||||
{
|
||||
validate_on_off(eax_all.ulOnOff);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_on_off(
|
||||
unsigned long ulOnOff)
|
||||
{
|
||||
eax_d_.ulOnOff = ulOnOff;
|
||||
eax_dirty_flags_.ulOnOff = (eax_.ulOnOff != eax_d_.ulOnOff);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_all(
|
||||
const EAXAGCCOMPRESSORPROPERTIES& eax_all)
|
||||
{
|
||||
defer_on_off(eax_all.ulOnOff);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_on_off(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& on_off =
|
||||
eax_call.get_value<EaxCompressorEffectException, const decltype(EAXAGCCOMPRESSORPROPERTIES::ulOnOff)>();
|
||||
|
||||
validate_on_off(on_off);
|
||||
defer_on_off(on_off);
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxCompressorEffectException, const EAXAGCCOMPRESSORPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxCompressorEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxCompressorEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.ulOnOff)
|
||||
{
|
||||
set_efx_on_off();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxCompressorEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxCompressorEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE:
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXAGCCOMPRESSOR_ONOFF:
|
||||
defer_on_off(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxCompressorEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_compressor_effect()
|
||||
{
|
||||
return std::make_unique<EaxCompressorEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "inprogext.h"
|
||||
#include "alc/inprogext.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
#include <cmath>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -112,3 +119,453 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Distortion);
|
||||
|
||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxDistortionEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxDistortionEffectDirtyFlagsValue flEdge : 1;
|
||||
EaxDistortionEffectDirtyFlagsValue lGain : 1;
|
||||
EaxDistortionEffectDirtyFlagsValue flLowPassCutOff : 1;
|
||||
EaxDistortionEffectDirtyFlagsValue flEQCenter : 1;
|
||||
EaxDistortionEffectDirtyFlagsValue flEQBandwidth : 1;
|
||||
}; // EaxDistortionEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxDistortionEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxDistortionEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXDISTORTIONPROPERTIES eax_{};
|
||||
EAXDISTORTIONPROPERTIES eax_d_{};
|
||||
EaxDistortionEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_edge();
|
||||
void set_efx_gain();
|
||||
void set_efx_lowpass_cutoff();
|
||||
void set_efx_eq_center();
|
||||
void set_efx_eq_bandwidth();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_edge(float flEdge);
|
||||
void validate_gain(long lGain);
|
||||
void validate_lowpass_cutoff(float flLowPassCutOff);
|
||||
void validate_eq_center(float flEQCenter);
|
||||
void validate_eq_bandwidth(float flEQBandwidth);
|
||||
void validate_all(const EAXDISTORTIONPROPERTIES& eax_all);
|
||||
|
||||
void defer_edge(float flEdge);
|
||||
void defer_gain(long lGain);
|
||||
void defer_low_pass_cutoff(float flLowPassCutOff);
|
||||
void defer_eq_center(float flEQCenter);
|
||||
void defer_eq_bandwidth(float flEQBandwidth);
|
||||
void defer_all(const EAXDISTORTIONPROPERTIES& eax_all);
|
||||
|
||||
void defer_edge(const EaxEaxCall& eax_call);
|
||||
void defer_gain(const EaxEaxCall& eax_call);
|
||||
void defer_low_pass_cutoff(const EaxEaxCall& eax_call);
|
||||
void defer_eq_center(const EaxEaxCall& eax_call);
|
||||
void defer_eq_bandwidth(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxDistortionEffect
|
||||
|
||||
|
||||
class EaxDistortionEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxDistortionEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_DISTORTION_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxDistortionEffectException
|
||||
|
||||
|
||||
EaxDistortionEffect::EaxDistortionEffect()
|
||||
: EaxEffect{AL_EFFECT_DISTORTION}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||
eax_.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||
eax_.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||
eax_.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||
eax_.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_efx_edge()
|
||||
{
|
||||
const auto edge = clamp(
|
||||
eax_.flEdge,
|
||||
AL_DISTORTION_MIN_EDGE,
|
||||
AL_DISTORTION_MAX_EDGE);
|
||||
|
||||
al_effect_props_.Distortion.Edge = edge;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_efx_gain()
|
||||
{
|
||||
const auto gain = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lGain)),
|
||||
AL_DISTORTION_MIN_GAIN,
|
||||
AL_DISTORTION_MAX_GAIN);
|
||||
|
||||
al_effect_props_.Distortion.Gain = gain;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_efx_lowpass_cutoff()
|
||||
{
|
||||
const auto lowpass_cutoff = clamp(
|
||||
eax_.flLowPassCutOff,
|
||||
AL_DISTORTION_MIN_LOWPASS_CUTOFF,
|
||||
AL_DISTORTION_MAX_LOWPASS_CUTOFF);
|
||||
|
||||
al_effect_props_.Distortion.LowpassCutoff = lowpass_cutoff;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_efx_eq_center()
|
||||
{
|
||||
const auto eq_center = clamp(
|
||||
eax_.flEQCenter,
|
||||
AL_DISTORTION_MIN_EQCENTER,
|
||||
AL_DISTORTION_MAX_EQCENTER);
|
||||
|
||||
al_effect_props_.Distortion.EQCenter = eq_center;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_efx_eq_bandwidth()
|
||||
{
|
||||
const auto eq_bandwidth = clamp(
|
||||
eax_.flEdge,
|
||||
AL_DISTORTION_MIN_EQBANDWIDTH,
|
||||
AL_DISTORTION_MAX_EQBANDWIDTH);
|
||||
|
||||
al_effect_props_.Distortion.EQBandwidth = eq_bandwidth;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_edge();
|
||||
set_efx_gain();
|
||||
set_efx_lowpass_cutoff();
|
||||
set_efx_eq_center();
|
||||
set_efx_eq_bandwidth();
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE:
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxDistortionEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_EDGE:
|
||||
eax_call.set_value<EaxDistortionEffectException>(eax_.flEdge);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_GAIN:
|
||||
eax_call.set_value<EaxDistortionEffectException>(eax_.lGain);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_LOWPASSCUTOFF:
|
||||
eax_call.set_value<EaxDistortionEffectException>(eax_.flLowPassCutOff);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_EQCENTER:
|
||||
eax_call.set_value<EaxDistortionEffectException>(eax_.flEQCenter);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_EQBANDWIDTH:
|
||||
eax_call.set_value<EaxDistortionEffectException>(eax_.flEQBandwidth);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxDistortionEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::validate_edge(
|
||||
float flEdge)
|
||||
{
|
||||
eax_validate_range<EaxDistortionEffectException>(
|
||||
"Edge",
|
||||
flEdge,
|
||||
EAXDISTORTION_MINEDGE,
|
||||
EAXDISTORTION_MAXEDGE);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::validate_gain(
|
||||
long lGain)
|
||||
{
|
||||
eax_validate_range<EaxDistortionEffectException>(
|
||||
"Gain",
|
||||
lGain,
|
||||
EAXDISTORTION_MINGAIN,
|
||||
EAXDISTORTION_MAXGAIN);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::validate_lowpass_cutoff(
|
||||
float flLowPassCutOff)
|
||||
{
|
||||
eax_validate_range<EaxDistortionEffectException>(
|
||||
"Low-pass Cut-off",
|
||||
flLowPassCutOff,
|
||||
EAXDISTORTION_MINLOWPASSCUTOFF,
|
||||
EAXDISTORTION_MAXLOWPASSCUTOFF);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::validate_eq_center(
|
||||
float flEQCenter)
|
||||
{
|
||||
eax_validate_range<EaxDistortionEffectException>(
|
||||
"EQ Center",
|
||||
flEQCenter,
|
||||
EAXDISTORTION_MINEQCENTER,
|
||||
EAXDISTORTION_MAXEQCENTER);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::validate_eq_bandwidth(
|
||||
float flEQBandwidth)
|
||||
{
|
||||
eax_validate_range<EaxDistortionEffectException>(
|
||||
"EQ Bandwidth",
|
||||
flEQBandwidth,
|
||||
EAXDISTORTION_MINEQBANDWIDTH,
|
||||
EAXDISTORTION_MAXEQBANDWIDTH);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::validate_all(
|
||||
const EAXDISTORTIONPROPERTIES& eax_all)
|
||||
{
|
||||
validate_edge(eax_all.flEdge);
|
||||
validate_gain(eax_all.lGain);
|
||||
validate_lowpass_cutoff(eax_all.flLowPassCutOff);
|
||||
validate_eq_center(eax_all.flEQCenter);
|
||||
validate_eq_bandwidth(eax_all.flEQBandwidth);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_edge(
|
||||
float flEdge)
|
||||
{
|
||||
eax_d_.flEdge = flEdge;
|
||||
eax_dirty_flags_.flEdge = (eax_.flEdge != eax_d_.flEdge);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_gain(
|
||||
long lGain)
|
||||
{
|
||||
eax_d_.lGain = lGain;
|
||||
eax_dirty_flags_.lGain = (eax_.lGain != eax_d_.lGain);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_low_pass_cutoff(
|
||||
float flLowPassCutOff)
|
||||
{
|
||||
eax_d_.flLowPassCutOff = flLowPassCutOff;
|
||||
eax_dirty_flags_.flLowPassCutOff = (eax_.flLowPassCutOff != eax_d_.flLowPassCutOff);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_eq_center(
|
||||
float flEQCenter)
|
||||
{
|
||||
eax_d_.flEQCenter = flEQCenter;
|
||||
eax_dirty_flags_.flEQCenter = (eax_.flEQCenter != eax_d_.flEQCenter);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_eq_bandwidth(
|
||||
float flEQBandwidth)
|
||||
{
|
||||
eax_d_.flEQBandwidth = flEQBandwidth;
|
||||
eax_dirty_flags_.flEQBandwidth = (eax_.flEQBandwidth != eax_d_.flEQBandwidth);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_all(
|
||||
const EAXDISTORTIONPROPERTIES& eax_all)
|
||||
{
|
||||
defer_edge(eax_all.flEdge);
|
||||
defer_gain(eax_all.lGain);
|
||||
defer_low_pass_cutoff(eax_all.flLowPassCutOff);
|
||||
defer_eq_center(eax_all.flEQCenter);
|
||||
defer_eq_bandwidth(eax_all.flEQBandwidth);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_edge(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& edge =
|
||||
eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEdge)>();
|
||||
|
||||
validate_edge(edge);
|
||||
defer_edge(edge);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_gain(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& gain =
|
||||
eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::lGain)>();
|
||||
|
||||
validate_gain(gain);
|
||||
defer_gain(gain);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_low_pass_cutoff(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& lowpass_cutoff =
|
||||
eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flLowPassCutOff)>();
|
||||
|
||||
validate_lowpass_cutoff(lowpass_cutoff);
|
||||
defer_low_pass_cutoff(lowpass_cutoff);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_eq_center(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& eq_center =
|
||||
eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQCenter)>();
|
||||
|
||||
validate_eq_center(eq_center);
|
||||
defer_eq_center(eq_center);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_eq_bandwidth(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& eq_bandwidth =
|
||||
eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQBandwidth)>();
|
||||
|
||||
validate_eq_bandwidth(eq_bandwidth);
|
||||
defer_eq_bandwidth(eq_bandwidth);
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxDistortionEffectException, const EAXDISTORTIONPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxDistortionEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxDistortionEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.flEdge)
|
||||
{
|
||||
set_efx_edge();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lGain)
|
||||
{
|
||||
set_efx_gain();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flLowPassCutOff)
|
||||
{
|
||||
set_efx_lowpass_cutoff();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flEQCenter)
|
||||
{
|
||||
set_efx_eq_center();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flEQBandwidth)
|
||||
{
|
||||
set_efx_eq_bandwidth();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxDistortionEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxDistortionEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE:
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_EDGE:
|
||||
defer_edge(eax_call);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_GAIN:
|
||||
defer_gain(eax_call);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_LOWPASSCUTOFF:
|
||||
defer_low_pass_cutoff(eax_call);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_EQCENTER:
|
||||
defer_eq_center(eax_call);
|
||||
break;
|
||||
|
||||
case EAXDISTORTION_EQBANDWIDTH:
|
||||
defer_eq_bandwidth(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxDistortionEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_distortion_effect()
|
||||
{
|
||||
return std::make_unique<EaxDistortionEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -109,3 +116,454 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Echo);
|
||||
|
||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxEchoEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxEchoEffectDirtyFlagsValue flDelay : 1;
|
||||
EaxEchoEffectDirtyFlagsValue flLRDelay : 1;
|
||||
EaxEchoEffectDirtyFlagsValue flDamping : 1;
|
||||
EaxEchoEffectDirtyFlagsValue flFeedback : 1;
|
||||
EaxEchoEffectDirtyFlagsValue flSpread : 1;
|
||||
}; // EaxEchoEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxEchoEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxEchoEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXECHOPROPERTIES eax_{};
|
||||
EAXECHOPROPERTIES eax_d_{};
|
||||
EaxEchoEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_delay();
|
||||
void set_efx_lr_delay();
|
||||
void set_efx_damping();
|
||||
void set_efx_feedback();
|
||||
void set_efx_spread();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_delay(float flDelay);
|
||||
void validate_lr_delay(float flLRDelay);
|
||||
void validate_damping(float flDamping);
|
||||
void validate_feedback(float flFeedback);
|
||||
void validate_spread(float flSpread);
|
||||
void validate_all(const EAXECHOPROPERTIES& all);
|
||||
|
||||
void defer_delay(float flDelay);
|
||||
void defer_lr_delay(float flLRDelay);
|
||||
void defer_damping(float flDamping);
|
||||
void defer_feedback(float flFeedback);
|
||||
void defer_spread(float flSpread);
|
||||
void defer_all(const EAXECHOPROPERTIES& all);
|
||||
|
||||
void defer_delay(const EaxEaxCall& eax_call);
|
||||
void defer_lr_delay(const EaxEaxCall& eax_call);
|
||||
void defer_damping(const EaxEaxCall& eax_call);
|
||||
void defer_feedback(const EaxEaxCall& eax_call);
|
||||
void defer_spread(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxEchoEffect
|
||||
|
||||
|
||||
class EaxEchoEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxEchoEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_ECHO_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxEchoEffectException
|
||||
|
||||
|
||||
EaxEchoEffect::EaxEchoEffect()
|
||||
: EaxEffect{AL_EFFECT_ECHO}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxEchoEffect::dispatch(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.flDelay = EAXECHO_DEFAULTDELAY;
|
||||
eax_.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||
eax_.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||
eax_.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||
eax_.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_efx_delay()
|
||||
{
|
||||
const auto delay = clamp(
|
||||
eax_.flDelay,
|
||||
AL_ECHO_MIN_DELAY,
|
||||
AL_ECHO_MAX_DELAY);
|
||||
|
||||
al_effect_props_.Echo.Delay = delay;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_efx_lr_delay()
|
||||
{
|
||||
const auto lr_delay = clamp(
|
||||
eax_.flLRDelay,
|
||||
AL_ECHO_MIN_LRDELAY,
|
||||
AL_ECHO_MAX_LRDELAY);
|
||||
|
||||
al_effect_props_.Echo.LRDelay = lr_delay;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_efx_damping()
|
||||
{
|
||||
const auto damping = clamp(
|
||||
eax_.flDamping,
|
||||
AL_ECHO_MIN_DAMPING,
|
||||
AL_ECHO_MAX_DAMPING);
|
||||
|
||||
al_effect_props_.Echo.Damping = damping;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_efx_feedback()
|
||||
{
|
||||
const auto feedback = clamp(
|
||||
eax_.flFeedback,
|
||||
AL_ECHO_MIN_FEEDBACK,
|
||||
AL_ECHO_MAX_FEEDBACK);
|
||||
|
||||
al_effect_props_.Echo.Feedback = feedback;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_efx_spread()
|
||||
{
|
||||
const auto spread = clamp(
|
||||
eax_.flSpread,
|
||||
AL_ECHO_MIN_SPREAD,
|
||||
AL_ECHO_MAX_SPREAD);
|
||||
|
||||
al_effect_props_.Echo.Spread = spread;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_delay();
|
||||
set_efx_lr_delay();
|
||||
set_efx_damping();
|
||||
set_efx_feedback();
|
||||
set_efx_spread();
|
||||
}
|
||||
|
||||
void EaxEchoEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE:
|
||||
break;
|
||||
|
||||
case EAXECHO_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxEchoEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXECHO_DELAY:
|
||||
eax_call.set_value<EaxEchoEffectException>(eax_.flDelay);
|
||||
break;
|
||||
|
||||
case EAXECHO_LRDELAY:
|
||||
eax_call.set_value<EaxEchoEffectException>(eax_.flLRDelay);
|
||||
break;
|
||||
|
||||
case EAXECHO_DAMPING:
|
||||
eax_call.set_value<EaxEchoEffectException>(eax_.flDamping);
|
||||
break;
|
||||
|
||||
case EAXECHO_FEEDBACK:
|
||||
eax_call.set_value<EaxEchoEffectException>(eax_.flFeedback);
|
||||
break;
|
||||
|
||||
case EAXECHO_SPREAD:
|
||||
eax_call.set_value<EaxEchoEffectException>(eax_.flSpread);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxEchoEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxEchoEffect::validate_delay(
|
||||
float flDelay)
|
||||
{
|
||||
eax_validate_range<EaxEchoEffectException>(
|
||||
"Delay",
|
||||
flDelay,
|
||||
EAXECHO_MINDELAY,
|
||||
EAXECHO_MAXDELAY);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::validate_lr_delay(
|
||||
float flLRDelay)
|
||||
{
|
||||
eax_validate_range<EaxEchoEffectException>(
|
||||
"LR Delay",
|
||||
flLRDelay,
|
||||
EAXECHO_MINLRDELAY,
|
||||
EAXECHO_MAXLRDELAY);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::validate_damping(
|
||||
float flDamping)
|
||||
{
|
||||
eax_validate_range<EaxEchoEffectException>(
|
||||
"Damping",
|
||||
flDamping,
|
||||
EAXECHO_MINDAMPING,
|
||||
EAXECHO_MAXDAMPING);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::validate_feedback(
|
||||
float flFeedback)
|
||||
{
|
||||
eax_validate_range<EaxEchoEffectException>(
|
||||
"Feedback",
|
||||
flFeedback,
|
||||
EAXECHO_MINFEEDBACK,
|
||||
EAXECHO_MAXFEEDBACK);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::validate_spread(
|
||||
float flSpread)
|
||||
{
|
||||
eax_validate_range<EaxEchoEffectException>(
|
||||
"Spread",
|
||||
flSpread,
|
||||
EAXECHO_MINSPREAD,
|
||||
EAXECHO_MAXSPREAD);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::validate_all(
|
||||
const EAXECHOPROPERTIES& all)
|
||||
{
|
||||
validate_delay(all.flDelay);
|
||||
validate_lr_delay(all.flLRDelay);
|
||||
validate_damping(all.flDamping);
|
||||
validate_feedback(all.flFeedback);
|
||||
validate_spread(all.flSpread);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_delay(
|
||||
float flDelay)
|
||||
{
|
||||
eax_d_.flDelay = flDelay;
|
||||
eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_lr_delay(
|
||||
float flLRDelay)
|
||||
{
|
||||
eax_d_.flLRDelay = flLRDelay;
|
||||
eax_dirty_flags_.flLRDelay = (eax_.flLRDelay != eax_d_.flLRDelay);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_damping(
|
||||
float flDamping)
|
||||
{
|
||||
eax_d_.flDamping = flDamping;
|
||||
eax_dirty_flags_.flDamping = (eax_.flDamping != eax_d_.flDamping);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_feedback(
|
||||
float flFeedback)
|
||||
{
|
||||
eax_d_.flFeedback = flFeedback;
|
||||
eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_spread(
|
||||
float flSpread)
|
||||
{
|
||||
eax_d_.flSpread = flSpread;
|
||||
eax_dirty_flags_.flSpread = (eax_.flSpread != eax_d_.flSpread);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_all(
|
||||
const EAXECHOPROPERTIES& all)
|
||||
{
|
||||
defer_delay(all.flDelay);
|
||||
defer_lr_delay(all.flLRDelay);
|
||||
defer_damping(all.flDamping);
|
||||
defer_feedback(all.flFeedback);
|
||||
defer_spread(all.flSpread);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_delay(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& delay =
|
||||
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDelay)>();
|
||||
|
||||
validate_delay(delay);
|
||||
defer_delay(delay);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_lr_delay(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& lr_delay =
|
||||
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flLRDelay)>();
|
||||
|
||||
validate_lr_delay(lr_delay);
|
||||
defer_lr_delay(lr_delay);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_damping(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& damping =
|
||||
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDamping)>();
|
||||
|
||||
validate_damping(damping);
|
||||
defer_damping(damping);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_feedback(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& feedback =
|
||||
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flFeedback)>();
|
||||
|
||||
validate_feedback(feedback);
|
||||
defer_feedback(feedback);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_spread(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& spread =
|
||||
eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flSpread)>();
|
||||
|
||||
validate_spread(spread);
|
||||
defer_spread(spread);
|
||||
}
|
||||
|
||||
void EaxEchoEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxEchoEffectException, const EAXECHOPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxEchoEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxEchoEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.flDelay)
|
||||
{
|
||||
set_efx_delay();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flLRDelay)
|
||||
{
|
||||
set_efx_lr_delay();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flDamping)
|
||||
{
|
||||
set_efx_damping();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flFeedback)
|
||||
{
|
||||
set_efx_feedback();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flSpread)
|
||||
{
|
||||
set_efx_spread();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxEchoEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxEchoEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE:
|
||||
break;
|
||||
|
||||
case EAXECHO_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXECHO_DELAY:
|
||||
defer_delay(eax_call);
|
||||
break;
|
||||
|
||||
case EAXECHO_LRDELAY:
|
||||
defer_lr_delay(eax_call);
|
||||
break;
|
||||
|
||||
case EAXECHO_DAMPING:
|
||||
defer_damping(eax_call);
|
||||
break;
|
||||
|
||||
case EAXECHO_FEEDBACK:
|
||||
defer_feedback(eax_call);
|
||||
break;
|
||||
|
||||
case EAXECHO_SPREAD:
|
||||
defer_spread(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxEchoEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_echo_effect()
|
||||
{
|
||||
return std::make_unique<EaxEchoEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
66
Engine/lib/openal-soft/al/effects/effects.cpp
Normal file
66
Engine/lib/openal-soft/al/effects/effects.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
#include "effects.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "AL/efx.h"
|
||||
|
||||
|
||||
EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type)
|
||||
{
|
||||
#define EAX_PREFIX "[EAX_MAKE_EAX_EFFECT] "
|
||||
|
||||
switch (al_effect_type)
|
||||
{
|
||||
case AL_EFFECT_NULL:
|
||||
return eax_create_eax_null_effect();
|
||||
|
||||
case AL_EFFECT_CHORUS:
|
||||
return eax_create_eax_chorus_effect();
|
||||
|
||||
case AL_EFFECT_DISTORTION:
|
||||
return eax_create_eax_distortion_effect();
|
||||
|
||||
case AL_EFFECT_ECHO:
|
||||
return eax_create_eax_echo_effect();
|
||||
|
||||
case AL_EFFECT_FLANGER:
|
||||
return eax_create_eax_flanger_effect();
|
||||
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER:
|
||||
return eax_create_eax_frequency_shifter_effect();
|
||||
|
||||
case AL_EFFECT_VOCAL_MORPHER:
|
||||
return eax_create_eax_vocal_morpher_effect();
|
||||
|
||||
case AL_EFFECT_PITCH_SHIFTER:
|
||||
return eax_create_eax_pitch_shifter_effect();
|
||||
|
||||
case AL_EFFECT_RING_MODULATOR:
|
||||
return eax_create_eax_ring_modulator_effect();
|
||||
|
||||
case AL_EFFECT_AUTOWAH:
|
||||
return eax_create_eax_auto_wah_effect();
|
||||
|
||||
case AL_EFFECT_COMPRESSOR:
|
||||
return eax_create_eax_compressor_effect();
|
||||
|
||||
case AL_EFFECT_EQUALIZER:
|
||||
return eax_create_eax_equalizer_effect();
|
||||
|
||||
case AL_EFFECT_EAXREVERB:
|
||||
return eax_create_eax_reverb_effect();
|
||||
|
||||
default:
|
||||
assert(false && "Unsupported AL effect type.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#undef EAX_PREFIX
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
#include "core/except.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax_effect.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
union EffectProps;
|
||||
|
||||
|
||||
|
|
@ -12,7 +16,11 @@ class effect_exception final : public al::base_exception {
|
|||
ALenum mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
effect_exception(ALenum code, const char *msg, ...);
|
||||
|
||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||
|
|
@ -76,4 +84,9 @@ extern const EffectVtable VmorpherEffectVtable;
|
|||
extern const EffectVtable DedicatedEffectVtable;
|
||||
extern const EffectVtable ConvolutionEffectVtable;
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
EaxEffectUPtr eax_create_eax_effect(ALenum al_effect_type);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
#endif /* AL_EFFECTS_EFFECTS_H */
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -167,3 +174,748 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Equalizer);
|
||||
|
||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxEqualizerEffectDirtyFlagsValue = std::uint_least16_t;
|
||||
|
||||
struct EaxEqualizerEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxEqualizerEffectDirtyFlagsValue lLowGain : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue flLowCutOff : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue lMid1Gain : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue flMid1Center : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue flMid1Width : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue lMid2Gain : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue flMid2Center : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue flMid2Width : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue lHighGain : 1;
|
||||
EaxEqualizerEffectDirtyFlagsValue flHighCutOff : 1;
|
||||
}; // EaxEqualizerEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxEqualizerEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxEqualizerEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXEQUALIZERPROPERTIES eax_{};
|
||||
EAXEQUALIZERPROPERTIES eax_d_{};
|
||||
EaxEqualizerEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_low_gain();
|
||||
void set_efx_low_cutoff();
|
||||
void set_efx_mid1_gain();
|
||||
void set_efx_mid1_center();
|
||||
void set_efx_mid1_width();
|
||||
void set_efx_mid2_gain();
|
||||
void set_efx_mid2_center();
|
||||
void set_efx_mid2_width();
|
||||
void set_efx_high_gain();
|
||||
void set_efx_high_cutoff();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_low_gain(long lLowGain);
|
||||
void validate_low_cutoff(float flLowCutOff);
|
||||
void validate_mid1_gain(long lMid1Gain);
|
||||
void validate_mid1_center(float flMid1Center);
|
||||
void validate_mid1_width(float flMid1Width);
|
||||
void validate_mid2_gain(long lMid2Gain);
|
||||
void validate_mid2_center(float flMid2Center);
|
||||
void validate_mid2_width(float flMid2Width);
|
||||
void validate_high_gain(long lHighGain);
|
||||
void validate_high_cutoff(float flHighCutOff);
|
||||
void validate_all(const EAXEQUALIZERPROPERTIES& all);
|
||||
|
||||
void defer_low_gain(long lLowGain);
|
||||
void defer_low_cutoff(float flLowCutOff);
|
||||
void defer_mid1_gain(long lMid1Gain);
|
||||
void defer_mid1_center(float flMid1Center);
|
||||
void defer_mid1_width(float flMid1Width);
|
||||
void defer_mid2_gain(long lMid2Gain);
|
||||
void defer_mid2_center(float flMid2Center);
|
||||
void defer_mid2_width(float flMid2Width);
|
||||
void defer_high_gain(long lHighGain);
|
||||
void defer_high_cutoff(float flHighCutOff);
|
||||
void defer_all(const EAXEQUALIZERPROPERTIES& all);
|
||||
|
||||
void defer_low_gain(const EaxEaxCall& eax_call);
|
||||
void defer_low_cutoff(const EaxEaxCall& eax_call);
|
||||
void defer_mid1_gain(const EaxEaxCall& eax_call);
|
||||
void defer_mid1_center(const EaxEaxCall& eax_call);
|
||||
void defer_mid1_width(const EaxEaxCall& eax_call);
|
||||
void defer_mid2_gain(const EaxEaxCall& eax_call);
|
||||
void defer_mid2_center(const EaxEaxCall& eax_call);
|
||||
void defer_mid2_width(const EaxEaxCall& eax_call);
|
||||
void defer_high_gain(const EaxEaxCall& eax_call);
|
||||
void defer_high_cutoff(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxEqualizerEffect
|
||||
|
||||
|
||||
class EaxEqualizerEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxEqualizerEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_EQUALIZER_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxEqualizerEffectException
|
||||
|
||||
|
||||
EaxEqualizerEffect::EaxEqualizerEffect()
|
||||
: EaxEffect{AL_EFFECT_EQUALIZER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
||||
eax_.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
||||
eax_.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
||||
eax_.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
||||
eax_.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
||||
eax_.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
||||
eax_.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
||||
eax_.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
||||
eax_.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
||||
eax_.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_low_gain()
|
||||
{
|
||||
const auto low_gain = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lLowGain)),
|
||||
AL_EQUALIZER_MIN_LOW_GAIN,
|
||||
AL_EQUALIZER_MAX_LOW_GAIN);
|
||||
|
||||
al_effect_props_.Equalizer.LowGain = low_gain;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_low_cutoff()
|
||||
{
|
||||
const auto low_cutoff = clamp(
|
||||
eax_.flLowCutOff,
|
||||
AL_EQUALIZER_MIN_LOW_CUTOFF,
|
||||
AL_EQUALIZER_MAX_LOW_CUTOFF);
|
||||
|
||||
al_effect_props_.Equalizer.LowCutoff = low_cutoff;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_mid1_gain()
|
||||
{
|
||||
const auto mid1_gain = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lMid1Gain)),
|
||||
AL_EQUALIZER_MIN_MID1_GAIN,
|
||||
AL_EQUALIZER_MAX_MID1_GAIN);
|
||||
|
||||
al_effect_props_.Equalizer.Mid1Gain = mid1_gain;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_mid1_center()
|
||||
{
|
||||
const auto mid1_center = clamp(
|
||||
eax_.flMid1Center,
|
||||
AL_EQUALIZER_MIN_MID1_CENTER,
|
||||
AL_EQUALIZER_MAX_MID1_CENTER);
|
||||
|
||||
al_effect_props_.Equalizer.Mid1Center = mid1_center;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_mid1_width()
|
||||
{
|
||||
const auto mid1_width = clamp(
|
||||
eax_.flMid1Width,
|
||||
AL_EQUALIZER_MIN_MID1_WIDTH,
|
||||
AL_EQUALIZER_MAX_MID1_WIDTH);
|
||||
|
||||
al_effect_props_.Equalizer.Mid1Width = mid1_width;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_mid2_gain()
|
||||
{
|
||||
const auto mid2_gain = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lMid2Gain)),
|
||||
AL_EQUALIZER_MIN_MID2_GAIN,
|
||||
AL_EQUALIZER_MAX_MID2_GAIN);
|
||||
|
||||
al_effect_props_.Equalizer.Mid2Gain = mid2_gain;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_mid2_center()
|
||||
{
|
||||
const auto mid2_center = clamp(
|
||||
eax_.flMid2Center,
|
||||
AL_EQUALIZER_MIN_MID2_CENTER,
|
||||
AL_EQUALIZER_MAX_MID2_CENTER);
|
||||
|
||||
al_effect_props_.Equalizer.Mid2Center = mid2_center;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_mid2_width()
|
||||
{
|
||||
const auto mid2_width = clamp(
|
||||
eax_.flMid2Width,
|
||||
AL_EQUALIZER_MIN_MID2_WIDTH,
|
||||
AL_EQUALIZER_MAX_MID2_WIDTH);
|
||||
|
||||
al_effect_props_.Equalizer.Mid2Width = mid2_width;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_high_gain()
|
||||
{
|
||||
const auto high_gain = clamp(
|
||||
level_mb_to_gain(static_cast<float>(eax_.lHighGain)),
|
||||
AL_EQUALIZER_MIN_HIGH_GAIN,
|
||||
AL_EQUALIZER_MAX_HIGH_GAIN);
|
||||
|
||||
al_effect_props_.Equalizer.HighGain = high_gain;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_high_cutoff()
|
||||
{
|
||||
const auto high_cutoff = clamp(
|
||||
eax_.flHighCutOff,
|
||||
AL_EQUALIZER_MIN_HIGH_CUTOFF,
|
||||
AL_EQUALIZER_MAX_HIGH_CUTOFF);
|
||||
|
||||
al_effect_props_.Equalizer.HighCutoff = high_cutoff;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_low_gain();
|
||||
set_efx_low_cutoff();
|
||||
set_efx_mid1_gain();
|
||||
set_efx_mid1_center();
|
||||
set_efx_mid1_width();
|
||||
set_efx_mid2_gain();
|
||||
set_efx_mid2_center();
|
||||
set_efx_mid2_width();
|
||||
set_efx_high_gain();
|
||||
set_efx_high_cutoff();
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXEQUALIZER_NONE:
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_LOWGAIN:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.lLowGain);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_LOWCUTOFF:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.flLowCutOff);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID1GAIN:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.lMid1Gain);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID1CENTER:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid1Center);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID1WIDTH:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid1Width);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID2GAIN:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.lMid2Gain);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID2CENTER:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid2Center);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID2WIDTH:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.flMid2Width);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_HIGHGAIN:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.lHighGain);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_HIGHCUTOFF:
|
||||
eax_call.set_value<EaxEqualizerEffectException>(eax_.flHighCutOff);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxEqualizerEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_low_gain(
|
||||
long lLowGain)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Low Gain",
|
||||
lLowGain,
|
||||
EAXEQUALIZER_MINLOWGAIN,
|
||||
EAXEQUALIZER_MAXLOWGAIN);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_low_cutoff(
|
||||
float flLowCutOff)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Low Cutoff",
|
||||
flLowCutOff,
|
||||
EAXEQUALIZER_MINLOWCUTOFF,
|
||||
EAXEQUALIZER_MAXLOWCUTOFF);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_mid1_gain(
|
||||
long lMid1Gain)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Mid1 Gain",
|
||||
lMid1Gain,
|
||||
EAXEQUALIZER_MINMID1GAIN,
|
||||
EAXEQUALIZER_MAXMID1GAIN);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_mid1_center(
|
||||
float flMid1Center)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Mid1 Center",
|
||||
flMid1Center,
|
||||
EAXEQUALIZER_MINMID1CENTER,
|
||||
EAXEQUALIZER_MAXMID1CENTER);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_mid1_width(
|
||||
float flMid1Width)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Mid1 Width",
|
||||
flMid1Width,
|
||||
EAXEQUALIZER_MINMID1WIDTH,
|
||||
EAXEQUALIZER_MAXMID1WIDTH);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_mid2_gain(
|
||||
long lMid2Gain)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Mid2 Gain",
|
||||
lMid2Gain,
|
||||
EAXEQUALIZER_MINMID2GAIN,
|
||||
EAXEQUALIZER_MAXMID2GAIN);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_mid2_center(
|
||||
float flMid2Center)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Mid2 Center",
|
||||
flMid2Center,
|
||||
EAXEQUALIZER_MINMID2CENTER,
|
||||
EAXEQUALIZER_MAXMID2CENTER);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_mid2_width(
|
||||
float flMid2Width)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"Mid2 Width",
|
||||
flMid2Width,
|
||||
EAXEQUALIZER_MINMID2WIDTH,
|
||||
EAXEQUALIZER_MAXMID2WIDTH);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_high_gain(
|
||||
long lHighGain)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"High Gain",
|
||||
lHighGain,
|
||||
EAXEQUALIZER_MINHIGHGAIN,
|
||||
EAXEQUALIZER_MAXHIGHGAIN);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_high_cutoff(
|
||||
float flHighCutOff)
|
||||
{
|
||||
eax_validate_range<EaxEqualizerEffectException>(
|
||||
"High Cutoff",
|
||||
flHighCutOff,
|
||||
EAXEQUALIZER_MINHIGHCUTOFF,
|
||||
EAXEQUALIZER_MAXHIGHCUTOFF);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::validate_all(
|
||||
const EAXEQUALIZERPROPERTIES& all)
|
||||
{
|
||||
validate_low_gain(all.lLowGain);
|
||||
validate_low_cutoff(all.flLowCutOff);
|
||||
validate_mid1_gain(all.lMid1Gain);
|
||||
validate_mid1_center(all.flMid1Center);
|
||||
validate_mid1_width(all.flMid1Width);
|
||||
validate_mid2_gain(all.lMid2Gain);
|
||||
validate_mid2_center(all.flMid2Center);
|
||||
validate_mid2_width(all.flMid2Width);
|
||||
validate_high_gain(all.lHighGain);
|
||||
validate_high_cutoff(all.flHighCutOff);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_low_gain(
|
||||
long lLowGain)
|
||||
{
|
||||
eax_d_.lLowGain = lLowGain;
|
||||
eax_dirty_flags_.lLowGain = (eax_.lLowGain != eax_d_.lLowGain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_low_cutoff(
|
||||
float flLowCutOff)
|
||||
{
|
||||
eax_d_.flLowCutOff = flLowCutOff;
|
||||
eax_dirty_flags_.flLowCutOff = (eax_.flLowCutOff != eax_d_.flLowCutOff);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid1_gain(
|
||||
long lMid1Gain)
|
||||
{
|
||||
eax_d_.lMid1Gain = lMid1Gain;
|
||||
eax_dirty_flags_.lMid1Gain = (eax_.lMid1Gain != eax_d_.lMid1Gain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid1_center(
|
||||
float flMid1Center)
|
||||
{
|
||||
eax_d_.flMid1Center = flMid1Center;
|
||||
eax_dirty_flags_.flMid1Center = (eax_.flMid1Center != eax_d_.flMid1Center);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid1_width(
|
||||
float flMid1Width)
|
||||
{
|
||||
eax_d_.flMid1Width = flMid1Width;
|
||||
eax_dirty_flags_.flMid1Width = (eax_.flMid1Width != eax_d_.flMid1Width);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid2_gain(
|
||||
long lMid2Gain)
|
||||
{
|
||||
eax_d_.lMid2Gain = lMid2Gain;
|
||||
eax_dirty_flags_.lMid2Gain = (eax_.lMid2Gain != eax_d_.lMid2Gain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid2_center(
|
||||
float flMid2Center)
|
||||
{
|
||||
eax_d_.flMid2Center = flMid2Center;
|
||||
eax_dirty_flags_.flMid2Center = (eax_.flMid2Center != eax_d_.flMid2Center);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid2_width(
|
||||
float flMid2Width)
|
||||
{
|
||||
eax_d_.flMid2Width = flMid2Width;
|
||||
eax_dirty_flags_.flMid2Width = (eax_.flMid2Width != eax_d_.flMid2Width);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_high_gain(
|
||||
long lHighGain)
|
||||
{
|
||||
eax_d_.lHighGain = lHighGain;
|
||||
eax_dirty_flags_.lHighGain = (eax_.lHighGain != eax_d_.lHighGain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_high_cutoff(
|
||||
float flHighCutOff)
|
||||
{
|
||||
eax_d_.flHighCutOff = flHighCutOff;
|
||||
eax_dirty_flags_.flHighCutOff = (eax_.flHighCutOff != eax_d_.flHighCutOff);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_all(
|
||||
const EAXEQUALIZERPROPERTIES& all)
|
||||
{
|
||||
defer_low_gain(all.lLowGain);
|
||||
defer_low_cutoff(all.flLowCutOff);
|
||||
defer_mid1_gain(all.lMid1Gain);
|
||||
defer_mid1_center(all.flMid1Center);
|
||||
defer_mid1_width(all.flMid1Width);
|
||||
defer_mid2_gain(all.lMid2Gain);
|
||||
defer_mid2_center(all.flMid2Center);
|
||||
defer_mid2_width(all.flMid2Width);
|
||||
defer_high_gain(all.lHighGain);
|
||||
defer_high_cutoff(all.flHighCutOff);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_low_gain(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& low_gain =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lLowGain)>();
|
||||
|
||||
validate_low_gain(low_gain);
|
||||
defer_low_gain(low_gain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_low_cutoff(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& low_cutoff =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flLowCutOff)>();
|
||||
|
||||
validate_low_cutoff(low_cutoff);
|
||||
defer_low_cutoff(low_cutoff);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid1_gain(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& mid1_gain =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lMid1Gain)>();
|
||||
|
||||
validate_mid1_gain(mid1_gain);
|
||||
defer_mid1_gain(mid1_gain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid1_center(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& mid1_center =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid1Center)>();
|
||||
|
||||
validate_mid1_center(mid1_center);
|
||||
defer_mid1_center(mid1_center);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid1_width(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& mid1_width =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid1Width)>();
|
||||
|
||||
validate_mid1_width(mid1_width);
|
||||
defer_mid1_width(mid1_width);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid2_gain(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& mid2_gain =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lMid2Gain)>();
|
||||
|
||||
validate_mid2_gain(mid2_gain);
|
||||
defer_mid2_gain(mid2_gain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid2_center(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& mid2_center =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid2Center)>();
|
||||
|
||||
validate_mid2_center(mid2_center);
|
||||
defer_mid2_center(mid2_center);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_mid2_width(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& mid2_width =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flMid2Width)>();
|
||||
|
||||
validate_mid2_width(mid2_width);
|
||||
defer_mid2_width(mid2_width);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_high_gain(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& high_gain =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::lHighGain)>();
|
||||
|
||||
validate_high_gain(high_gain);
|
||||
defer_high_gain(high_gain);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_high_cutoff(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& high_cutoff =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const decltype(EAXEQUALIZERPROPERTIES::flHighCutOff)>();
|
||||
|
||||
validate_high_cutoff(high_cutoff);
|
||||
defer_high_cutoff(high_cutoff);
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxEqualizerEffectException, const EAXEQUALIZERPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxEqualizerEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxEqualizerEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.lLowGain)
|
||||
{
|
||||
set_efx_low_gain();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flLowCutOff)
|
||||
{
|
||||
set_efx_low_cutoff();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lMid1Gain)
|
||||
{
|
||||
set_efx_mid1_gain();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flMid1Center)
|
||||
{
|
||||
set_efx_mid1_center();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flMid1Width)
|
||||
{
|
||||
set_efx_mid1_width();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lMid2Gain)
|
||||
{
|
||||
set_efx_mid2_gain();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flMid2Center)
|
||||
{
|
||||
set_efx_mid2_center();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flMid2Width)
|
||||
{
|
||||
set_efx_mid2_width();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lHighGain)
|
||||
{
|
||||
set_efx_high_gain();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flHighCutOff)
|
||||
{
|
||||
set_efx_high_cutoff();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxEqualizerEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxEqualizerEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXEQUALIZER_NONE:
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_LOWGAIN:
|
||||
defer_low_gain(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_LOWCUTOFF:
|
||||
defer_low_cutoff(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID1GAIN:
|
||||
defer_mid1_gain(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID1CENTER:
|
||||
defer_mid1_center(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID1WIDTH:
|
||||
defer_mid1_width(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID2GAIN:
|
||||
defer_mid2_gain(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID2CENTER:
|
||||
defer_mid2_center(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_MID2WIDTH:
|
||||
defer_mid2_width(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_HIGHGAIN:
|
||||
defer_high_gain(eax_call);
|
||||
break;
|
||||
|
||||
case EAXEQUALIZER_HIGHCUTOFF:
|
||||
defer_high_cutoff(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxEqualizerEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_equalizer_effect()
|
||||
{
|
||||
return std::make_unique<EaxEqualizerEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -1,12 +1,23 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -126,3 +137,343 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Fshifter);
|
||||
|
||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxFrequencyShifterEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxFrequencyShifterEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxFrequencyShifterEffectDirtyFlagsValue flFrequency : 1;
|
||||
EaxFrequencyShifterEffectDirtyFlagsValue ulLeftDirection : 1;
|
||||
EaxFrequencyShifterEffectDirtyFlagsValue ulRightDirection : 1;
|
||||
}; // EaxFrequencyShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxFrequencyShifterEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxFrequencyShifterEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXFREQUENCYSHIFTERPROPERTIES eax_{};
|
||||
EAXFREQUENCYSHIFTERPROPERTIES eax_d_{};
|
||||
EaxFrequencyShifterEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_frequency();
|
||||
void set_efx_left_direction();
|
||||
void set_efx_right_direction();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_frequency(float flFrequency);
|
||||
void validate_left_direction(unsigned long ulLeftDirection);
|
||||
void validate_right_direction(unsigned long ulRightDirection);
|
||||
void validate_all(const EAXFREQUENCYSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_frequency(float flFrequency);
|
||||
void defer_left_direction(unsigned long ulLeftDirection);
|
||||
void defer_right_direction(unsigned long ulRightDirection);
|
||||
void defer_all(const EAXFREQUENCYSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_frequency(const EaxEaxCall& eax_call);
|
||||
void defer_left_direction(const EaxEaxCall& eax_call);
|
||||
void defer_right_direction(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxFrequencyShifterEffect
|
||||
|
||||
|
||||
class EaxFrequencyShifterEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxFrequencyShifterEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_FREQUENCY_SHIFTER_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxFrequencyShifterEffectException
|
||||
|
||||
|
||||
EaxFrequencyShifterEffect::EaxFrequencyShifterEffect()
|
||||
: EaxEffect{AL_EFFECT_FREQUENCY_SHIFTER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
eax_.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
eax_.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_frequency()
|
||||
{
|
||||
const auto frequency = clamp(
|
||||
eax_.flFrequency,
|
||||
AL_FREQUENCY_SHIFTER_MIN_FREQUENCY,
|
||||
AL_FREQUENCY_SHIFTER_MAX_FREQUENCY);
|
||||
|
||||
al_effect_props_.Fshifter.Frequency = frequency;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_left_direction()
|
||||
{
|
||||
const auto left_direction = clamp(
|
||||
static_cast<ALint>(eax_.ulLeftDirection),
|
||||
AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION,
|
||||
AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION);
|
||||
|
||||
const auto efx_left_direction = DirectionFromEmum(left_direction);
|
||||
assert(efx_left_direction.has_value());
|
||||
al_effect_props_.Fshifter.LeftDirection = *efx_left_direction;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_right_direction()
|
||||
{
|
||||
const auto right_direction = clamp(
|
||||
static_cast<ALint>(eax_.ulRightDirection),
|
||||
AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION,
|
||||
AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION);
|
||||
|
||||
const auto efx_right_direction = DirectionFromEmum(right_direction);
|
||||
assert(efx_right_direction.has_value());
|
||||
al_effect_props_.Fshifter.RightDirection = *efx_right_direction;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_frequency();
|
||||
set_efx_left_direction();
|
||||
set_efx_right_direction();
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.flFrequency);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.ulLeftDirection);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION:
|
||||
eax_call.set_value<EaxFrequencyShifterEffectException>(eax_.ulRightDirection);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxFrequencyShifterEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_frequency(
|
||||
float flFrequency)
|
||||
{
|
||||
eax_validate_range<EaxFrequencyShifterEffectException>(
|
||||
"Frequency",
|
||||
flFrequency,
|
||||
EAXFREQUENCYSHIFTER_MINFREQUENCY,
|
||||
EAXFREQUENCYSHIFTER_MAXFREQUENCY);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_left_direction(
|
||||
unsigned long ulLeftDirection)
|
||||
{
|
||||
eax_validate_range<EaxFrequencyShifterEffectException>(
|
||||
"Left Direction",
|
||||
ulLeftDirection,
|
||||
EAXFREQUENCYSHIFTER_MINLEFTDIRECTION,
|
||||
EAXFREQUENCYSHIFTER_MAXLEFTDIRECTION);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_right_direction(
|
||||
unsigned long ulRightDirection)
|
||||
{
|
||||
eax_validate_range<EaxFrequencyShifterEffectException>(
|
||||
"Right Direction",
|
||||
ulRightDirection,
|
||||
EAXFREQUENCYSHIFTER_MINRIGHTDIRECTION,
|
||||
EAXFREQUENCYSHIFTER_MAXRIGHTDIRECTION);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::validate_all(
|
||||
const EAXFREQUENCYSHIFTERPROPERTIES& all)
|
||||
{
|
||||
validate_frequency(all.flFrequency);
|
||||
validate_left_direction(all.ulLeftDirection);
|
||||
validate_right_direction(all.ulRightDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_frequency(
|
||||
float flFrequency)
|
||||
{
|
||||
eax_d_.flFrequency = flFrequency;
|
||||
eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_left_direction(
|
||||
unsigned long ulLeftDirection)
|
||||
{
|
||||
eax_d_.ulLeftDirection = ulLeftDirection;
|
||||
eax_dirty_flags_.ulLeftDirection = (eax_.ulLeftDirection != eax_d_.ulLeftDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_right_direction(
|
||||
unsigned long ulRightDirection)
|
||||
{
|
||||
eax_d_.ulRightDirection = ulRightDirection;
|
||||
eax_dirty_flags_.ulRightDirection = (eax_.ulRightDirection != eax_d_.ulRightDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_all(
|
||||
const EAXFREQUENCYSHIFTERPROPERTIES& all)
|
||||
{
|
||||
defer_frequency(all.flFrequency);
|
||||
defer_left_direction(all.ulLeftDirection);
|
||||
defer_right_direction(all.ulRightDirection);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_frequency(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& frequency =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::flFrequency)>();
|
||||
|
||||
validate_frequency(frequency);
|
||||
defer_frequency(frequency);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_left_direction(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& left_direction =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulLeftDirection)>();
|
||||
|
||||
validate_left_direction(left_direction);
|
||||
defer_left_direction(left_direction);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_right_direction(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& right_direction =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const decltype(EAXFREQUENCYSHIFTERPROPERTIES::ulRightDirection)>();
|
||||
|
||||
validate_right_direction(right_direction);
|
||||
defer_right_direction(right_direction);
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<
|
||||
EaxFrequencyShifterEffectException, const EAXFREQUENCYSHIFTERPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxFrequencyShifterEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxFrequencyShifterEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.flFrequency)
|
||||
{
|
||||
set_efx_frequency();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulLeftDirection)
|
||||
{
|
||||
set_efx_left_direction();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulRightDirection)
|
||||
{
|
||||
set_efx_right_direction();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxFrequencyShifterEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxFrequencyShifterEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY:
|
||||
defer_frequency(eax_call);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION:
|
||||
defer_left_direction(eax_call);
|
||||
break;
|
||||
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION:
|
||||
defer_right_direction(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxFrequencyShifterEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_frequency_shifter_effect()
|
||||
{
|
||||
return std::make_unique<EaxFrequencyShifterEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -1,12 +1,23 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -132,3 +143,340 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Modulator);
|
||||
|
||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxRingModulatorEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1;
|
||||
EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1;
|
||||
EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1;
|
||||
}; // EaxPitchShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxRingModulatorEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxRingModulatorEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXRINGMODULATORPROPERTIES eax_{};
|
||||
EAXRINGMODULATORPROPERTIES eax_d_{};
|
||||
EaxRingModulatorEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_frequency();
|
||||
void set_efx_high_pass_cutoff();
|
||||
void set_efx_waveform();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_frequency(float flFrequency);
|
||||
void validate_high_pass_cutoff(float flHighPassCutOff);
|
||||
void validate_waveform(unsigned long ulWaveform);
|
||||
void validate_all(const EAXRINGMODULATORPROPERTIES& all);
|
||||
|
||||
void defer_frequency(float flFrequency);
|
||||
void defer_high_pass_cutoff(float flHighPassCutOff);
|
||||
void defer_waveform(unsigned long ulWaveform);
|
||||
void defer_all(const EAXRINGMODULATORPROPERTIES& all);
|
||||
|
||||
void defer_frequency(const EaxEaxCall& eax_call);
|
||||
void defer_high_pass_cutoff(const EaxEaxCall& eax_call);
|
||||
void defer_waveform(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxRingModulatorEffect
|
||||
|
||||
|
||||
class EaxRingModulatorEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxRingModulatorEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_RING_MODULATOR_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxRingModulatorEffectException
|
||||
|
||||
|
||||
EaxRingModulatorEffect::EaxRingModulatorEffect()
|
||||
: EaxEffect{AL_EFFECT_RING_MODULATOR}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||
eax_.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||
eax_.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set_efx_frequency()
|
||||
{
|
||||
const auto frequency = clamp(
|
||||
eax_.flFrequency,
|
||||
AL_RING_MODULATOR_MIN_FREQUENCY,
|
||||
AL_RING_MODULATOR_MAX_FREQUENCY);
|
||||
|
||||
al_effect_props_.Modulator.Frequency = frequency;
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set_efx_high_pass_cutoff()
|
||||
{
|
||||
const auto high_pass_cutoff = clamp(
|
||||
eax_.flHighPassCutOff,
|
||||
AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF,
|
||||
AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF);
|
||||
|
||||
al_effect_props_.Modulator.HighPassCutoff = high_pass_cutoff;
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set_efx_waveform()
|
||||
{
|
||||
const auto waveform = clamp(
|
||||
static_cast<ALint>(eax_.ulWaveform),
|
||||
AL_RING_MODULATOR_MIN_WAVEFORM,
|
||||
AL_RING_MODULATOR_MAX_WAVEFORM);
|
||||
|
||||
const auto efx_waveform = WaveformFromEmum(waveform);
|
||||
assert(efx_waveform.has_value());
|
||||
al_effect_props_.Modulator.Waveform = *efx_waveform;
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_frequency();
|
||||
set_efx_high_pass_cutoff();
|
||||
set_efx_waveform();
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE:
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxRingModulatorEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_FREQUENCY:
|
||||
eax_call.set_value<EaxRingModulatorEffectException>(eax_.flFrequency);
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF:
|
||||
eax_call.set_value<EaxRingModulatorEffectException>(eax_.flHighPassCutOff);
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_WAVEFORM:
|
||||
eax_call.set_value<EaxRingModulatorEffectException>(eax_.ulWaveform);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxRingModulatorEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::validate_frequency(
|
||||
float flFrequency)
|
||||
{
|
||||
eax_validate_range<EaxRingModulatorEffectException>(
|
||||
"Frequency",
|
||||
flFrequency,
|
||||
EAXRINGMODULATOR_MINFREQUENCY,
|
||||
EAXRINGMODULATOR_MAXFREQUENCY);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::validate_high_pass_cutoff(
|
||||
float flHighPassCutOff)
|
||||
{
|
||||
eax_validate_range<EaxRingModulatorEffectException>(
|
||||
"High-Pass Cutoff",
|
||||
flHighPassCutOff,
|
||||
EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
|
||||
EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::validate_waveform(
|
||||
unsigned long ulWaveform)
|
||||
{
|
||||
eax_validate_range<EaxRingModulatorEffectException>(
|
||||
"Waveform",
|
||||
ulWaveform,
|
||||
EAXRINGMODULATOR_MINWAVEFORM,
|
||||
EAXRINGMODULATOR_MAXWAVEFORM);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::validate_all(
|
||||
const EAXRINGMODULATORPROPERTIES& all)
|
||||
{
|
||||
validate_frequency(all.flFrequency);
|
||||
validate_high_pass_cutoff(all.flHighPassCutOff);
|
||||
validate_waveform(all.ulWaveform);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_frequency(
|
||||
float flFrequency)
|
||||
{
|
||||
eax_d_.flFrequency = flFrequency;
|
||||
eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_high_pass_cutoff(
|
||||
float flHighPassCutOff)
|
||||
{
|
||||
eax_d_.flHighPassCutOff = flHighPassCutOff;
|
||||
eax_dirty_flags_.flHighPassCutOff = (eax_.flHighPassCutOff != eax_d_.flHighPassCutOff);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_waveform(
|
||||
unsigned long ulWaveform)
|
||||
{
|
||||
eax_d_.ulWaveform = ulWaveform;
|
||||
eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_all(
|
||||
const EAXRINGMODULATORPROPERTIES& all)
|
||||
{
|
||||
defer_frequency(all.flFrequency);
|
||||
defer_high_pass_cutoff(all.flHighPassCutOff);
|
||||
defer_waveform(all.ulWaveform);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_frequency(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& frequency =
|
||||
eax_call.get_value<
|
||||
EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flFrequency)>();
|
||||
|
||||
validate_frequency(frequency);
|
||||
defer_frequency(frequency);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_high_pass_cutoff(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& high_pass_cutoff =
|
||||
eax_call.get_value<
|
||||
EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flHighPassCutOff)>();
|
||||
|
||||
validate_high_pass_cutoff(high_pass_cutoff);
|
||||
defer_high_pass_cutoff(high_pass_cutoff);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_waveform(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& waveform =
|
||||
eax_call.get_value<
|
||||
EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::ulWaveform)>();
|
||||
|
||||
validate_waveform(waveform);
|
||||
defer_waveform(waveform);
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxRingModulatorEffectException, const EAXRINGMODULATORPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxRingModulatorEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxRingModulatorEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.flFrequency)
|
||||
{
|
||||
set_efx_frequency();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flHighPassCutOff)
|
||||
{
|
||||
set_efx_high_pass_cutoff();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulWaveform)
|
||||
{
|
||||
set_efx_waveform();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxRingModulatorEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch (eax_call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE:
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_FREQUENCY:
|
||||
defer_frequency(eax_call);
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF:
|
||||
defer_high_pass_cutoff(eax_call);
|
||||
break;
|
||||
|
||||
case EAXRINGMODULATOR_WAVEFORM:
|
||||
defer_waveform(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxRingModulatorEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_ring_modulator_effect()
|
||||
{
|
||||
return std::make_unique<EaxRingModulatorEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -4,8 +4,12 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax_exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -91,3 +95,58 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Null);
|
||||
|
||||
const EffectProps NullEffectProps{genDefaultProps()};
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
class EaxNullEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxNullEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
}; // EaxNullEffect
|
||||
|
||||
|
||||
class EaxNullEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxNullEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_NULL_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxNullEffectException
|
||||
|
||||
|
||||
EaxNullEffect::EaxNullEffect()
|
||||
: EaxEffect{AL_EFFECT_NULL}
|
||||
{
|
||||
}
|
||||
|
||||
void EaxNullEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
if(eax_call.get_property_id() != 0)
|
||||
throw EaxNullEffectException{"Unsupported property id."};
|
||||
}
|
||||
|
||||
bool EaxNullEffect::apply_deferred()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_null_effect()
|
||||
{
|
||||
return std::make_unique<EaxNullEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -82,3 +89,276 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Pshifter);
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxPitchShifterEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxPitchShifterEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxPitchShifterEffectDirtyFlagsValue lCoarseTune : 1;
|
||||
EaxPitchShifterEffectDirtyFlagsValue lFineTune : 1;
|
||||
}; // EaxPitchShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxPitchShifterEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxPitchShifterEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXPITCHSHIFTERPROPERTIES eax_{};
|
||||
EAXPITCHSHIFTERPROPERTIES eax_d_{};
|
||||
EaxPitchShifterEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_coarse_tune();
|
||||
void set_efx_fine_tune();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_coarse_tune(long lCoarseTune);
|
||||
void validate_fine_tune(long lFineTune);
|
||||
void validate_all(const EAXPITCHSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_coarse_tune(long lCoarseTune);
|
||||
void defer_fine_tune(long lFineTune);
|
||||
void defer_all(const EAXPITCHSHIFTERPROPERTIES& all);
|
||||
|
||||
void defer_coarse_tune(const EaxEaxCall& eax_call);
|
||||
void defer_fine_tune(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxPitchShifterEffect
|
||||
|
||||
|
||||
class EaxPitchShifterEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxPitchShifterEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_PITCH_SHIFTER_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxPitchShifterEffectException
|
||||
|
||||
|
||||
EaxPitchShifterEffect::EaxPitchShifterEffect()
|
||||
: EaxEffect{AL_EFFECT_PITCH_SHIFTER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
|
||||
eax_.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_efx_coarse_tune()
|
||||
{
|
||||
const auto coarse_tune = clamp(
|
||||
static_cast<ALint>(eax_.lCoarseTune),
|
||||
AL_PITCH_SHIFTER_MIN_COARSE_TUNE,
|
||||
AL_PITCH_SHIFTER_MAX_COARSE_TUNE);
|
||||
|
||||
al_effect_props_.Pshifter.CoarseTune = coarse_tune;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_efx_fine_tune()
|
||||
{
|
||||
const auto fine_tune = clamp(
|
||||
static_cast<ALint>(eax_.lFineTune),
|
||||
AL_PITCH_SHIFTER_MIN_FINE_TUNE,
|
||||
AL_PITCH_SHIFTER_MAX_FINE_TUNE);
|
||||
|
||||
al_effect_props_.Pshifter.FineTune = fine_tune;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_coarse_tune();
|
||||
set_efx_fine_tune();
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxPitchShifterEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_COARSETUNE:
|
||||
eax_call.set_value<EaxPitchShifterEffectException>(eax_.lCoarseTune);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_FINETUNE:
|
||||
eax_call.set_value<EaxPitchShifterEffectException>(eax_.lFineTune);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxPitchShifterEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::validate_coarse_tune(
|
||||
long lCoarseTune)
|
||||
{
|
||||
eax_validate_range<EaxPitchShifterEffectException>(
|
||||
"Coarse Tune",
|
||||
lCoarseTune,
|
||||
EAXPITCHSHIFTER_MINCOARSETUNE,
|
||||
EAXPITCHSHIFTER_MAXCOARSETUNE);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::validate_fine_tune(
|
||||
long lFineTune)
|
||||
{
|
||||
eax_validate_range<EaxPitchShifterEffectException>(
|
||||
"Fine Tune",
|
||||
lFineTune,
|
||||
EAXPITCHSHIFTER_MINFINETUNE,
|
||||
EAXPITCHSHIFTER_MAXFINETUNE);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::validate_all(
|
||||
const EAXPITCHSHIFTERPROPERTIES& all)
|
||||
{
|
||||
validate_coarse_tune(all.lCoarseTune);
|
||||
validate_fine_tune(all.lFineTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_coarse_tune(
|
||||
long lCoarseTune)
|
||||
{
|
||||
eax_d_.lCoarseTune = lCoarseTune;
|
||||
eax_dirty_flags_.lCoarseTune = (eax_.lCoarseTune != eax_d_.lCoarseTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_fine_tune(
|
||||
long lFineTune)
|
||||
{
|
||||
eax_d_.lFineTune = lFineTune;
|
||||
eax_dirty_flags_.lFineTune = (eax_.lFineTune != eax_d_.lFineTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_all(
|
||||
const EAXPITCHSHIFTERPROPERTIES& all)
|
||||
{
|
||||
defer_coarse_tune(all.lCoarseTune);
|
||||
defer_fine_tune(all.lFineTune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_coarse_tune(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& coarse_tune =
|
||||
eax_call.get_value<EaxPitchShifterEffectException, const decltype(EAXPITCHSHIFTERPROPERTIES::lCoarseTune)>();
|
||||
|
||||
validate_coarse_tune(coarse_tune);
|
||||
defer_coarse_tune(coarse_tune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_fine_tune(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& fine_tune =
|
||||
eax_call.get_value<EaxPitchShifterEffectException, const decltype(EAXPITCHSHIFTERPROPERTIES::lFineTune)>();
|
||||
|
||||
validate_fine_tune(fine_tune);
|
||||
defer_fine_tune(fine_tune);
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all =
|
||||
eax_call.get_value<EaxPitchShifterEffectException, const EAXPITCHSHIFTERPROPERTIES>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxPitchShifterEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxPitchShifterEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.lCoarseTune)
|
||||
{
|
||||
set_efx_coarse_tune();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lFineTune)
|
||||
{
|
||||
set_efx_fine_tune();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxPitchShifterEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxPitchShifterEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE:
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_COARSETUNE:
|
||||
defer_coarse_tune(eax_call);
|
||||
break;
|
||||
|
||||
case EAXPITCHSHIFTER_FINETUNE:
|
||||
defer_fine_tune(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxPitchShifterEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EaxEffectUPtr eax_create_eax_pitch_shifter_effect()
|
||||
{
|
||||
return std::make_unique<EaxPitchShifterEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,23 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "alnumeric.h"
|
||||
|
||||
#include "al/eax_exception.h"
|
||||
#include "al/eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -245,3 +256,531 @@ EffectProps genDefaultProps() noexcept
|
|||
DEFINE_ALEFFECT_VTABLE(Vmorpher);
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
using EaxVocalMorpherEffectDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxVocalMorpherEffectDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeA : 1;
|
||||
EaxVocalMorpherEffectDirtyFlagsValue lPhonemeACoarseTuning : 1;
|
||||
EaxVocalMorpherEffectDirtyFlagsValue ulPhonemeB : 1;
|
||||
EaxVocalMorpherEffectDirtyFlagsValue lPhonemeBCoarseTuning : 1;
|
||||
EaxVocalMorpherEffectDirtyFlagsValue ulWaveform : 1;
|
||||
EaxVocalMorpherEffectDirtyFlagsValue flRate : 1;
|
||||
}; // EaxPitchShifterEffectDirtyFlags
|
||||
|
||||
|
||||
class EaxVocalMorpherEffect final :
|
||||
public EaxEffect
|
||||
{
|
||||
public:
|
||||
EaxVocalMorpherEffect();
|
||||
|
||||
void dispatch(const EaxEaxCall& eax_call) override;
|
||||
|
||||
// [[nodiscard]]
|
||||
bool apply_deferred() override;
|
||||
|
||||
private:
|
||||
EAXVOCALMORPHERPROPERTIES eax_{};
|
||||
EAXVOCALMORPHERPROPERTIES eax_d_{};
|
||||
EaxVocalMorpherEffectDirtyFlags eax_dirty_flags_{};
|
||||
|
||||
void set_eax_defaults();
|
||||
|
||||
void set_efx_phoneme_a();
|
||||
void set_efx_phoneme_a_coarse_tuning();
|
||||
void set_efx_phoneme_b();
|
||||
void set_efx_phoneme_b_coarse_tuning();
|
||||
void set_efx_waveform();
|
||||
void set_efx_rate();
|
||||
void set_efx_defaults();
|
||||
|
||||
void get(const EaxEaxCall& eax_call);
|
||||
|
||||
void validate_phoneme_a(unsigned long ulPhonemeA);
|
||||
void validate_phoneme_a_coarse_tuning(long lPhonemeACoarseTuning);
|
||||
void validate_phoneme_b(unsigned long ulPhonemeB);
|
||||
void validate_phoneme_b_coarse_tuning(long lPhonemeBCoarseTuning);
|
||||
void validate_waveform(unsigned long ulWaveform);
|
||||
void validate_rate(float flRate);
|
||||
void validate_all(const EAXVOCALMORPHERPROPERTIES& all);
|
||||
|
||||
void defer_phoneme_a(unsigned long ulPhonemeA);
|
||||
void defer_phoneme_a_coarse_tuning(long lPhonemeACoarseTuning);
|
||||
void defer_phoneme_b(unsigned long ulPhonemeB);
|
||||
void defer_phoneme_b_coarse_tuning(long lPhonemeBCoarseTuning);
|
||||
void defer_waveform(unsigned long ulWaveform);
|
||||
void defer_rate(float flRate);
|
||||
void defer_all(const EAXVOCALMORPHERPROPERTIES& all);
|
||||
|
||||
void defer_phoneme_a(const EaxEaxCall& eax_call);
|
||||
void defer_phoneme_a_coarse_tuning(const EaxEaxCall& eax_call);
|
||||
void defer_phoneme_b(const EaxEaxCall& eax_call);
|
||||
void defer_phoneme_b_coarse_tuning(const EaxEaxCall& eax_call);
|
||||
void defer_waveform(const EaxEaxCall& eax_call);
|
||||
void defer_rate(const EaxEaxCall& eax_call);
|
||||
void defer_all(const EaxEaxCall& eax_call);
|
||||
|
||||
void set(const EaxEaxCall& eax_call);
|
||||
}; // EaxVocalMorpherEffect
|
||||
|
||||
|
||||
class EaxVocalMorpherEffectException :
|
||||
public EaxException
|
||||
{
|
||||
public:
|
||||
explicit EaxVocalMorpherEffectException(
|
||||
const char* message)
|
||||
:
|
||||
EaxException{"EAX_VOCAL_MORPHER_EFFECT", message}
|
||||
{
|
||||
}
|
||||
}; // EaxVocalMorpherEffectException
|
||||
|
||||
|
||||
EaxVocalMorpherEffect::EaxVocalMorpherEffect()
|
||||
: EaxEffect{AL_EFFECT_VOCAL_MORPHER}
|
||||
{
|
||||
set_eax_defaults();
|
||||
set_efx_defaults();
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::dispatch(const EaxEaxCall& eax_call)
|
||||
{
|
||||
eax_call.is_get() ? get(eax_call) : set(eax_call);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_eax_defaults()
|
||||
{
|
||||
eax_.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||
eax_.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||
eax_.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||
eax_.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||
eax_.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||
eax_.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||
|
||||
eax_d_ = eax_;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_phoneme_a()
|
||||
{
|
||||
const auto phoneme_a = clamp(
|
||||
static_cast<ALint>(eax_.ulPhonemeA),
|
||||
AL_VOCAL_MORPHER_MIN_PHONEMEA,
|
||||
AL_VOCAL_MORPHER_MAX_PHONEMEA);
|
||||
|
||||
const auto efx_phoneme_a = PhenomeFromEnum(phoneme_a);
|
||||
assert(efx_phoneme_a.has_value());
|
||||
al_effect_props_.Vmorpher.PhonemeA = *efx_phoneme_a;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_phoneme_a_coarse_tuning()
|
||||
{
|
||||
const auto phoneme_a_coarse_tuning = clamp(
|
||||
static_cast<ALint>(eax_.lPhonemeACoarseTuning),
|
||||
AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING,
|
||||
AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING);
|
||||
|
||||
al_effect_props_.Vmorpher.PhonemeACoarseTuning = phoneme_a_coarse_tuning;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_phoneme_b()
|
||||
{
|
||||
const auto phoneme_b = clamp(
|
||||
static_cast<ALint>(eax_.ulPhonemeB),
|
||||
AL_VOCAL_MORPHER_MIN_PHONEMEB,
|
||||
AL_VOCAL_MORPHER_MAX_PHONEMEB);
|
||||
|
||||
const auto efx_phoneme_b = PhenomeFromEnum(phoneme_b);
|
||||
assert(efx_phoneme_b.has_value());
|
||||
al_effect_props_.Vmorpher.PhonemeB = *efx_phoneme_b;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_phoneme_b_coarse_tuning()
|
||||
{
|
||||
const auto phoneme_b_coarse_tuning = clamp(
|
||||
static_cast<ALint>(eax_.lPhonemeBCoarseTuning),
|
||||
AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING,
|
||||
AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING);
|
||||
|
||||
al_effect_props_.Vmorpher.PhonemeBCoarseTuning = phoneme_b_coarse_tuning;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_waveform()
|
||||
{
|
||||
const auto waveform = clamp(
|
||||
static_cast<ALint>(eax_.ulWaveform),
|
||||
AL_VOCAL_MORPHER_MIN_WAVEFORM,
|
||||
AL_VOCAL_MORPHER_MAX_WAVEFORM);
|
||||
|
||||
const auto wfx_waveform = WaveformFromEmum(waveform);
|
||||
assert(wfx_waveform.has_value());
|
||||
al_effect_props_.Vmorpher.Waveform = *wfx_waveform;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_rate()
|
||||
{
|
||||
const auto rate = clamp(
|
||||
eax_.flRate,
|
||||
AL_VOCAL_MORPHER_MIN_RATE,
|
||||
AL_VOCAL_MORPHER_MAX_RATE);
|
||||
|
||||
al_effect_props_.Vmorpher.Rate = rate;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set_efx_defaults()
|
||||
{
|
||||
set_efx_phoneme_a();
|
||||
set_efx_phoneme_a_coarse_tuning();
|
||||
set_efx_phoneme_b();
|
||||
set_efx_phoneme_b_coarse_tuning();
|
||||
set_efx_waveform();
|
||||
set_efx_rate();
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::get(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEA:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulPhonemeA);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_.lPhonemeACoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEB:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulPhonemeB);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_.lPhonemeBCoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_WAVEFORM:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_.ulWaveform);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_RATE:
|
||||
eax_call.set_value<EaxVocalMorpherEffectException>(eax_.flRate);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxVocalMorpherEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_phoneme_a(
|
||||
unsigned long ulPhonemeA)
|
||||
{
|
||||
eax_validate_range<EaxVocalMorpherEffectException>(
|
||||
"Phoneme A",
|
||||
ulPhonemeA,
|
||||
EAXVOCALMORPHER_MINPHONEMEA,
|
||||
EAXVOCALMORPHER_MAXPHONEMEA);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_phoneme_a_coarse_tuning(
|
||||
long lPhonemeACoarseTuning)
|
||||
{
|
||||
eax_validate_range<EaxVocalMorpherEffectException>(
|
||||
"Phoneme A Coarse Tuning",
|
||||
lPhonemeACoarseTuning,
|
||||
EAXVOCALMORPHER_MINPHONEMEACOARSETUNING,
|
||||
EAXVOCALMORPHER_MAXPHONEMEACOARSETUNING);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_phoneme_b(
|
||||
unsigned long ulPhonemeB)
|
||||
{
|
||||
eax_validate_range<EaxVocalMorpherEffectException>(
|
||||
"Phoneme B",
|
||||
ulPhonemeB,
|
||||
EAXVOCALMORPHER_MINPHONEMEB,
|
||||
EAXVOCALMORPHER_MAXPHONEMEB);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_phoneme_b_coarse_tuning(
|
||||
long lPhonemeBCoarseTuning)
|
||||
{
|
||||
eax_validate_range<EaxVocalMorpherEffectException>(
|
||||
"Phoneme B Coarse Tuning",
|
||||
lPhonemeBCoarseTuning,
|
||||
EAXVOCALMORPHER_MINPHONEMEBCOARSETUNING,
|
||||
EAXVOCALMORPHER_MAXPHONEMEBCOARSETUNING);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_waveform(
|
||||
unsigned long ulWaveform)
|
||||
{
|
||||
eax_validate_range<EaxVocalMorpherEffectException>(
|
||||
"Waveform",
|
||||
ulWaveform,
|
||||
EAXVOCALMORPHER_MINWAVEFORM,
|
||||
EAXVOCALMORPHER_MAXWAVEFORM);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_rate(
|
||||
float flRate)
|
||||
{
|
||||
eax_validate_range<EaxVocalMorpherEffectException>(
|
||||
"Rate",
|
||||
flRate,
|
||||
EAXVOCALMORPHER_MINRATE,
|
||||
EAXVOCALMORPHER_MAXRATE);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::validate_all(
|
||||
const EAXVOCALMORPHERPROPERTIES& all)
|
||||
{
|
||||
validate_phoneme_a(all.ulPhonemeA);
|
||||
validate_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning);
|
||||
validate_phoneme_b(all.ulPhonemeB);
|
||||
validate_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning);
|
||||
validate_waveform(all.ulWaveform);
|
||||
validate_rate(all.flRate);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_a(
|
||||
unsigned long ulPhonemeA)
|
||||
{
|
||||
eax_d_.ulPhonemeA = ulPhonemeA;
|
||||
eax_dirty_flags_.ulPhonemeA = (eax_.ulPhonemeA != eax_d_.ulPhonemeA);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning(
|
||||
long lPhonemeACoarseTuning)
|
||||
{
|
||||
eax_d_.lPhonemeACoarseTuning = lPhonemeACoarseTuning;
|
||||
eax_dirty_flags_.lPhonemeACoarseTuning = (eax_.lPhonemeACoarseTuning != eax_d_.lPhonemeACoarseTuning);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_b(
|
||||
unsigned long ulPhonemeB)
|
||||
{
|
||||
eax_d_.ulPhonemeB = ulPhonemeB;
|
||||
eax_dirty_flags_.ulPhonemeB = (eax_.ulPhonemeB != eax_d_.ulPhonemeB);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning(
|
||||
long lPhonemeBCoarseTuning)
|
||||
{
|
||||
eax_d_.lPhonemeBCoarseTuning = lPhonemeBCoarseTuning;
|
||||
eax_dirty_flags_.lPhonemeBCoarseTuning = (eax_.lPhonemeBCoarseTuning != eax_d_.lPhonemeBCoarseTuning);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_waveform(
|
||||
unsigned long ulWaveform)
|
||||
{
|
||||
eax_d_.ulWaveform = ulWaveform;
|
||||
eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_rate(
|
||||
float flRate)
|
||||
{
|
||||
eax_d_.flRate = flRate;
|
||||
eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_all(
|
||||
const EAXVOCALMORPHERPROPERTIES& all)
|
||||
{
|
||||
defer_phoneme_a(all.ulPhonemeA);
|
||||
defer_phoneme_a_coarse_tuning(all.lPhonemeACoarseTuning);
|
||||
defer_phoneme_b(all.ulPhonemeB);
|
||||
defer_phoneme_b_coarse_tuning(all.lPhonemeBCoarseTuning);
|
||||
defer_waveform(all.ulWaveform);
|
||||
defer_rate(all.flRate);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_a(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& phoneme_a = eax_call.get_value<EaxVocalMorpherEffectException,
|
||||
const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeA)>();
|
||||
|
||||
validate_phoneme_a(phoneme_a);
|
||||
defer_phoneme_a(phoneme_a);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_a_coarse_tuning(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& phoneme_a_coarse_tuning = eax_call.get_value<
|
||||
EaxVocalMorpherEffectException,
|
||||
const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeACoarseTuning)
|
||||
>();
|
||||
|
||||
validate_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning);
|
||||
defer_phoneme_a_coarse_tuning(phoneme_a_coarse_tuning);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_b(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& phoneme_b = eax_call.get_value<
|
||||
EaxVocalMorpherEffectException,
|
||||
const decltype(EAXVOCALMORPHERPROPERTIES::ulPhonemeB)
|
||||
>();
|
||||
|
||||
validate_phoneme_b(phoneme_b);
|
||||
defer_phoneme_b(phoneme_b);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_phoneme_b_coarse_tuning(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& phoneme_b_coarse_tuning = eax_call.get_value<
|
||||
EaxVocalMorpherEffectException,
|
||||
const decltype(EAXVOCALMORPHERPROPERTIES::lPhonemeBCoarseTuning)
|
||||
>();
|
||||
|
||||
validate_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning);
|
||||
defer_phoneme_b_coarse_tuning(phoneme_b_coarse_tuning);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_waveform(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& waveform = eax_call.get_value<
|
||||
EaxVocalMorpherEffectException,
|
||||
const decltype(EAXVOCALMORPHERPROPERTIES::ulWaveform)
|
||||
>();
|
||||
|
||||
validate_waveform(waveform);
|
||||
defer_waveform(waveform);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_rate(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& rate = eax_call.get_value<
|
||||
EaxVocalMorpherEffectException,
|
||||
const decltype(EAXVOCALMORPHERPROPERTIES::flRate)
|
||||
>();
|
||||
|
||||
validate_rate(rate);
|
||||
defer_rate(rate);
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::defer_all(
|
||||
const EaxEaxCall& eax_call)
|
||||
{
|
||||
const auto& all = eax_call.get_value<
|
||||
EaxVocalMorpherEffectException,
|
||||
const EAXVOCALMORPHERPROPERTIES
|
||||
>();
|
||||
|
||||
validate_all(all);
|
||||
defer_all(all);
|
||||
}
|
||||
|
||||
// [[nodiscard]]
|
||||
bool EaxVocalMorpherEffect::apply_deferred()
|
||||
{
|
||||
if (eax_dirty_flags_ == EaxVocalMorpherEffectDirtyFlags{})
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
eax_ = eax_d_;
|
||||
|
||||
if (eax_dirty_flags_.ulPhonemeA)
|
||||
{
|
||||
set_efx_phoneme_a();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lPhonemeACoarseTuning)
|
||||
{
|
||||
set_efx_phoneme_a_coarse_tuning();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulPhonemeB)
|
||||
{
|
||||
set_efx_phoneme_b();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.lPhonemeBCoarseTuning)
|
||||
{
|
||||
set_efx_phoneme_b_coarse_tuning();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.ulWaveform)
|
||||
{
|
||||
set_efx_waveform();
|
||||
}
|
||||
|
||||
if (eax_dirty_flags_.flRate)
|
||||
{
|
||||
set_efx_rate();
|
||||
}
|
||||
|
||||
eax_dirty_flags_ = EaxVocalMorpherEffectDirtyFlags{};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EaxVocalMorpherEffect::set(const EaxEaxCall& eax_call)
|
||||
{
|
||||
switch(eax_call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
||||
defer_all(eax_call);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEA:
|
||||
defer_phoneme_a(eax_call);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
||||
defer_phoneme_a_coarse_tuning(eax_call);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEB:
|
||||
defer_phoneme_b(eax_call);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
||||
defer_phoneme_b_coarse_tuning(eax_call);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_WAVEFORM:
|
||||
defer_waveform(eax_call);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_RATE:
|
||||
defer_rate(eax_call);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw EaxVocalMorpherEffectException{"Unsupported property id."};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
EaxEffectUPtr eax_create_eax_vocal_morpher_effect()
|
||||
{
|
||||
return std::make_unique<EaxVocalMorpherEffect>();
|
||||
}
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
|
|
@ -85,9 +85,9 @@ AL_API ALenum AL_APIENTRY alGetError(void)
|
|||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context)
|
||||
if(unlikely(!context))
|
||||
{
|
||||
constexpr ALenum deferror{AL_INVALID_OPERATION};
|
||||
static constexpr ALenum deferror{AL_INVALID_OPERATION};
|
||||
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
|
||||
if(TrapALError)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,24 +18,24 @@
|
|||
#include "AL/alc.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "async_event.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects/base.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "voice_change.h"
|
||||
|
||||
|
||||
static int EventThread(ALCcontext *context)
|
||||
{
|
||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||
bool quitnow{false};
|
||||
while LIKELY(!quitnow)
|
||||
while(likely(!quitnow))
|
||||
{
|
||||
auto evt_data = ring->getReadVector().first;
|
||||
if(evt_data.len == 0)
|
||||
|
|
@ -54,10 +54,10 @@ static int EventThread(ALCcontext *context)
|
|||
al::destroy_at(evt_ptr);
|
||||
ring->readAdvance(1);
|
||||
|
||||
quitnow = evt.EnumType == EventType_KillThread;
|
||||
if UNLIKELY(quitnow) break;
|
||||
quitnow = evt.EnumType == AsyncEvent::KillThread;
|
||||
if(unlikely(quitnow)) break;
|
||||
|
||||
if(evt.EnumType == EventType_ReleaseEffectState)
|
||||
if(evt.EnumType == AsyncEvent::ReleaseEffectState)
|
||||
{
|
||||
evt.u.mEffectState->release();
|
||||
continue;
|
||||
|
|
@ -66,41 +66,38 @@ static int EventThread(ALCcontext *context)
|
|||
uint enabledevts{context->mEnabledEvts.load(std::memory_order_acquire)};
|
||||
if(!context->mEventCb) continue;
|
||||
|
||||
if(evt.EnumType == EventType_SourceStateChange)
|
||||
if(evt.EnumType == AsyncEvent::SourceStateChange)
|
||||
{
|
||||
if(!(enabledevts&EventType_SourceStateChange))
|
||||
if(!(enabledevts&AsyncEvent::SourceStateChange))
|
||||
continue;
|
||||
ALuint state{};
|
||||
std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
|
||||
msg += " state has changed to ";
|
||||
switch(evt.u.srcstate.state)
|
||||
{
|
||||
case VChangeState::Reset:
|
||||
case AsyncEvent::SrcState::Reset:
|
||||
msg += "AL_INITIAL";
|
||||
state = AL_INITIAL;
|
||||
break;
|
||||
case VChangeState::Stop:
|
||||
case AsyncEvent::SrcState::Stop:
|
||||
msg += "AL_STOPPED";
|
||||
state = AL_STOPPED;
|
||||
break;
|
||||
case VChangeState::Play:
|
||||
case AsyncEvent::SrcState::Play:
|
||||
msg += "AL_PLAYING";
|
||||
state = AL_PLAYING;
|
||||
break;
|
||||
case VChangeState::Pause:
|
||||
case AsyncEvent::SrcState::Pause:
|
||||
msg += "AL_PAUSED";
|
||||
state = AL_PAUSED;
|
||||
break;
|
||||
/* Shouldn't happen */
|
||||
case VChangeState::Restart:
|
||||
break;
|
||||
}
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == EventType_BufferCompleted)
|
||||
else if(evt.EnumType == AsyncEvent::BufferCompleted)
|
||||
{
|
||||
if(!(enabledevts&EventType_BufferCompleted))
|
||||
if(!(enabledevts&AsyncEvent::BufferCompleted))
|
||||
continue;
|
||||
std::string msg{std::to_string(evt.u.bufcomp.count)};
|
||||
if(evt.u.bufcomp.count == 1) msg += " buffer completed";
|
||||
|
|
@ -109,9 +106,9 @@ static int EventThread(ALCcontext *context)
|
|||
evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
|
||||
context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == EventType_Disconnected)
|
||||
else if(evt.EnumType == AsyncEvent::Disconnected)
|
||||
{
|
||||
if(!(enabledevts&EventType_Disconnected))
|
||||
if(!(enabledevts&AsyncEvent::Disconnected))
|
||||
continue;
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
|
||||
|
|
@ -146,7 +143,7 @@ void StopEventThrd(ALCcontext *ctx)
|
|||
evt_data = ring->getWriteVector().first;
|
||||
} while(evt_data.len == 0);
|
||||
}
|
||||
::new(evt_data.buf) AsyncEvent{EventType_KillThread};
|
||||
al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
|
||||
ring->writeAdvance(1);
|
||||
|
||||
ctx->mEventSem.post();
|
||||
|
|
@ -158,7 +155,7 @@ AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, A
|
|||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
if(unlikely(!context)) return;
|
||||
|
||||
if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
|
||||
if(count <= 0) return;
|
||||
|
|
@ -170,11 +167,11 @@ START_API_FUNC
|
|||
[&flags](ALenum type) noexcept -> bool
|
||||
{
|
||||
if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
|
||||
flags |= EventType_BufferCompleted;
|
||||
flags |= AsyncEvent::BufferCompleted;
|
||||
else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
|
||||
flags |= EventType_SourceStateChange;
|
||||
flags |= AsyncEvent::SourceStateChange;
|
||||
else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
|
||||
flags |= EventType_Disconnected;
|
||||
flags |= AsyncEvent::Disconnected;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
|
|
@ -204,7 +201,7 @@ START_API_FUNC
|
|||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
std::lock_guard<std::mutex>{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
@ -213,7 +210,7 @@ AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *user
|
|||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
if(unlikely(!context)) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> __{context->mEventCbLock};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "opthelpers.h"
|
||||
|
|
@ -37,7 +37,7 @@ AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
|||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return AL_FALSE;
|
||||
if(unlikely(!context)) return AL_FALSE;
|
||||
|
||||
if(!extName)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE, AL_FALSE, "NULL pointer");
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/except.h"
|
||||
|
|
@ -52,7 +52,11 @@ class filter_exception final : public al::base_exception {
|
|||
ALenum mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
filter_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
|
|
@ -63,8 +67,6 @@ public:
|
|||
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||
};
|
||||
|
||||
#define FILTER_MIN_GAIN 0.0f
|
||||
#define FILTER_MAX_GAIN 4.0f /* +12dB */
|
||||
|
||||
#define DEFINE_ALFILTER_VTABLE(T) \
|
||||
const ALfilter::Vtable T##_vtable = { \
|
||||
|
|
@ -84,7 +86,7 @@ void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
|
|||
switch(param)
|
||||
{
|
||||
case AL_LOWPASS_GAIN:
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN))
|
||||
throw filter_exception{AL_INVALID_VALUE, "Low-pass gain %f out of range", val};
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
|
@ -143,7 +145,7 @@ void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
|
|||
switch(param)
|
||||
{
|
||||
case AL_HIGHPASS_GAIN:
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN))
|
||||
throw filter_exception{AL_INVALID_VALUE, "High-pass gain %f out of range", val};
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
|
@ -202,7 +204,7 @@ void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
|
|||
switch(param)
|
||||
{
|
||||
case AL_BANDPASS_GAIN:
|
||||
if(!(val >= FILTER_MIN_GAIN && val <= FILTER_MAX_GAIN))
|
||||
if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN))
|
||||
throw filter_exception{AL_INVALID_VALUE, "Band-pass gain %f out of range", val};
|
||||
filter->Gain = val;
|
||||
break;
|
||||
|
|
@ -351,12 +353,12 @@ ALfilter *AllocFilter(ALCdevice *device)
|
|||
{
|
||||
auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
|
||||
[](const FilterSubList &entry) noexcept -> bool
|
||||
{ return entry.FreeMask != 0; }
|
||||
);
|
||||
{ return entry.FreeMask != 0; });
|
||||
auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
|
||||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALfilter *filter{::new(sublist->Filters + slidx) ALfilter{}};
|
||||
ALfilter *filter{al::construct_at(sublist->Filters + slidx)};
|
||||
InitFilterParams(filter, AL_FILTER_NULL);
|
||||
|
||||
/* Add 1 to avoid filter ID 0. */
|
||||
|
|
@ -404,8 +406,8 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
if(!EnsureFilters(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
|
||||
|
|
@ -444,7 +446,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
/* First try to find any filters that are invalid. */
|
||||
|
|
@ -475,7 +477,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
if(!filter || LookupFilter(device, filter))
|
||||
return AL_TRUE;
|
||||
|
|
@ -491,7 +493,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -532,7 +534,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -555,7 +557,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -578,7 +580,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -601,7 +603,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -636,7 +638,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -659,7 +661,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
@ -682,7 +684,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#ifndef AL_FILTER_H
|
||||
#define AL_FILTER_H
|
||||
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
#define LOWPASSFREQREF 5000.0f
|
||||
#define HIGHPASSFREQREF 250.0f
|
||||
|
||||
|
|
|
|||
|
|
@ -29,20 +29,53 @@
|
|||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
#define DO_UPDATEPROPS() do { \
|
||||
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
||||
UpdateListenerProps(context.get()); \
|
||||
else \
|
||||
listener.PropsClean.clear(std::memory_order_release); \
|
||||
} while(0)
|
||||
namespace {
|
||||
|
||||
inline void UpdateProps(ALCcontext *context)
|
||||
{
|
||||
if(!context->mDeferUpdates)
|
||||
{
|
||||
UpdateContextProps(context);
|
||||
return;
|
||||
}
|
||||
context->mPropsDirty = true;
|
||||
}
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
inline void CommitAndUpdateProps(ALCcontext *context)
|
||||
{
|
||||
if(!context->mDeferUpdates)
|
||||
{
|
||||
if(context->has_eax())
|
||||
{
|
||||
context->mHoldUpdates.store(true, std::memory_order_release);
|
||||
while((context->mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
|
||||
/* busy-wait */
|
||||
}
|
||||
|
||||
context->eax_commit_and_update_sources();
|
||||
}
|
||||
UpdateContextProps(context);
|
||||
context->mHoldUpdates.store(false, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
context->mPropsDirty = true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline void CommitAndUpdateProps(ALCcontext *context)
|
||||
{ UpdateProps(context); }
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
||||
START_API_FUNC
|
||||
|
|
@ -58,14 +91,14 @@ START_API_FUNC
|
|||
if(!(value >= 0.0f && std::isfinite(value)))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener gain out of range");
|
||||
listener.Gain = value;
|
||||
DO_UPDATEPROPS();
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Listener meters per unit out of range");
|
||||
listener.mMetersPerUnit = value;
|
||||
DO_UPDATEPROPS();
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -90,7 +123,7 @@ START_API_FUNC
|
|||
listener.Position[0] = value1;
|
||||
listener.Position[1] = value2;
|
||||
listener.Position[2] = value3;
|
||||
DO_UPDATEPROPS();
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
case AL_VELOCITY:
|
||||
|
|
@ -99,7 +132,7 @@ START_API_FUNC
|
|||
listener.Velocity[0] = value1;
|
||||
listener.Velocity[1] = value2;
|
||||
listener.Velocity[2] = value3;
|
||||
DO_UPDATEPROPS();
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -146,7 +179,7 @@ START_API_FUNC
|
|||
listener.OrientUp[0] = values[3];
|
||||
listener.OrientUp[1] = values[4];
|
||||
listener.OrientUp[2] = values[5];
|
||||
DO_UPDATEPROPS();
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -414,39 +447,3 @@ START_API_FUNC
|
|||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
void UpdateListenerProps(ALCcontext *context)
|
||||
{
|
||||
/* Get an unused proprty container, or allocate a new one as needed. */
|
||||
ListenerProps *props{context->mFreeListenerProps.load(std::memory_order_acquire)};
|
||||
if(!props)
|
||||
props = new ListenerProps{};
|
||||
else
|
||||
{
|
||||
ListenerProps *next;
|
||||
do {
|
||||
next = props->next.load(std::memory_order_relaxed);
|
||||
} while(context->mFreeListenerProps.compare_exchange_weak(props, next,
|
||||
std::memory_order_seq_cst, std::memory_order_acquire) == 0);
|
||||
}
|
||||
|
||||
/* Copy in current property values. */
|
||||
ALlistener &listener = context->mListener;
|
||||
props->Position = listener.Position;
|
||||
props->Velocity = listener.Velocity;
|
||||
props->OrientAt = listener.OrientAt;
|
||||
props->OrientUp = listener.OrientUp;
|
||||
props->Gain = listener.Gain;
|
||||
props->MetersPerUnit = listener.mMetersPerUnit;
|
||||
|
||||
/* Set the new container for updating internal parameters. */
|
||||
props = context->mParams.ListenerUpdate.exchange(props, std::memory_order_acq_rel);
|
||||
if(props)
|
||||
{
|
||||
/* If there was an unused update container, put it back in the
|
||||
* freelist.
|
||||
*/
|
||||
AtomicReplaceHead(context->mFreeListenerProps, props);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#define AL_LISTENER_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
|
@ -19,13 +18,7 @@ struct ALlistener {
|
|||
float Gain{1.0f};
|
||||
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
||||
|
||||
std::atomic_flag PropsClean;
|
||||
|
||||
ALlistener() { PropsClean.test_and_set(std::memory_order_relaxed); }
|
||||
|
||||
DISABLE_ALLOC()
|
||||
};
|
||||
|
||||
void UpdateListenerProps(ALCcontext *context);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -11,19 +11,31 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "aldeque.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alu.h"
|
||||
#include "math_defs.h"
|
||||
#include "atomic.h"
|
||||
#include "core/voice.h"
|
||||
#include "vector.h"
|
||||
#include "voice.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax_eax_call.h"
|
||||
#include "eax_fx_slot_index.h"
|
||||
#include "eax_utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffectslot;
|
||||
|
||||
|
||||
enum class SourceStereo : bool {
|
||||
Normal = AL_NORMAL_SOFT,
|
||||
Enhanced = AL_SUPER_STEREO_SOFT
|
||||
};
|
||||
|
||||
#define DEFAULT_SENDS 2
|
||||
|
||||
#define INVALID_VOICE_IDX static_cast<ALuint>(-1)
|
||||
|
|
@ -35,6 +47,70 @@ struct ALbufferQueueItem : public VoiceBufferItem {
|
|||
};
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
using EaxSourceSourceFilterDirtyFlagsValue = std::uint_least16_t;
|
||||
|
||||
struct EaxSourceSourceFilterDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxSourceSourceFilterDirtyFlagsValue lDirect : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue lDirectHF : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue lRoom : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue lRoomHF : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue lObstruction : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue flObstructionLFRatio : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue lOcclusion : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue flOcclusionLFRatio : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue flOcclusionRoomRatio : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue flOcclusionDirectRatio : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue lExclusion : 1;
|
||||
EaxSourceSourceFilterDirtyFlagsValue flExclusionLFRatio : 1;
|
||||
}; // EaxSourceSourceFilterDirtyFlags
|
||||
|
||||
|
||||
using EaxSourceSourceMiscDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxSourceSourceMiscDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxSourceSourceMiscDirtyFlagsValue lOutsideVolumeHF : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue flDopplerFactor : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue flRolloffFactor : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue flRoomRolloffFactor : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue flAirAbsorptionFactor : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue ulFlags : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue flMacroFXFactor : 1;
|
||||
EaxSourceSourceMiscDirtyFlagsValue speaker_levels : 1;
|
||||
}; // EaxSourceSourceMiscDirtyFlags
|
||||
|
||||
|
||||
using EaxSourceSendDirtyFlagsValue = std::uint_least8_t;
|
||||
|
||||
struct EaxSourceSendDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxSourceSendDirtyFlagsValue lSend : 1;
|
||||
EaxSourceSendDirtyFlagsValue lSendHF : 1;
|
||||
EaxSourceSendDirtyFlagsValue lOcclusion : 1;
|
||||
EaxSourceSendDirtyFlagsValue flOcclusionLFRatio : 1;
|
||||
EaxSourceSendDirtyFlagsValue flOcclusionRoomRatio : 1;
|
||||
EaxSourceSendDirtyFlagsValue flOcclusionDirectRatio : 1;
|
||||
EaxSourceSendDirtyFlagsValue lExclusion : 1;
|
||||
EaxSourceSendDirtyFlagsValue flExclusionLFRatio : 1;
|
||||
}; // EaxSourceSendDirtyFlags
|
||||
|
||||
|
||||
struct EaxSourceSendsDirtyFlags
|
||||
{
|
||||
using EaxIsBitFieldStruct = bool;
|
||||
|
||||
EaxSourceSendDirtyFlags sends[EAX_MAX_FXSLOTS];
|
||||
}; // EaxSourceSendsDirtyFlags
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct ALsource {
|
||||
/** Source properties. */
|
||||
float Pitch{1.0f};
|
||||
|
|
@ -47,6 +123,11 @@ struct ALsource {
|
|||
float RefDistance{1.0f};
|
||||
float MaxDistance{std::numeric_limits<float>::max()};
|
||||
float RolloffFactor{1.0f};
|
||||
#ifdef ALSOFT_EAX
|
||||
// For EAXSOURCE_ROLLOFFFACTOR, which is distinct from and added to
|
||||
// AL_ROLLOFF_FACTOR
|
||||
float RolloffFactor2{0.0f};
|
||||
#endif
|
||||
std::array<float,3> Position{{0.0f, 0.0f, 0.0f}};
|
||||
std::array<float,3> Velocity{{0.0f, 0.0f, 0.0f}};
|
||||
std::array<float,3> Direction{{0.0f, 0.0f, 0.0f}};
|
||||
|
|
@ -58,6 +139,7 @@ struct ALsource {
|
|||
Resampler mResampler{ResamplerDefault};
|
||||
DirectMode DirectChannels{DirectMode::Off};
|
||||
SpatializeMode mSpatialize{SpatializeMode::Auto};
|
||||
SourceStereo mStereoMode{SourceStereo::Normal};
|
||||
|
||||
bool DryGainHFAuto{true};
|
||||
bool WetGainAuto{true};
|
||||
|
|
@ -71,9 +153,10 @@ struct ALsource {
|
|||
/* NOTE: Stereo pan angles are specified in radians, counter-clockwise
|
||||
* rather than clockwise.
|
||||
*/
|
||||
std::array<float,2> StereoPan{{Deg2Rad( 30.0f), Deg2Rad(-30.0f)}};
|
||||
std::array<float,2> StereoPan{{al::numbers::pi_v<float>/6.0f, -al::numbers::pi_v<float>/6.0f}};
|
||||
|
||||
float Radius{0.0f};
|
||||
float EnhWidth{0.593f};
|
||||
|
||||
/** Direct filter and auxiliary send info. */
|
||||
struct {
|
||||
|
|
@ -109,7 +192,7 @@ struct ALsource {
|
|||
/** Source Buffer Queue head. */
|
||||
al::deque<ALbufferQueueItem> mQueue;
|
||||
|
||||
std::atomic_flag PropsClean;
|
||||
bool mPropsDirty{true};
|
||||
|
||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||
* reset when looking up the voice.
|
||||
|
|
@ -127,6 +210,598 @@ struct ALsource {
|
|||
ALsource& operator=(const ALsource&) = delete;
|
||||
|
||||
DISABLE_ALLOC()
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
void eax_initialize(ALCcontext *context) noexcept;
|
||||
|
||||
|
||||
void eax_dispatch(const EaxEaxCall& eax_call)
|
||||
{ eax_call.is_get() ? eax_get(eax_call) : eax_set(eax_call); }
|
||||
|
||||
|
||||
void eax_update_filters();
|
||||
|
||||
void eax_update(
|
||||
EaxContextSharedDirtyFlags dirty_flags);
|
||||
|
||||
void eax_commit() { eax_apply_deferred(); }
|
||||
void eax_commit_and_update();
|
||||
|
||||
bool eax_is_initialized() const noexcept { return eax_al_context_; }
|
||||
|
||||
|
||||
static ALsource* eax_lookup_source(
|
||||
ALCcontext& al_context,
|
||||
ALuint source_id) noexcept;
|
||||
|
||||
|
||||
private:
|
||||
static constexpr auto eax_max_speakers = 9;
|
||||
|
||||
|
||||
using EaxActiveFxSlots = std::array<bool, EAX_MAX_FXSLOTS>;
|
||||
using EaxSpeakerLevels = std::array<long, eax_max_speakers>;
|
||||
|
||||
struct Eax
|
||||
{
|
||||
using Sends = std::array<EAXSOURCEALLSENDPROPERTIES, EAX_MAX_FXSLOTS>;
|
||||
|
||||
EAX50ACTIVEFXSLOTS active_fx_slots{};
|
||||
EAX50SOURCEPROPERTIES source{};
|
||||
Sends sends{};
|
||||
EaxSpeakerLevels speaker_levels{};
|
||||
}; // Eax
|
||||
|
||||
|
||||
bool eax_uses_primary_id_{};
|
||||
bool eax_has_active_fx_slots_{};
|
||||
bool eax_are_active_fx_slots_dirty_{};
|
||||
|
||||
ALCcontext* eax_al_context_{};
|
||||
|
||||
EAXBUFFER_REVERBPROPERTIES eax1_{};
|
||||
Eax eax_{};
|
||||
Eax eax_d_{};
|
||||
EaxActiveFxSlots eax_active_fx_slots_{};
|
||||
|
||||
EaxSourceSendsDirtyFlags eax_sends_dirty_flags_{};
|
||||
EaxSourceSourceFilterDirtyFlags eax_source_dirty_filter_flags_{};
|
||||
EaxSourceSourceMiscDirtyFlags eax_source_dirty_misc_flags_{};
|
||||
|
||||
|
||||
[[noreturn]]
|
||||
static void eax_fail(
|
||||
const char* message);
|
||||
|
||||
|
||||
void eax_set_source_defaults() noexcept;
|
||||
void eax_set_active_fx_slots_defaults() noexcept;
|
||||
void eax_set_send_defaults(EAXSOURCEALLSENDPROPERTIES& eax_send) noexcept;
|
||||
void eax_set_sends_defaults() noexcept;
|
||||
void eax_set_speaker_levels_defaults() noexcept;
|
||||
void eax_set_defaults() noexcept;
|
||||
|
||||
|
||||
static float eax_calculate_dst_occlusion_mb(
|
||||
long src_occlusion_mb,
|
||||
float path_ratio,
|
||||
float lf_ratio) noexcept;
|
||||
|
||||
EaxAlLowPassParam eax_create_direct_filter_param() const noexcept;
|
||||
|
||||
EaxAlLowPassParam eax_create_room_filter_param(
|
||||
const ALeffectslot& fx_slot,
|
||||
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept;
|
||||
|
||||
void eax_set_fx_slots();
|
||||
|
||||
void eax_initialize_fx_slots();
|
||||
|
||||
void eax_update_direct_filter_internal();
|
||||
|
||||
void eax_update_room_filters_internal();
|
||||
|
||||
void eax_update_filters_internal();
|
||||
|
||||
void eax_update_primary_fx_slot_id();
|
||||
|
||||
|
||||
void eax_defer_active_fx_slots(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
static const char* eax_get_exclusion_name() noexcept;
|
||||
|
||||
static const char* eax_get_exclusion_lf_ratio_name() noexcept;
|
||||
|
||||
|
||||
static const char* eax_get_occlusion_name() noexcept;
|
||||
|
||||
static const char* eax_get_occlusion_lf_ratio_name() noexcept;
|
||||
|
||||
static const char* eax_get_occlusion_direct_ratio_name() noexcept;
|
||||
|
||||
static const char* eax_get_occlusion_room_ratio_name() noexcept;
|
||||
|
||||
|
||||
static void eax1_validate_reverb_mix(float reverb_mix);
|
||||
|
||||
static void eax_validate_send_receiving_fx_slot_guid(
|
||||
const GUID& guidReceivingFXSlotID);
|
||||
|
||||
static void eax_validate_send_send(
|
||||
long lSend);
|
||||
|
||||
static void eax_validate_send_send_hf(
|
||||
long lSendHF);
|
||||
|
||||
static void eax_validate_send_occlusion(
|
||||
long lOcclusion);
|
||||
|
||||
static void eax_validate_send_occlusion_lf_ratio(
|
||||
float flOcclusionLFRatio);
|
||||
|
||||
static void eax_validate_send_occlusion_room_ratio(
|
||||
float flOcclusionRoomRatio);
|
||||
|
||||
static void eax_validate_send_occlusion_direct_ratio(
|
||||
float flOcclusionDirectRatio);
|
||||
|
||||
static void eax_validate_send_exclusion(
|
||||
long lExclusion);
|
||||
|
||||
static void eax_validate_send_exclusion_lf_ratio(
|
||||
float flExclusionLFRatio);
|
||||
|
||||
static void eax_validate_send(
|
||||
const EAXSOURCESENDPROPERTIES& all);
|
||||
|
||||
static void eax_validate_send_exclusion_all(
|
||||
const EAXSOURCEEXCLUSIONSENDPROPERTIES& all);
|
||||
|
||||
static void eax_validate_send_occlusion_all(
|
||||
const EAXSOURCEOCCLUSIONSENDPROPERTIES& all);
|
||||
|
||||
static void eax_validate_send_all(
|
||||
const EAXSOURCEALLSENDPROPERTIES& all);
|
||||
|
||||
|
||||
static EaxFxSlotIndexValue eax_get_send_index(
|
||||
const GUID& send_guid);
|
||||
|
||||
|
||||
void eax_defer_send_send(
|
||||
long lSend,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_send_hf(
|
||||
long lSendHF,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_occlusion(
|
||||
long lOcclusion,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_occlusion_lf_ratio(
|
||||
float flOcclusionLFRatio,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_occlusion_room_ratio(
|
||||
float flOcclusionRoomRatio,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_occlusion_direct_ratio(
|
||||
float flOcclusionDirectRatio,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_exclusion(
|
||||
long lExclusion,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_exclusion_lf_ratio(
|
||||
float flExclusionLFRatio,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send(
|
||||
const EAXSOURCESENDPROPERTIES& all,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_exclusion_all(
|
||||
const EAXSOURCEEXCLUSIONSENDPROPERTIES& all,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_occlusion_all(
|
||||
const EAXSOURCEOCCLUSIONSENDPROPERTIES& all,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
void eax_defer_send_all(
|
||||
const EAXSOURCEALLSENDPROPERTIES& all,
|
||||
EaxFxSlotIndexValue index);
|
||||
|
||||
|
||||
void eax_defer_send(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_send_exclusion_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_send_occlusion_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_send_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
static void eax_validate_source_direct(
|
||||
long direct);
|
||||
|
||||
static void eax_validate_source_direct_hf(
|
||||
long direct_hf);
|
||||
|
||||
static void eax_validate_source_room(
|
||||
long room);
|
||||
|
||||
static void eax_validate_source_room_hf(
|
||||
long room_hf);
|
||||
|
||||
static void eax_validate_source_obstruction(
|
||||
long obstruction);
|
||||
|
||||
static void eax_validate_source_obstruction_lf_ratio(
|
||||
float obstruction_lf_ratio);
|
||||
|
||||
static void eax_validate_source_occlusion(
|
||||
long occlusion);
|
||||
|
||||
static void eax_validate_source_occlusion_lf_ratio(
|
||||
float occlusion_lf_ratio);
|
||||
|
||||
static void eax_validate_source_occlusion_room_ratio(
|
||||
float occlusion_room_ratio);
|
||||
|
||||
static void eax_validate_source_occlusion_direct_ratio(
|
||||
float occlusion_direct_ratio);
|
||||
|
||||
static void eax_validate_source_exclusion(
|
||||
long exclusion);
|
||||
|
||||
static void eax_validate_source_exclusion_lf_ratio(
|
||||
float exclusion_lf_ratio);
|
||||
|
||||
static void eax_validate_source_outside_volume_hf(
|
||||
long outside_volume_hf);
|
||||
|
||||
static void eax_validate_source_doppler_factor(
|
||||
float doppler_factor);
|
||||
|
||||
static void eax_validate_source_rolloff_factor(
|
||||
float rolloff_factor);
|
||||
|
||||
static void eax_validate_source_room_rolloff_factor(
|
||||
float room_rolloff_factor);
|
||||
|
||||
static void eax_validate_source_air_absorption_factor(
|
||||
float air_absorption_factor);
|
||||
|
||||
static void eax_validate_source_flags(
|
||||
unsigned long flags,
|
||||
int eax_version);
|
||||
|
||||
static void eax_validate_source_macro_fx_factor(
|
||||
float macro_fx_factor);
|
||||
|
||||
static void eax_validate_source_2d_all(
|
||||
const EAXSOURCE2DPROPERTIES& all,
|
||||
int eax_version);
|
||||
|
||||
static void eax_validate_source_obstruction_all(
|
||||
const EAXOBSTRUCTIONPROPERTIES& all);
|
||||
|
||||
static void eax_validate_source_exclusion_all(
|
||||
const EAXEXCLUSIONPROPERTIES& all);
|
||||
|
||||
static void eax_validate_source_occlusion_all(
|
||||
const EAXOCCLUSIONPROPERTIES& all);
|
||||
|
||||
static void eax_validate_source_all(
|
||||
const EAX20BUFFERPROPERTIES& all,
|
||||
int eax_version);
|
||||
|
||||
static void eax_validate_source_all(
|
||||
const EAX30SOURCEPROPERTIES& all,
|
||||
int eax_version);
|
||||
|
||||
static void eax_validate_source_all(
|
||||
const EAX50SOURCEPROPERTIES& all,
|
||||
int eax_version);
|
||||
|
||||
static void eax_validate_source_speaker_id(
|
||||
long speaker_id);
|
||||
|
||||
static void eax_validate_source_speaker_level(
|
||||
long speaker_level);
|
||||
|
||||
static void eax_validate_source_speaker_level_all(
|
||||
const EAXSPEAKERLEVELPROPERTIES& all);
|
||||
|
||||
|
||||
void eax_defer_source_direct(
|
||||
long lDirect);
|
||||
|
||||
void eax_defer_source_direct_hf(
|
||||
long lDirectHF);
|
||||
|
||||
void eax_defer_source_room(
|
||||
long lRoom);
|
||||
|
||||
void eax_defer_source_room_hf(
|
||||
long lRoomHF);
|
||||
|
||||
void eax_defer_source_obstruction(
|
||||
long lObstruction);
|
||||
|
||||
void eax_defer_source_obstruction_lf_ratio(
|
||||
float flObstructionLFRatio);
|
||||
|
||||
void eax_defer_source_occlusion(
|
||||
long lOcclusion);
|
||||
|
||||
void eax_defer_source_occlusion_lf_ratio(
|
||||
float flOcclusionLFRatio);
|
||||
|
||||
void eax_defer_source_occlusion_room_ratio(
|
||||
float flOcclusionRoomRatio);
|
||||
|
||||
void eax_defer_source_occlusion_direct_ratio(
|
||||
float flOcclusionDirectRatio);
|
||||
|
||||
void eax_defer_source_exclusion(
|
||||
long lExclusion);
|
||||
|
||||
void eax_defer_source_exclusion_lf_ratio(
|
||||
float flExclusionLFRatio);
|
||||
|
||||
void eax_defer_source_outside_volume_hf(
|
||||
long lOutsideVolumeHF);
|
||||
|
||||
void eax_defer_source_doppler_factor(
|
||||
float flDopplerFactor);
|
||||
|
||||
void eax_defer_source_rolloff_factor(
|
||||
float flRolloffFactor);
|
||||
|
||||
void eax_defer_source_room_rolloff_factor(
|
||||
float flRoomRolloffFactor);
|
||||
|
||||
void eax_defer_source_air_absorption_factor(
|
||||
float flAirAbsorptionFactor);
|
||||
|
||||
void eax_defer_source_flags(
|
||||
unsigned long ulFlags);
|
||||
|
||||
void eax_defer_source_macro_fx_factor(
|
||||
float flMacroFXFactor);
|
||||
|
||||
void eax_defer_source_2d_all(
|
||||
const EAXSOURCE2DPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_obstruction_all(
|
||||
const EAXOBSTRUCTIONPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_exclusion_all(
|
||||
const EAXEXCLUSIONPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_occlusion_all(
|
||||
const EAXOCCLUSIONPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_all(
|
||||
const EAX20BUFFERPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_all(
|
||||
const EAX30SOURCEPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_all(
|
||||
const EAX50SOURCEPROPERTIES& all);
|
||||
|
||||
void eax_defer_source_speaker_level_all(
|
||||
const EAXSPEAKERLEVELPROPERTIES& all);
|
||||
|
||||
|
||||
void eax_defer_source_direct(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_direct_hf(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_room(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_room_hf(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_obstruction(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_obstruction_lf_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_occlusion(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_occlusion_lf_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_occlusion_room_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_occlusion_direct_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_exclusion(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_exclusion_lf_ratio(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_outside_volume_hf(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_doppler_factor(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_rolloff_factor(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_room_rolloff_factor(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_air_absorption_factor(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_flags(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_macro_fx_factor(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_2d_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_obstruction_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_exclusion_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_occlusion_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_defer_source_speaker_level_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
void eax_set_outside_volume_hf();
|
||||
|
||||
void eax_set_doppler_factor();
|
||||
|
||||
void eax_set_rolloff_factor();
|
||||
|
||||
void eax_set_room_rolloff_factor();
|
||||
|
||||
void eax_set_air_absorption_factor();
|
||||
|
||||
|
||||
void eax_set_direct_hf_auto_flag();
|
||||
|
||||
void eax_set_room_auto_flag();
|
||||
|
||||
void eax_set_room_hf_auto_flag();
|
||||
|
||||
void eax_set_flags();
|
||||
|
||||
|
||||
void eax_set_macro_fx_factor();
|
||||
|
||||
void eax_set_speaker_levels();
|
||||
|
||||
|
||||
void eax1_set_efx();
|
||||
void eax1_set_reverb_mix(const EaxEaxCall& eax_call);
|
||||
void eax1_set(const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_apply_deferred();
|
||||
|
||||
void eax_set(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
static const GUID& eax_get_send_fx_slot_guid(
|
||||
int eax_version,
|
||||
EaxFxSlotIndexValue fx_slot_index);
|
||||
|
||||
static void eax_copy_send(
|
||||
const EAXSOURCEALLSENDPROPERTIES& src_send,
|
||||
EAXSOURCESENDPROPERTIES& dst_send);
|
||||
|
||||
static void eax_copy_send(
|
||||
const EAXSOURCEALLSENDPROPERTIES& src_send,
|
||||
EAXSOURCEALLSENDPROPERTIES& dst_send);
|
||||
|
||||
static void eax_copy_send(
|
||||
const EAXSOURCEALLSENDPROPERTIES& src_send,
|
||||
EAXSOURCEOCCLUSIONSENDPROPERTIES& dst_send);
|
||||
|
||||
static void eax_copy_send(
|
||||
const EAXSOURCEALLSENDPROPERTIES& src_send,
|
||||
EAXSOURCEEXCLUSIONSENDPROPERTIES& dst_send);
|
||||
|
||||
template<
|
||||
typename TException,
|
||||
typename TSrcSend
|
||||
>
|
||||
void eax_api_get_send_properties(
|
||||
const EaxEaxCall& eax_call) const
|
||||
{
|
||||
const auto eax_version = eax_call.get_version();
|
||||
const auto dst_sends = eax_call.get_values<TException, TSrcSend>();
|
||||
const auto send_count = dst_sends.size();
|
||||
|
||||
for (auto fx_slot_index = EaxFxSlotIndexValue{}; fx_slot_index < send_count; ++fx_slot_index)
|
||||
{
|
||||
auto& dst_send = dst_sends[fx_slot_index];
|
||||
const auto& src_send = eax_.sends[fx_slot_index];
|
||||
|
||||
eax_copy_send(src_send, dst_send);
|
||||
|
||||
dst_send.guidReceivingFXSlotID = eax_get_send_fx_slot_guid(eax_version, fx_slot_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void eax1_get(const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_v2(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_v3(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_v5(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_obstruction(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_occlusion(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_exclusion(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_active_fx_slot_id(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_all_2d(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_api_get_source_speaker_level_all(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
void eax_get(
|
||||
const EaxEaxCall& eax_call);
|
||||
|
||||
|
||||
// `alSource3i(source, AL_AUXILIARY_SEND_FILTER, ...)`
|
||||
void eax_set_al_source_send(ALeffectslot *slot, size_t sendidx,
|
||||
const EaxAlLowPassParam &filter);
|
||||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
void UpdateAllSourceProps(ALCcontext *context);
|
||||
|
|
|
|||
|
|
@ -24,26 +24,34 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alu.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/context.h"
|
||||
#include "core/except.h"
|
||||
#include "event.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "core/voice.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "strutils.h"
|
||||
#include "voice.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alc/device.h"
|
||||
|
||||
#include "eax_globals.h"
|
||||
#include "eax_x_ram.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -129,7 +137,7 @@ ALenum ALenumFromDistanceModel(DistanceModel model)
|
|||
/* WARNING: Non-standard export! Not part of any extension, or exposed in the
|
||||
* alcFunctions list.
|
||||
*/
|
||||
extern "C" AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
|
||||
AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
|
||||
START_API_FUNC
|
||||
{
|
||||
static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
|
||||
|
|
@ -139,10 +147,10 @@ START_API_FUNC
|
|||
END_API_FUNC
|
||||
|
||||
#define DO_UPDATEPROPS() do { \
|
||||
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
||||
if(!context->mDeferUpdates) \
|
||||
UpdateContextProps(context.get()); \
|
||||
else \
|
||||
context->mPropsClean.clear(std::memory_order_release); \
|
||||
context->mPropsDirty = true; \
|
||||
} while(0)
|
||||
|
||||
|
||||
|
|
@ -152,12 +160,18 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(capability)
|
||||
{
|
||||
case AL_SOURCE_DISTANCE_MODEL:
|
||||
context->mSourceDistanceModel = true;
|
||||
DO_UPDATEPROPS();
|
||||
{
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
context->mSourceDistanceModel = true;
|
||||
DO_UPDATEPROPS();
|
||||
}
|
||||
break;
|
||||
|
||||
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||
context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -172,12 +186,18 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(capability)
|
||||
{
|
||||
case AL_SOURCE_DISTANCE_MODEL:
|
||||
context->mSourceDistanceModel = false;
|
||||
DO_UPDATEPROPS();
|
||||
{
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
context->mSourceDistanceModel = false;
|
||||
DO_UPDATEPROPS();
|
||||
}
|
||||
break;
|
||||
|
||||
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||
context->mStopVoicesOnDisconnect = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -200,6 +220,10 @@ START_API_FUNC
|
|||
value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
|
||||
break;
|
||||
|
||||
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||
value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
|
||||
}
|
||||
|
|
@ -239,7 +263,7 @@ START_API_FUNC
|
|||
break;
|
||||
|
||||
case AL_DEFERRED_UPDATES_SOFT:
|
||||
if(context->mDeferUpdates.load(std::memory_order_acquire))
|
||||
if(context->mDeferUpdates)
|
||||
value = AL_TRUE;
|
||||
break;
|
||||
|
||||
|
|
@ -292,7 +316,7 @@ START_API_FUNC
|
|||
break;
|
||||
|
||||
case AL_DEFERRED_UPDATES_SOFT:
|
||||
if(context->mDeferUpdates.load(std::memory_order_acquire))
|
||||
if(context->mDeferUpdates)
|
||||
value = static_cast<ALdouble>(AL_TRUE);
|
||||
break;
|
||||
|
||||
|
|
@ -343,7 +367,7 @@ START_API_FUNC
|
|||
break;
|
||||
|
||||
case AL_DEFERRED_UPDATES_SOFT:
|
||||
if(context->mDeferUpdates.load(std::memory_order_acquire))
|
||||
if(context->mDeferUpdates)
|
||||
value = static_cast<ALfloat>(AL_TRUE);
|
||||
break;
|
||||
|
||||
|
|
@ -394,7 +418,7 @@ START_API_FUNC
|
|||
break;
|
||||
|
||||
case AL_DEFERRED_UPDATES_SOFT:
|
||||
if(context->mDeferUpdates.load(std::memory_order_acquire))
|
||||
if(context->mDeferUpdates)
|
||||
value = AL_TRUE;
|
||||
break;
|
||||
|
||||
|
|
@ -410,6 +434,41 @@ START_API_FUNC
|
|||
value = static_cast<int>(ResamplerDefault);
|
||||
break;
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
#define EAX_ERROR "[alGetInteger] EAX not enabled."
|
||||
|
||||
case AL_EAX_RAM_SIZE:
|
||||
if (eax_g_is_enabled)
|
||||
{
|
||||
value = eax_x_ram_max_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_ERROR);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AL_EAX_RAM_FREE:
|
||||
if (eax_g_is_enabled)
|
||||
{
|
||||
auto device = context->mALDevice.get();
|
||||
std::lock_guard<std::mutex> device_lock{device->BufferLock};
|
||||
|
||||
value = static_cast<ALint>(device->eax_x_ram_free_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, EAX_ERROR);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#undef EAX_ERROR
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname);
|
||||
}
|
||||
|
|
@ -418,7 +477,7 @@ START_API_FUNC
|
|||
}
|
||||
END_API_FUNC
|
||||
|
||||
extern "C" AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
|
||||
AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
|
|
@ -445,7 +504,7 @@ START_API_FUNC
|
|||
break;
|
||||
|
||||
case AL_DEFERRED_UPDATES_SOFT:
|
||||
if(context->mDeferUpdates.load(std::memory_order_acquire))
|
||||
if(context->mDeferUpdates)
|
||||
value = AL_TRUE;
|
||||
break;
|
||||
|
||||
|
|
@ -627,7 +686,7 @@ START_API_FUNC
|
|||
}
|
||||
END_API_FUNC
|
||||
|
||||
extern "C" AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
|
||||
AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
if(values)
|
||||
|
|
@ -819,6 +878,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
context->deferUpdates();
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
@ -829,6 +889,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
context->processUpdates();
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
@ -874,6 +935,14 @@ void UpdateContextProps(ALCcontext *context)
|
|||
}
|
||||
|
||||
/* Copy in current property values. */
|
||||
ALlistener &listener = context->mListener;
|
||||
props->Position = listener.Position;
|
||||
props->Velocity = listener.Velocity;
|
||||
props->OrientAt = listener.OrientAt;
|
||||
props->OrientUp = listener.OrientUp;
|
||||
props->Gain = listener.Gain;
|
||||
props->MetersPerUnit = listener.mMetersPerUnit;
|
||||
|
||||
props->DopplerFactor = context->mDopplerFactor;
|
||||
props->DopplerVelocity = context->mDopplerVelocity;
|
||||
props->SpeedOfSound = context->mSpeedOfSound;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include "alfstream.h"
|
||||
#include "alstring.h"
|
||||
#include "compat.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "strutils.h"
|
||||
#include "vector.h"
|
||||
|
|
@ -277,6 +277,52 @@ void LoadConfigFromFile(std::istream &f)
|
|||
ConfOpts.shrink_to_fit();
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
if(!keyName)
|
||||
return nullptr;
|
||||
|
||||
std::string key;
|
||||
if(blockName && al::strcasecmp(blockName, "general") != 0)
|
||||
{
|
||||
key = blockName;
|
||||
if(devName)
|
||||
{
|
||||
key += '/';
|
||||
key += devName;
|
||||
}
|
||||
key += '/';
|
||||
key += keyName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(devName)
|
||||
{
|
||||
key = devName;
|
||||
key += '/';
|
||||
}
|
||||
key += keyName;
|
||||
}
|
||||
|
||||
auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
|
||||
[&key](const ConfigEntry &entry) -> bool
|
||||
{ return entry.key == key; });
|
||||
if(iter != ConfOpts.cend())
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||
if(!iter->value.empty())
|
||||
return iter->value.c_str();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
return GetConfigValue(nullptr, blockName, keyName);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
|
@ -437,106 +483,46 @@ void ReadALConfig()
|
|||
}
|
||||
#endif
|
||||
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
if(!keyName)
|
||||
return def;
|
||||
|
||||
std::string key;
|
||||
if(blockName && al::strcasecmp(blockName, "general") != 0)
|
||||
{
|
||||
key = blockName;
|
||||
if(devName)
|
||||
{
|
||||
key += '/';
|
||||
key += devName;
|
||||
}
|
||||
key += '/';
|
||||
key += keyName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(devName)
|
||||
{
|
||||
key = devName;
|
||||
key += '/';
|
||||
}
|
||||
key += keyName;
|
||||
}
|
||||
|
||||
auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
|
||||
[&key](const ConfigEntry &entry) -> bool
|
||||
{ return entry.key == key; }
|
||||
);
|
||||
if(iter != ConfOpts.cend())
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||
if(!iter->value.empty())
|
||||
return iter->value.c_str();
|
||||
return def;
|
||||
}
|
||||
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key.c_str());
|
||||
return def;
|
||||
}
|
||||
return GetConfigValue(nullptr, blockName, keyName, def);
|
||||
}
|
||||
|
||||
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
return val[0] != 0;
|
||||
}
|
||||
|
||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return al::nullopt;
|
||||
|
||||
return al::make_optional<std::string>(val);
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::make_optional<std::string>(val);
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return al::nullopt;
|
||||
|
||||
return al::make_optional(static_cast<int>(std::strtol(val, nullptr, 0)));
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::make_optional(static_cast<int>(std::strtol(val, nullptr, 0)));
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return al::nullopt;
|
||||
|
||||
return al::make_optional(static_cast<unsigned int>(std::strtoul(val, nullptr, 0)));
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::make_optional(static_cast<unsigned int>(std::strtoul(val, nullptr, 0)));
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return al::nullopt;
|
||||
|
||||
return al::make_optional(std::strtof(val, nullptr));
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::make_optional(std::strtof(val, nullptr));
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return al::nullopt;
|
||||
|
||||
return al::make_optional(
|
||||
al::strcasecmp(val, "true") == 0 || al::strcasecmp(val, "yes") == 0 ||
|
||||
al::strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::make_optional(al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true")==0 || atoi(val) != 0);
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
|
||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return def != 0;
|
||||
return (al::strcasecmp(val, "true") == 0 || al::strcasecmp(val, "yes") == 0 ||
|
||||
al::strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0);
|
||||
return def;
|
||||
}
|
||||
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
void ReadALConfig();
|
||||
|
||||
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName);
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def);
|
||||
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def);
|
||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def);
|
||||
|
||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
|
||||
File diff suppressed because it is too large
Load diff
38
Engine/lib/openal-soft/alc/alu.h
Normal file
38
Engine/lib/openal-soft/alc/alu.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef ALU_H
|
||||
#define ALU_H
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "aloptional.h"
|
||||
|
||||
struct ALCcontext;
|
||||
struct ALCdevice;
|
||||
struct EffectSlot;
|
||||
|
||||
enum class StereoEncoding : unsigned char;
|
||||
|
||||
|
||||
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
||||
|
||||
|
||||
enum CompatFlags : uint8_t {
|
||||
ReverseX,
|
||||
ReverseY,
|
||||
ReverseZ,
|
||||
|
||||
Count
|
||||
};
|
||||
using CompatFlagBitset = std::bitset<CompatFlags::Count>;
|
||||
|
||||
void aluInit(CompatFlagBitset flags);
|
||||
|
||||
/* aluInitRenderer
|
||||
*
|
||||
* Set up the appropriate panning method and mixing method given the device
|
||||
* properties.
|
||||
*/
|
||||
void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode);
|
||||
|
||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
||||
|
||||
#endif
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/alsa.h"
|
||||
#include "alsa.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
|
@ -36,12 +36,12 @@
|
|||
#include <utility>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
|
@ -68,35 +68,37 @@ constexpr char alsaDevice[] = "ALSA Default";
|
|||
MAGIC(snd_pcm_hw_params_free); \
|
||||
MAGIC(snd_pcm_hw_params_any); \
|
||||
MAGIC(snd_pcm_hw_params_current); \
|
||||
MAGIC(snd_pcm_hw_params_get_access); \
|
||||
MAGIC(snd_pcm_hw_params_get_buffer_size); \
|
||||
MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
|
||||
MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
|
||||
MAGIC(snd_pcm_hw_params_get_channels); \
|
||||
MAGIC(snd_pcm_hw_params_get_period_size); \
|
||||
MAGIC(snd_pcm_hw_params_get_period_time_max); \
|
||||
MAGIC(snd_pcm_hw_params_get_period_time_min); \
|
||||
MAGIC(snd_pcm_hw_params_get_periods); \
|
||||
MAGIC(snd_pcm_hw_params_set_access); \
|
||||
MAGIC(snd_pcm_hw_params_set_format); \
|
||||
MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
|
||||
MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_channels); \
|
||||
MAGIC(snd_pcm_hw_params_set_channels_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_format); \
|
||||
MAGIC(snd_pcm_hw_params_set_period_time_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_period_size_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_periods_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_rate_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_rate); \
|
||||
MAGIC(snd_pcm_hw_params_set_rate_resample); \
|
||||
MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_period_time_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_period_size_near); \
|
||||
MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
|
||||
MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
|
||||
MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
|
||||
MAGIC(snd_pcm_hw_params_get_period_time_min); \
|
||||
MAGIC(snd_pcm_hw_params_get_period_time_max); \
|
||||
MAGIC(snd_pcm_hw_params_get_buffer_size); \
|
||||
MAGIC(snd_pcm_hw_params_get_period_size); \
|
||||
MAGIC(snd_pcm_hw_params_get_access); \
|
||||
MAGIC(snd_pcm_hw_params_get_periods); \
|
||||
MAGIC(snd_pcm_hw_params_test_format); \
|
||||
MAGIC(snd_pcm_hw_params_test_channels); \
|
||||
MAGIC(snd_pcm_hw_params); \
|
||||
MAGIC(snd_pcm_sw_params_malloc); \
|
||||
MAGIC(snd_pcm_sw_params); \
|
||||
MAGIC(snd_pcm_sw_params_current); \
|
||||
MAGIC(snd_pcm_sw_params_free); \
|
||||
MAGIC(snd_pcm_sw_params_malloc); \
|
||||
MAGIC(snd_pcm_sw_params_set_avail_min); \
|
||||
MAGIC(snd_pcm_sw_params_set_stop_threshold); \
|
||||
MAGIC(snd_pcm_sw_params); \
|
||||
MAGIC(snd_pcm_sw_params_free); \
|
||||
MAGIC(snd_pcm_prepare); \
|
||||
MAGIC(snd_pcm_start); \
|
||||
MAGIC(snd_pcm_resume); \
|
||||
|
|
@ -105,7 +107,6 @@ constexpr char alsaDevice[] = "ALSA Default";
|
|||
MAGIC(snd_pcm_delay); \
|
||||
MAGIC(snd_pcm_state); \
|
||||
MAGIC(snd_pcm_avail_update); \
|
||||
MAGIC(snd_pcm_areas_silence); \
|
||||
MAGIC(snd_pcm_mmap_begin); \
|
||||
MAGIC(snd_pcm_mmap_commit); \
|
||||
MAGIC(snd_pcm_readi); \
|
||||
|
|
@ -150,6 +151,7 @@ ALSA_FUNCS(MAKE_FUNC);
|
|||
#define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
|
||||
#define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
|
||||
#define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
|
||||
#define snd_pcm_hw_params_set_channels_near psnd_pcm_hw_params_set_channels_near
|
||||
#define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
|
||||
#define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
|
||||
#define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
|
||||
|
|
@ -167,6 +169,7 @@ ALSA_FUNCS(MAKE_FUNC);
|
|||
#define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
|
||||
#define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
|
||||
#define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
|
||||
#define snd_pcm_hw_params_get_channels psnd_pcm_hw_params_get_channels
|
||||
#define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
|
||||
#define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
|
||||
#define snd_pcm_hw_params psnd_pcm_hw_params
|
||||
|
|
@ -184,7 +187,6 @@ ALSA_FUNCS(MAKE_FUNC);
|
|||
#define snd_pcm_delay psnd_pcm_delay
|
||||
#define snd_pcm_state psnd_pcm_state
|
||||
#define snd_pcm_avail_update psnd_pcm_avail_update
|
||||
#define snd_pcm_areas_silence psnd_pcm_areas_silence
|
||||
#define snd_pcm_mmap_begin psnd_pcm_mmap_begin
|
||||
#define snd_pcm_mmap_commit psnd_pcm_mmap_commit
|
||||
#define snd_pcm_readi psnd_pcm_readi
|
||||
|
|
@ -260,29 +262,36 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
snd_pcm_info_t *pcminfo;
|
||||
snd_pcm_info_malloc(&pcminfo);
|
||||
|
||||
devlist.emplace_back(DevMap{alsaDevice,
|
||||
GetConfigValue(nullptr, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture",
|
||||
"default")});
|
||||
auto defname = ConfigValueStr(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture");
|
||||
devlist.emplace_back(DevMap{alsaDevice, defname ? defname->c_str() : "default"});
|
||||
|
||||
const char *customdevs{GetConfigValue(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures", "")};
|
||||
while(const char *curdev{customdevs})
|
||||
if(auto customdevs = ConfigValueStr(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures"))
|
||||
{
|
||||
if(!curdev[0]) break;
|
||||
customdevs = strchr(curdev, ';');
|
||||
const char *sep{strchr(curdev, '=')};
|
||||
if(!sep)
|
||||
size_t nextpos{customdevs->find_first_not_of(';')};
|
||||
size_t curpos;
|
||||
while((curpos=nextpos) < customdevs->length())
|
||||
{
|
||||
std::string spec{customdevs ? std::string(curdev, customdevs++) : std::string(curdev)};
|
||||
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
||||
continue;
|
||||
}
|
||||
nextpos = customdevs->find_first_of(';', curpos+1);
|
||||
|
||||
const char *oldsep{sep++};
|
||||
devlist.emplace_back(DevMap{std::string(curdev, oldsep),
|
||||
customdevs ? std::string(sep, customdevs++) : std::string(sep)});
|
||||
const auto &entry = devlist.back();
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
size_t seppos{customdevs->find_first_of('=', curpos)};
|
||||
if(seppos == curpos || seppos >= nextpos)
|
||||
{
|
||||
std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
||||
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
devlist.emplace_back(DevMap{customdevs->substr(curpos, seppos-curpos),
|
||||
customdevs->substr(seppos+1, nextpos-seppos-1)});
|
||||
const auto &entry = devlist.back();
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
}
|
||||
|
||||
if(nextpos < customdevs->length())
|
||||
nextpos = customdevs->find_first_not_of(';', nextpos+1);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string main_prefix{
|
||||
|
|
@ -407,7 +416,7 @@ int verify_state(snd_pcm_t *handle)
|
|||
|
||||
|
||||
struct AlsaPlayback final : public BackendBase {
|
||||
AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~AlsaPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
|
@ -424,6 +433,7 @@ struct AlsaPlayback final : public BackendBase {
|
|||
|
||||
std::mutex mMutex;
|
||||
|
||||
uint mFrameStep{};
|
||||
al::vector<al::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
|
|
@ -445,7 +455,6 @@ int AlsaPlayback::mixerProc()
|
|||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
|
||||
const size_t samplebits{mDevice->bytesFromFmt() * 8};
|
||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||
while(!mKillNow.load(std::memory_order_acquire))
|
||||
|
|
@ -507,7 +516,7 @@ int AlsaPlayback::mixerProc()
|
|||
}
|
||||
|
||||
char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(frames), areas->step/samplebits);
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep);
|
||||
|
||||
snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)};
|
||||
if(commitres < 0 || static_cast<snd_pcm_uframes_t>(commitres) != frames)
|
||||
|
|
@ -529,7 +538,6 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
|
||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||
while(!mKillNow.load(std::memory_order_acquire))
|
||||
|
|
@ -575,7 +583,7 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
al::byte *WritePtr{mBuffer.data()};
|
||||
avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(avail), frame_step);
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep);
|
||||
while(avail > 0)
|
||||
{
|
||||
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr,
|
||||
|
|
@ -615,16 +623,15 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
|
||||
void AlsaPlayback::open(const char *name)
|
||||
{
|
||||
const char *driver{};
|
||||
al::optional<std::string> driveropt;
|
||||
const char *driver{"default"};
|
||||
if(name)
|
||||
{
|
||||
if(PlaybackDevices.empty())
|
||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||
|
||||
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
|
||||
[name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name; }
|
||||
);
|
||||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
|
|
@ -633,14 +640,19 @@ void AlsaPlayback::open(const char *name)
|
|||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
driver = GetConfigValue(nullptr, "alsa", "device", "default");
|
||||
if(bool{driveropt = ConfigValueStr(nullptr, "alsa", "device")})
|
||||
driver = driveropt->c_str();
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver);
|
||||
int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
|
||||
|
||||
snd_pcm_t *pcmHandle{};
|
||||
int err{snd_pcm_open(&pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
|
||||
if(err < 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not open ALSA device \"%s\"", driver};
|
||||
if(mPcmHandle)
|
||||
snd_pcm_close(mPcmHandle);
|
||||
mPcmHandle = pcmHandle;
|
||||
|
||||
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
|
||||
snd_config_update_free_global();
|
||||
|
|
@ -723,37 +735,25 @@ bool AlsaPlayback::reset()
|
|||
}
|
||||
}
|
||||
CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp.get(), format));
|
||||
/* test and set channels (implicitly sets frame bits) */
|
||||
if(snd_pcm_hw_params_test_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()) < 0)
|
||||
/* set channels (implicitly sets frame bits) */
|
||||
if(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()) < 0)
|
||||
{
|
||||
static const DevFmtChannels channellist[] = {
|
||||
DevFmtStereo,
|
||||
DevFmtQuad,
|
||||
DevFmtX51,
|
||||
DevFmtX71,
|
||||
DevFmtMono,
|
||||
};
|
||||
|
||||
for(const auto &chan : channellist)
|
||||
{
|
||||
if(snd_pcm_hw_params_test_channels(mPcmHandle, hp.get(), ChannelsFromDevFmt(chan, 0)) >= 0)
|
||||
{
|
||||
mDevice->FmtChans = chan;
|
||||
mDevice->mAmbiOrder = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint numchans{2u};
|
||||
CHECK(snd_pcm_hw_params_set_channels_near(mPcmHandle, hp.get(), &numchans));
|
||||
if(numchans < 1)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Got 0 device channels"};
|
||||
if(numchans == 1) mDevice->FmtChans = DevFmtMono;
|
||||
else mDevice->FmtChans = DevFmtStereo;
|
||||
}
|
||||
CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()));
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", 0)
|
||||
|| !mDevice->Flags.test(FrequencyRequest))
|
||||
{
|
||||
if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0)
|
||||
ERR("Failed to disable ALSA resampler\n");
|
||||
WARN("Failed to disable ALSA resampler\n");
|
||||
}
|
||||
else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 1) < 0)
|
||||
ERR("Failed to enable ALSA resampler\n");
|
||||
WARN("Failed to enable ALSA resampler\n");
|
||||
CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr));
|
||||
/* set period time (implicitly constrains period/buffer parameters) */
|
||||
if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0)
|
||||
|
|
@ -772,6 +772,7 @@ bool AlsaPlayback::reset()
|
|||
CHECK(snd_pcm_hw_params_get_access(hp.get(), &access));
|
||||
CHECK(snd_pcm_hw_params_get_period_size(hp.get(), &periodSizeInFrames, nullptr));
|
||||
CHECK(snd_pcm_hw_params_get_buffer_size(hp.get(), &bufferSizeInFrames));
|
||||
CHECK(snd_pcm_hw_params_get_channels(hp.get(), &mFrameStep));
|
||||
hp = nullptr;
|
||||
|
||||
SwParamsPtr sp{CreateSwParams()};
|
||||
|
|
@ -809,8 +810,8 @@ void AlsaPlayback::start()
|
|||
int (AlsaPlayback::*thread_func)(){};
|
||||
if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
|
||||
{
|
||||
mBuffer.resize(
|
||||
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize)));
|
||||
auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize);
|
||||
mBuffer.resize(static_cast<size_t>(datalen));
|
||||
thread_func = &AlsaPlayback::mixerNoMMapProc;
|
||||
}
|
||||
else
|
||||
|
|
@ -863,7 +864,7 @@ ClockLatency AlsaPlayback::getClockLatency()
|
|||
|
||||
|
||||
struct AlsaCapture final : public BackendBase {
|
||||
AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~AlsaCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
|
|
@ -895,16 +896,15 @@ AlsaCapture::~AlsaCapture()
|
|||
|
||||
void AlsaCapture::open(const char *name)
|
||||
{
|
||||
const char *driver{};
|
||||
al::optional<std::string> driveropt;
|
||||
const char *driver{"default"};
|
||||
if(name)
|
||||
{
|
||||
if(CaptureDevices.empty())
|
||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||
|
||||
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
|
||||
[name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name; }
|
||||
);
|
||||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
|
|
@ -913,7 +913,8 @@ void AlsaCapture::open(const char *name)
|
|||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
driver = GetConfigValue(nullptr, "alsa", "capture", "default");
|
||||
if(bool{driveropt = ConfigValueStr(nullptr, "alsa", "capture")})
|
||||
driver = driveropt->c_str();
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver);
|
||||
|
|
@ -1252,7 +1253,7 @@ std::string AlsaBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr AlsaBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr AlsaBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new AlsaPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_ALSA_H
|
||||
#define BACKENDS_ALSA_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct AlsaBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -3,21 +3,22 @@
|
|||
|
||||
#include "base.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
#endif
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/logging.h"
|
||||
#include "aloptional.h"
|
||||
#endif
|
||||
|
||||
#include "atomic.h"
|
||||
#include "core/devformat.h"
|
||||
|
||||
|
||||
bool BackendBase::reset()
|
||||
|
|
@ -78,14 +79,6 @@ void BackendBase::setDefaultWFXChannelOrder()
|
|||
mDevice->RealOut.ChannelIndex[SideLeft] = 4;
|
||||
mDevice->RealOut.ChannelIndex[SideRight] = 5;
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
mDevice->RealOut.ChannelIndex[FrontCenter] = 2;
|
||||
mDevice->RealOut.ChannelIndex[LFE] = 3;
|
||||
mDevice->RealOut.ChannelIndex[BackLeft] = 4;
|
||||
mDevice->RealOut.ChannelIndex[BackRight] = 5;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
|
|
@ -116,11 +109,11 @@ void BackendBase::setDefaultChannelOrder()
|
|||
|
||||
switch(mDevice->FmtChans)
|
||||
{
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX51:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
mDevice->RealOut.ChannelIndex[BackLeft] = 2;
|
||||
mDevice->RealOut.ChannelIndex[BackRight] = 3;
|
||||
mDevice->RealOut.ChannelIndex[SideLeft] = 2;
|
||||
mDevice->RealOut.ChannelIndex[SideRight] = 3;
|
||||
mDevice->RealOut.ChannelIndex[FrontCenter] = 4;
|
||||
mDevice->RealOut.ChannelIndex[LFE] = 5;
|
||||
return;
|
||||
|
|
@ -139,7 +132,6 @@ void BackendBase::setDefaultChannelOrder()
|
|||
case DevFmtMono:
|
||||
case DevFmtStereo:
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX61:
|
||||
case DevFmtAmbi3D:
|
||||
setDefaultWFXChannelOrder();
|
||||
|
|
@ -150,6 +142,13 @@ void BackendBase::setDefaultChannelOrder()
|
|||
#ifdef _WIN32
|
||||
void BackendBase::setChannelOrderFromWFXMask(uint chanmask)
|
||||
{
|
||||
static constexpr uint x51{SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER
|
||||
| SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT};
|
||||
static constexpr uint x51rear{SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER
|
||||
| SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT};
|
||||
/* Swap a 5.1 mask using the back channels for one with the sides. */
|
||||
if(chanmask == x51rear) chanmask = x51;
|
||||
|
||||
auto get_channel = [](const DWORD chanbit) noexcept -> al::optional<Channel>
|
||||
{
|
||||
switch(chanbit)
|
||||
|
|
@ -2,12 +2,13 @@
|
|||
#define ALC_BACKENDS_BASE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "core/device.h"
|
||||
#include "core/except.h"
|
||||
|
||||
|
||||
|
|
@ -30,9 +31,9 @@ struct BackendBase {
|
|||
|
||||
virtual ClockLatency getClockLatency();
|
||||
|
||||
ALCdevice *const mDevice;
|
||||
DeviceBase *const mDevice;
|
||||
|
||||
BackendBase(ALCdevice *device) noexcept : mDevice{device} { }
|
||||
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||
virtual ~BackendBase() = default;
|
||||
|
||||
protected:
|
||||
|
|
@ -57,7 +58,7 @@ enum class BackendType {
|
|||
/* Helper to get the current clock time from the device's ClockBase, and
|
||||
* SamplesDone converted from the sample rate.
|
||||
*/
|
||||
inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
|
||||
inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device)
|
||||
{
|
||||
using std::chrono::seconds;
|
||||
using std::chrono::nanoseconds;
|
||||
|
|
@ -69,9 +70,8 @@ inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
|
|||
/* Helper to get the device latency from the backend, including any fixed
|
||||
* latency from post-processing.
|
||||
*/
|
||||
inline ClockLatency GetClockLatency(ALCdevice *device)
|
||||
inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend)
|
||||
{
|
||||
BackendBase *backend{device->Backend.get()};
|
||||
ClockLatency ret{backend->getClockLatency()};
|
||||
ret.Latency += device->FixedLatency;
|
||||
return ret;
|
||||
|
|
@ -85,7 +85,7 @@ struct BackendFactory {
|
|||
|
||||
virtual std::string probe(BackendType type) = 0;
|
||||
|
||||
virtual BackendPtr createBackend(ALCdevice *device, BackendType type) = 0;
|
||||
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~BackendFactory() = default;
|
||||
|
|
@ -103,7 +103,11 @@ class backend_exception final : public base_exception {
|
|||
backend_error mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
|
|
@ -20,34 +20,239 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/coreaudio.h"
|
||||
#include "coreaudio.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "converter.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/converter.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "backends/base.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static const char ca_device[] = "CoreAudio Default";
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#define CAN_ENUMERATE 0
|
||||
#else
|
||||
#define CAN_ENUMERATE 1
|
||||
#endif
|
||||
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
struct DeviceEntry {
|
||||
AudioDeviceID mId;
|
||||
std::string mName;
|
||||
};
|
||||
|
||||
std::vector<DeviceEntry> PlaybackList;
|
||||
std::vector<DeviceEntry> CaptureList;
|
||||
|
||||
|
||||
OSStatus GetHwProperty(AudioHardwarePropertyID propId, UInt32 dataSize, void *propData)
|
||||
{
|
||||
const AudioObjectPropertyAddress addr{propId, kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster};
|
||||
return AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, nullptr, &dataSize,
|
||||
propData);
|
||||
}
|
||||
|
||||
OSStatus GetHwPropertySize(AudioHardwarePropertyID propId, UInt32 *outSize)
|
||||
{
|
||||
const AudioObjectPropertyAddress addr{propId, kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster};
|
||||
return AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, nullptr, outSize);
|
||||
}
|
||||
|
||||
OSStatus GetDevProperty(AudioDeviceID devId, AudioDevicePropertyID propId, bool isCapture,
|
||||
UInt32 elem, UInt32 dataSize, void *propData)
|
||||
{
|
||||
static const AudioObjectPropertyScope scopes[2]{kAudioDevicePropertyScopeOutput,
|
||||
kAudioDevicePropertyScopeInput};
|
||||
const AudioObjectPropertyAddress addr{propId, scopes[isCapture], elem};
|
||||
return AudioObjectGetPropertyData(devId, &addr, 0, nullptr, &dataSize, propData);
|
||||
}
|
||||
|
||||
OSStatus GetDevPropertySize(AudioDeviceID devId, AudioDevicePropertyID inPropertyID,
|
||||
bool isCapture, UInt32 elem, UInt32 *outSize)
|
||||
{
|
||||
static const AudioObjectPropertyScope scopes[2]{kAudioDevicePropertyScopeOutput,
|
||||
kAudioDevicePropertyScopeInput};
|
||||
const AudioObjectPropertyAddress addr{inPropertyID, scopes[isCapture], elem};
|
||||
return AudioObjectGetPropertyDataSize(devId, &addr, 0, nullptr, outSize);
|
||||
}
|
||||
|
||||
|
||||
std::string GetDeviceName(AudioDeviceID devId)
|
||||
{
|
||||
std::string devname;
|
||||
CFStringRef nameRef;
|
||||
|
||||
/* Try to get the device name as a CFString, for Unicode name support. */
|
||||
OSStatus err{GetDevProperty(devId, kAudioDevicePropertyDeviceNameCFString, false, 0,
|
||||
sizeof(nameRef), &nameRef)};
|
||||
if(err == noErr)
|
||||
{
|
||||
const CFIndex propSize{CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef),
|
||||
kCFStringEncodingUTF8)};
|
||||
devname.resize(static_cast<size_t>(propSize)+1, '\0');
|
||||
|
||||
CFStringGetCString(nameRef, &devname[0], propSize+1, kCFStringEncodingUTF8);
|
||||
CFRelease(nameRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If that failed, just get the C string. Hopefully there's nothing bad
|
||||
* with this.
|
||||
*/
|
||||
UInt32 propSize{};
|
||||
if(GetDevPropertySize(devId, kAudioDevicePropertyDeviceName, false, 0, &propSize))
|
||||
return devname;
|
||||
|
||||
devname.resize(propSize+1, '\0');
|
||||
if(GetDevProperty(devId, kAudioDevicePropertyDeviceName, false, 0, propSize, &devname[0]))
|
||||
{
|
||||
devname.clear();
|
||||
return devname;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear extraneous nul chars that may have been written with the name
|
||||
* string, and return it.
|
||||
*/
|
||||
while(!devname.back())
|
||||
devname.pop_back();
|
||||
return devname;
|
||||
}
|
||||
|
||||
UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
||||
{
|
||||
UInt32 propSize{};
|
||||
auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0,
|
||||
&propSize);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto buflist_data = std::make_unique<char[]>(propSize);
|
||||
auto *buflist = reinterpret_cast<AudioBufferList*>(buflist_data.get());
|
||||
|
||||
err = GetDevProperty(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, propSize,
|
||||
buflist);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
UInt32 numChannels{0};
|
||||
for(size_t i{0};i < buflist->mNumberBuffers;++i)
|
||||
numChannels += buflist->mBuffers[i].mNumberChannels;
|
||||
|
||||
return numChannels;
|
||||
}
|
||||
|
||||
|
||||
void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
||||
{
|
||||
UInt32 propSize{};
|
||||
if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize))
|
||||
{
|
||||
ERR("Failed to get device list size: %u\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
|
||||
if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
|
||||
{
|
||||
ERR("Failed to get device list: %u\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<DeviceEntry> newdevs;
|
||||
newdevs.reserve(devIds.size());
|
||||
|
||||
AudioDeviceID defaultId{kAudioDeviceUnknown};
|
||||
GetHwProperty(isCapture ? kAudioHardwarePropertyDefaultInputDevice :
|
||||
kAudioHardwarePropertyDefaultOutputDevice, sizeof(defaultId), &defaultId);
|
||||
|
||||
if(defaultId != kAudioDeviceUnknown)
|
||||
{
|
||||
newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)});
|
||||
const auto &entry = newdevs.back();
|
||||
TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
|
||||
}
|
||||
for(const AudioDeviceID devId : devIds)
|
||||
{
|
||||
if(devId == kAudioDeviceUnknown)
|
||||
continue;
|
||||
|
||||
auto match_devid = [devId](const DeviceEntry &entry) noexcept -> bool
|
||||
{ return entry.mId == devId; };
|
||||
auto match = std::find_if(newdevs.cbegin(), newdevs.cend(), match_devid);
|
||||
if(match != newdevs.cend()) continue;
|
||||
|
||||
auto numChannels = GetDeviceChannelCount(devId, isCapture);
|
||||
if(numChannels > 0)
|
||||
{
|
||||
newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)});
|
||||
const auto &entry = newdevs.back();
|
||||
TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId);
|
||||
}
|
||||
}
|
||||
|
||||
if(newdevs.size() > 1)
|
||||
{
|
||||
/* Rename entries that have matching names, by appending '#2', '#3',
|
||||
* etc, as needed.
|
||||
*/
|
||||
for(auto curitem = newdevs.begin()+1;curitem != newdevs.end();++curitem)
|
||||
{
|
||||
auto check_match = [curitem](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == curitem->mName; };
|
||||
if(std::find_if(newdevs.begin(), curitem, check_match) != curitem)
|
||||
{
|
||||
std::string name{curitem->mName};
|
||||
size_t count{1};
|
||||
auto check_name = [&name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
do {
|
||||
name = curitem->mName;
|
||||
name += " #";
|
||||
name += std::to_string(++count);
|
||||
} while(std::find_if(newdevs.begin(), curitem, check_name) != curitem);
|
||||
curitem->mName = std::move(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newdevs.shrink_to_fit();
|
||||
newdevs.swap(list);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static constexpr char ca_device[] = "CoreAudio Default";
|
||||
#endif
|
||||
|
||||
|
||||
struct CoreAudioPlayback final : public BackendBase {
|
||||
CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~CoreAudioPlayback() override;
|
||||
|
||||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
|
|
@ -96,19 +301,41 @@ OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTi
|
|||
|
||||
void CoreAudioPlayback::open(const char *name)
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||
if(!name)
|
||||
GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
|
||||
&audioDevice);
|
||||
else
|
||||
{
|
||||
if(PlaybackList.empty())
|
||||
EnumerateDevices(PlaybackList, false);
|
||||
|
||||
auto find_name = [name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
|
||||
if(devmatch == PlaybackList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
#else
|
||||
if(!name)
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
#endif
|
||||
|
||||
/* open the default output unit */
|
||||
AudioComponentDescription desc{};
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#if CAN_ENUMERATE
|
||||
desc.componentSubType = (audioDevice == kAudioDeviceUnknown) ?
|
||||
kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
|
|
@ -118,18 +345,50 @@ void CoreAudioPlayback::open(const char *name)
|
|||
if(comp == nullptr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
|
||||
|
||||
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
|
||||
AudioUnit audioUnit{};
|
||||
OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
|
||||
/* init and start the default audio unit... */
|
||||
err = AudioUnitInitialize(mAudioUnit);
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &audioDevice, sizeof(AudioDeviceID));
|
||||
#endif
|
||||
|
||||
err = AudioUnitInitialize(audioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: %u", err};
|
||||
|
||||
/* WARNING: I don't know if "valid" audio unit values are guaranteed to be
|
||||
* non-0. If not, this logic is broken.
|
||||
*/
|
||||
if(mAudioUnit)
|
||||
{
|
||||
AudioUnitUninitialize(mAudioUnit);
|
||||
AudioComponentInstanceDispose(mAudioUnit);
|
||||
}
|
||||
mAudioUnit = audioUnit;
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(name)
|
||||
mDevice->DeviceName = name;
|
||||
else
|
||||
{
|
||||
UInt32 propSize{sizeof(audioDevice)};
|
||||
audioDevice = kAudioDeviceUnknown;
|
||||
AudioUnitGetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &audioDevice, &propSize);
|
||||
|
||||
std::string devname{GetDeviceName(audioDevice)};
|
||||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||
else mDevice->DeviceName = "Unknown Device Name";
|
||||
}
|
||||
#else
|
||||
mDevice->DeviceName = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CoreAudioPlayback::reset()
|
||||
|
|
@ -140,10 +399,10 @@ bool CoreAudioPlayback::reset()
|
|||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
AudioStreamBasicDescription streamFormat{};
|
||||
auto size = static_cast<UInt32>(sizeof(AudioStreamBasicDescription));
|
||||
UInt32 size{sizeof(streamFormat)};
|
||||
err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
|
||||
0, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
|
||||
if(err != noErr || size != sizeof(streamFormat))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
return false;
|
||||
|
|
@ -159,15 +418,9 @@ bool CoreAudioPlayback::reset()
|
|||
TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate);
|
||||
#endif
|
||||
|
||||
/* set default output unit's input side to match output side */
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
|
||||
0, &streamFormat, size);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Use the sample rate from the output unit's current parameters, but reset
|
||||
* everything else.
|
||||
*/
|
||||
if(mDevice->Frequency != streamFormat.mSampleRate)
|
||||
{
|
||||
mDevice->BufferSize = static_cast<uint>(uint64_t{mDevice->BufferSize} *
|
||||
|
|
@ -176,82 +429,54 @@ bool CoreAudioPlayback::reset()
|
|||
}
|
||||
|
||||
/* FIXME: How to tell what channels are what in the output device, and how
|
||||
* to specify what we're giving? eg, 6.0 vs 5.1 */
|
||||
switch(streamFormat.mChannelsPerFrame)
|
||||
{
|
||||
case 1:
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
break;
|
||||
case 2:
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
break;
|
||||
case 4:
|
||||
mDevice->FmtChans = DevFmtQuad;
|
||||
break;
|
||||
case 6:
|
||||
mDevice->FmtChans = DevFmtX51;
|
||||
break;
|
||||
case 7:
|
||||
mDevice->FmtChans = DevFmtX61;
|
||||
break;
|
||||
case 8:
|
||||
mDevice->FmtChans = DevFmtX71;
|
||||
break;
|
||||
default:
|
||||
ERR("Unhandled channel count (%d), using Stereo\n", streamFormat.mChannelsPerFrame);
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
streamFormat.mChannelsPerFrame = 2;
|
||||
break;
|
||||
}
|
||||
setDefaultWFXChannelOrder();
|
||||
* to specify what we're giving? e.g. 6.0 vs 5.1
|
||||
*/
|
||||
streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt();
|
||||
|
||||
/* use channel count and sample rate from the default output unit's current
|
||||
* parameters, but reset everything else */
|
||||
streamFormat.mFramesPerPacket = 1;
|
||||
streamFormat.mFormatFlags = 0;
|
||||
streamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked;
|
||||
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
mDevice->FmtType = DevFmtByte;
|
||||
/* fall-through */
|
||||
case DevFmtByte:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
mDevice->FmtType = DevFmtInt;
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
||||
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
|
||||
streamFormat.mBitsPerChannel = 32;
|
||||
break;
|
||||
}
|
||||
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
|
||||
streamFormat.mBitsPerChannel / 8;
|
||||
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
|
||||
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
|
||||
kLinearPCMFormatFlagIsPacked;
|
||||
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame*streamFormat.mBitsPerChannel/8;
|
||||
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame*streamFormat.mFramesPerPacket;
|
||||
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
|
||||
0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||
0, &streamFormat, sizeof(streamFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
/* setup callback */
|
||||
mFrameSize = mDevice->frameSizeFromFmt();
|
||||
AURenderCallbackStruct input{};
|
||||
|
|
@ -294,7 +519,7 @@ void CoreAudioPlayback::stop()
|
|||
|
||||
|
||||
struct CoreAudioCapture final : public BackendBase {
|
||||
CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~CoreAudioCapture() override;
|
||||
|
||||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
|
|
@ -383,45 +608,64 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags*,
|
|||
|
||||
void CoreAudioCapture::open(const char *name)
|
||||
{
|
||||
AudioStreamBasicDescription requestedFormat; // The application requested format
|
||||
AudioStreamBasicDescription hardwareFormat; // The hardware format
|
||||
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
|
||||
AURenderCallbackStruct input;
|
||||
AudioComponentDescription desc;
|
||||
UInt32 propertySize;
|
||||
UInt32 enableIO;
|
||||
AudioComponent comp;
|
||||
OSStatus err;
|
||||
#if CAN_ENUMERATE
|
||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||
if(!name)
|
||||
GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
|
||||
&audioDevice);
|
||||
else
|
||||
{
|
||||
if(CaptureList.empty())
|
||||
EnumerateDevices(CaptureList, true);
|
||||
|
||||
auto find_name = [name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
|
||||
if(devmatch == CaptureList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
#else
|
||||
if(!name)
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
#endif
|
||||
|
||||
AudioComponentDescription desc{};
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#if CAN_ENUMERATE
|
||||
desc.componentSubType = (audioDevice == kAudioDeviceUnknown) ?
|
||||
kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
// Search for component with given description
|
||||
comp = AudioComponentFindNext(NULL, &desc);
|
||||
AudioComponent comp{AudioComponentFindNext(NULL, &desc)};
|
||||
if(comp == NULL)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
|
||||
|
||||
// Open the component
|
||||
err = AudioComponentInstanceNew(comp, &mAudioUnit);
|
||||
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &audioDevice, sizeof(AudioDeviceID));
|
||||
#endif
|
||||
|
||||
// Turn off AudioUnit output
|
||||
enableIO = 0;
|
||||
UInt32 enableIO{0};
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
|
||||
kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
|
|
@ -436,35 +680,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not enable audio unit input property: %u", err};
|
||||
|
||||
#if !TARGET_OS_IOS
|
||||
{
|
||||
// Get the default input device
|
||||
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
||||
|
||||
propertySize = sizeof(AudioDeviceID);
|
||||
AudioObjectPropertyAddress propertyAddress{};
|
||||
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||
|
||||
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, nullptr,
|
||||
&propertySize, &inputDevice);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not get input device: %u", err};
|
||||
if(inputDevice == kAudioDeviceUnknown)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Unknown input device"};
|
||||
|
||||
// Track the input device
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not set input device: %u", err};
|
||||
}
|
||||
#endif
|
||||
|
||||
// set capture callback
|
||||
AURenderCallbackStruct input{};
|
||||
input.inputProc = CoreAudioCapture::RecordProcC;
|
||||
input.inputProcRefCon = this;
|
||||
|
||||
|
|
@ -489,14 +706,16 @@ void CoreAudioCapture::open(const char *name)
|
|||
"Could not initialize audio unit: %u", err};
|
||||
|
||||
// Get the hardware format
|
||||
propertySize = sizeof(AudioStreamBasicDescription);
|
||||
AudioStreamBasicDescription hardwareFormat{};
|
||||
UInt32 propertySize{sizeof(hardwareFormat)};
|
||||
err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
|
||||
1, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
|
||||
if(err != noErr || propertySize != sizeof(hardwareFormat))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input format: %u", err};
|
||||
|
||||
// Set up the requested format description
|
||||
AudioStreamBasicDescription requestedFormat{};
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
|
|
@ -509,7 +728,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
break;
|
||||
case DevFmtShort:
|
||||
requestedFormat.mBitsPerChannel = 16;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger
|
||||
| kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
requestedFormat.mBitsPerChannel = 16;
|
||||
|
|
@ -517,7 +737,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
break;
|
||||
case DevFmtInt:
|
||||
requestedFormat.mBitsPerChannel = 32;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
requestedFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger
|
||||
| kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
requestedFormat.mBitsPerChannel = 32;
|
||||
|
|
@ -525,7 +746,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
break;
|
||||
case DevFmtFloat:
|
||||
requestedFormat.mBitsPerChannel = 32;
|
||||
requestedFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
|
||||
requestedFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagsNativeEndian
|
||||
| kAudioFormatFlagIsPacked;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -540,7 +762,6 @@ void CoreAudioCapture::open(const char *name)
|
|||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtAmbi3D:
|
||||
|
|
@ -561,7 +782,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
|
||||
// Use intermediate format for sample rate conversion (outputFormat)
|
||||
// Set sample rate to the same as hardware for resampling later
|
||||
outputFormat = requestedFormat;
|
||||
AudioStreamBasicDescription outputFormat{requestedFormat};
|
||||
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
|
||||
|
||||
// The output format should be the requested format, but using the hardware sample rate
|
||||
|
|
@ -600,7 +821,23 @@ void CoreAudioCapture::open(const char *name)
|
|||
mFormat.mChannelsPerFrame, static_cast<uint>(hardwareFormat.mSampleRate),
|
||||
mDevice->Frequency, Resampler::FastBSinc24);
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(name)
|
||||
mDevice->DeviceName = name;
|
||||
else
|
||||
{
|
||||
UInt32 propSize{sizeof(audioDevice)};
|
||||
audioDevice = kAudioDeviceUnknown;
|
||||
AudioUnitGetProperty(mAudioUnit, kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &audioDevice, &propSize);
|
||||
|
||||
std::string devname{GetDeviceName(audioDevice)};
|
||||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||
else mDevice->DeviceName = "Unknown Device Name";
|
||||
}
|
||||
#else
|
||||
mDevice->DeviceName = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -665,6 +902,26 @@ bool CoreAudioBackendFactory::querySupport(BackendType type)
|
|||
std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||
{
|
||||
std::string outnames;
|
||||
#if CAN_ENUMERATE
|
||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||
{
|
||||
/* Includes null char. */
|
||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||
};
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
EnumerateDevices(PlaybackList, false);
|
||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
EnumerateDevices(CaptureList, true);
|
||||
std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
|
||||
break;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
|
|
@ -673,10 +930,11 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
|||
outnames.append(ca_device, sizeof(ca_device));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new CoreAudioPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_COREAUDIO_H
|
||||
#define BACKENDS_COREAUDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct CoreAudioBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/dsound.h"
|
||||
#include "dsound.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
|
@ -44,9 +44,10 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "alnumeric.h"
|
||||
#include "comptr.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
|
@ -169,21 +170,21 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
|
|||
|
||||
|
||||
struct DSoundPlayback final : public BackendBase {
|
||||
DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~DSoundPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
||||
void open(const ALCchar *name) override;
|
||||
void open(const char *name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
IDirectSound *mDS{nullptr};
|
||||
IDirectSoundBuffer *mPrimaryBuffer{nullptr};
|
||||
IDirectSoundBuffer *mBuffer{nullptr};
|
||||
IDirectSoundNotify *mNotifies{nullptr};
|
||||
HANDLE mNotifyEvent{nullptr};
|
||||
ComPtr<IDirectSound> mDS;
|
||||
ComPtr<IDirectSoundBuffer> mPrimaryBuffer;
|
||||
ComPtr<IDirectSoundBuffer> mBuffer;
|
||||
ComPtr<IDirectSoundNotify> mNotifies;
|
||||
HANDLE mNotifyEvent{nullptr};
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
|
@ -193,19 +194,11 @@ struct DSoundPlayback final : public BackendBase {
|
|||
|
||||
DSoundPlayback::~DSoundPlayback()
|
||||
{
|
||||
if(mNotifies)
|
||||
mNotifies->Release();
|
||||
mNotifies = nullptr;
|
||||
if(mBuffer)
|
||||
mBuffer->Release();
|
||||
mBuffer = nullptr;
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
|
||||
if(mDS)
|
||||
mDS->Release();
|
||||
mDS = nullptr;
|
||||
|
||||
if(mNotifyEvent)
|
||||
CloseHandle(mNotifyEvent);
|
||||
mNotifyEvent = nullptr;
|
||||
|
|
@ -234,8 +227,8 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
|||
bool Playing{false};
|
||||
DWORD LastCursor{0u};
|
||||
mBuffer->GetCurrentPosition(&LastCursor, nullptr);
|
||||
while(!mKillNow.load(std::memory_order_acquire) &&
|
||||
mDevice->Connected.load(std::memory_order_acquire))
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
// Get current play cursor
|
||||
DWORD PlayCursor;
|
||||
|
|
@ -344,31 +337,34 @@ void DSoundPlayback::open(const char *name)
|
|||
}
|
||||
|
||||
hr = DS_OK;
|
||||
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
if(!mNotifyEvent) hr = E_FAIL;
|
||||
if(!mNotifyEvent)
|
||||
{
|
||||
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
if(!mNotifyEvent) hr = E_FAIL;
|
||||
}
|
||||
|
||||
//DirectSound Init code
|
||||
ComPtr<IDirectSound> ds;
|
||||
if(SUCCEEDED(hr))
|
||||
hr = DirectSoundCreate(guid, &mDS, nullptr);
|
||||
hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
if(FAILED(hr))
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||
hr};
|
||||
|
||||
mNotifies = nullptr;
|
||||
mBuffer = nullptr;
|
||||
mPrimaryBuffer = nullptr;
|
||||
mDS = std::move(ds);
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool DSoundPlayback::reset()
|
||||
{
|
||||
if(mNotifies)
|
||||
mNotifies->Release();
|
||||
mNotifies = nullptr;
|
||||
if(mBuffer)
|
||||
mBuffer->Release();
|
||||
mBuffer = nullptr;
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
|
|
@ -406,17 +402,14 @@ bool DSoundPlayback::reset()
|
|||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(speakers == DSSPEAKER_QUAD)
|
||||
mDevice->FmtChans = DevFmtQuad;
|
||||
else if(speakers == DSSPEAKER_5POINT1_SURROUND)
|
||||
else if(speakers == DSSPEAKER_5POINT1_SURROUND || speakers == DSSPEAKER_5POINT1_BACK)
|
||||
mDevice->FmtChans = DevFmtX51;
|
||||
else if(speakers == DSSPEAKER_5POINT1_BACK)
|
||||
mDevice->FmtChans = DevFmtX51Rear;
|
||||
else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
|
||||
mDevice->FmtChans = DevFmtX71;
|
||||
else
|
||||
ERR("Unknown system speaker config: 0x%lx\n", speakers);
|
||||
}
|
||||
mDevice->IsHeadphones = mDevice->FmtChans == DevFmtStereo
|
||||
&& speakers == DSSPEAKER_HEADPHONE;
|
||||
mDevice->Flags.set(DirectEar, (speakers == DSSPEAKER_HEADPHONE));
|
||||
|
||||
switch(mDevice->FmtChans)
|
||||
{
|
||||
|
|
@ -426,7 +419,6 @@ bool DSoundPlayback::reset()
|
|||
case DevFmtStereo: OutputType.dwChannelMask = STEREO; break;
|
||||
case DevFmtQuad: OutputType.dwChannelMask = QUAD; break;
|
||||
case DevFmtX51: OutputType.dwChannelMask = X5DOT1; break;
|
||||
case DevFmtX51Rear: OutputType.dwChannelMask = X5DOT1REAR; break;
|
||||
case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
|
||||
case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
|
||||
}
|
||||
|
|
@ -454,8 +446,6 @@ retry_open:
|
|||
else
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
}
|
||||
else
|
||||
|
|
@ -465,7 +455,7 @@ retry_open:
|
|||
DSBUFFERDESC DSBDescription{};
|
||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||
|
|
@ -485,7 +475,7 @@ retry_open:
|
|||
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
||||
DSBDescription.lpwfxFormat = &OutputType.Format;
|
||||
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
|
||||
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
|
|
@ -499,7 +489,7 @@ retry_open:
|
|||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
mNotifies = static_cast<IDirectSoundNotify*>(ptr);
|
||||
mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
|
||||
|
||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||
assert(num_updates <= MAX_UPDATES);
|
||||
|
|
@ -517,14 +507,8 @@ retry_open:
|
|||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if(mNotifies)
|
||||
mNotifies->Release();
|
||||
mNotifies = nullptr;
|
||||
if(mBuffer)
|
||||
mBuffer->Release();
|
||||
mBuffer = nullptr;
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -558,7 +542,7 @@ void DSoundPlayback::stop()
|
|||
|
||||
|
||||
struct DSoundCapture final : public BackendBase {
|
||||
DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~DSoundCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
|
|
@ -567,8 +551,8 @@ struct DSoundCapture final : public BackendBase {
|
|||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
IDirectSoundCapture *mDSC{nullptr};
|
||||
IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
|
||||
ComPtr<IDirectSoundCapture> mDSC;
|
||||
ComPtr<IDirectSoundCaptureBuffer> mDSCbuffer;
|
||||
DWORD mBufferBytes{0u};
|
||||
DWORD mCursor{0u};
|
||||
|
||||
|
|
@ -582,12 +566,8 @@ DSoundCapture::~DSoundCapture()
|
|||
if(mDSCbuffer)
|
||||
{
|
||||
mDSCbuffer->Stop();
|
||||
mDSCbuffer->Release();
|
||||
mDSCbuffer = nullptr;
|
||||
}
|
||||
|
||||
if(mDSC)
|
||||
mDSC->Release();
|
||||
mDSC = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +633,6 @@ void DSoundCapture::open(const char *name)
|
|||
case DevFmtStereo: InputType.dwChannelMask = STEREO; break;
|
||||
case DevFmtQuad: InputType.dwChannelMask = QUAD; break;
|
||||
case DevFmtX51: InputType.dwChannelMask = X5DOT1; break;
|
||||
case DevFmtX51Rear: InputType.dwChannelMask = X5DOT1REAR; break;
|
||||
case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
|
||||
case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
|
||||
case DevFmtAmbi3D:
|
||||
|
|
@ -693,20 +672,16 @@ void DSoundCapture::open(const char *name)
|
|||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||
|
||||
//DirectSoundCapture Init code
|
||||
hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
|
||||
hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
mRing = nullptr;
|
||||
if(mDSCbuffer)
|
||||
mDSCbuffer->Release();
|
||||
mDSCbuffer = nullptr;
|
||||
if(mDSC)
|
||||
mDSC->Release();
|
||||
mDSC = nullptr;
|
||||
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||
|
|
@ -858,7 +833,7 @@ std::string DSoundBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr DSoundBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new DSoundPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_DSOUND_H
|
||||
#define BACKENDS_DSOUND_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct DSoundBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/jack.h"
|
||||
#include "jack.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
|
@ -31,9 +31,10 @@
|
|||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
|
@ -45,9 +46,6 @@
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr char jackDevice[] = "JACK Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define JACK_FUNCS(MAGIC) \
|
||||
MAGIC(jack_client_open); \
|
||||
|
|
@ -101,6 +99,8 @@ decltype(jack_error_callback) * pjack_error_callback;
|
|||
#endif
|
||||
|
||||
|
||||
constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE;
|
||||
|
||||
jack_options_t ClientOptions = JackNullOption;
|
||||
|
||||
bool jack_load()
|
||||
|
|
@ -152,6 +152,11 @@ bool jack_load()
|
|||
}
|
||||
|
||||
|
||||
struct JackDeleter {
|
||||
void operator()(void *ptr) { jack_free(ptr); }
|
||||
};
|
||||
using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>;
|
||||
|
||||
struct DeviceEntry {
|
||||
std::string mName;
|
||||
std::string mPattern;
|
||||
|
|
@ -160,53 +165,125 @@ struct DeviceEntry {
|
|||
al::vector<DeviceEntry> PlaybackList;
|
||||
|
||||
|
||||
void EnumerateDevices(al::vector<DeviceEntry> &list)
|
||||
void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
||||
{
|
||||
al::vector<DeviceEntry>{}.swap(list);
|
||||
std::remove_reference_t<decltype(list)>{}.swap(list);
|
||||
|
||||
list.emplace_back(DeviceEntry{jackDevice, ""});
|
||||
|
||||
std::string customList{ConfigValueStr(nullptr, "jack", "custom-devices").value_or("")};
|
||||
size_t strpos{0};
|
||||
while(strpos < customList.size())
|
||||
if(JackPortsPtr ports{jack_get_ports(client, nullptr, JackDefaultAudioType, JackPortIsInput)})
|
||||
{
|
||||
size_t nextpos{customList.find(';', strpos)};
|
||||
size_t seppos{customList.find('=', strpos)};
|
||||
if(seppos >= nextpos || seppos == strpos)
|
||||
for(size_t i{0};ports[i];++i)
|
||||
{
|
||||
const std::string entry{customList.substr(strpos, nextpos-strpos)};
|
||||
ERR("Invalid device entry: \"%s\"\n", entry.c_str());
|
||||
const char *sep{std::strchr(ports[i], ':')};
|
||||
if(!sep || ports[i] == sep) continue;
|
||||
|
||||
const al::span<const char> portdev{ports[i], sep};
|
||||
auto check_name = [portdev](const DeviceEntry &entry) -> bool
|
||||
{
|
||||
const size_t len{portdev.size()};
|
||||
return entry.mName.length() == len
|
||||
&& entry.mName.compare(0, len, portdev.data(), len) == 0;
|
||||
};
|
||||
if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
|
||||
continue;
|
||||
|
||||
std::string name{portdev.data(), portdev.size()};
|
||||
list.emplace_back(DeviceEntry{name, name+":"});
|
||||
const auto &entry = list.back();
|
||||
TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||
}
|
||||
/* There are ports but couldn't get device names from them. Add a
|
||||
* generic entry.
|
||||
*/
|
||||
if(ports[0] && list.empty())
|
||||
{
|
||||
WARN("No device names found in available ports, adding a generic name.\n");
|
||||
list.emplace_back(DeviceEntry{"JACK", ""});
|
||||
}
|
||||
}
|
||||
|
||||
if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices"))
|
||||
{
|
||||
for(size_t strpos{0};strpos < listopt->size();)
|
||||
{
|
||||
size_t nextpos{listopt->find(';', strpos)};
|
||||
size_t seppos{listopt->find('=', strpos)};
|
||||
if(seppos >= nextpos || seppos == strpos)
|
||||
{
|
||||
const std::string entry{listopt->substr(strpos, nextpos-strpos)};
|
||||
ERR("Invalid device entry: \"%s\"\n", entry.c_str());
|
||||
if(nextpos != std::string::npos) ++nextpos;
|
||||
strpos = nextpos;
|
||||
continue;
|
||||
}
|
||||
|
||||
const al::span<const char> name{listopt->data()+strpos, seppos-strpos};
|
||||
const al::span<const char> pattern{listopt->data()+(seppos+1),
|
||||
std::min(nextpos, listopt->size())-(seppos+1)};
|
||||
|
||||
/* Check if this custom pattern already exists in the list. */
|
||||
auto check_pattern = [pattern](const DeviceEntry &entry) -> bool
|
||||
{
|
||||
const size_t len{pattern.size()};
|
||||
return entry.mPattern.length() == len
|
||||
&& entry.mPattern.compare(0, len, pattern.data(), len) == 0;
|
||||
};
|
||||
auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern);
|
||||
if(itemmatch != list.end())
|
||||
{
|
||||
/* If so, replace the name with this custom one. */
|
||||
itemmatch->mName.assign(name.data(), name.size());
|
||||
TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(),
|
||||
itemmatch->mPattern.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, add a new device entry. */
|
||||
list.emplace_back(DeviceEntry{std::string{name.data(), name.size()},
|
||||
std::string{pattern.data(), pattern.size()}});
|
||||
const auto &entry = list.back();
|
||||
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||
}
|
||||
|
||||
if(nextpos != std::string::npos) ++nextpos;
|
||||
strpos = nextpos;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
size_t count{1};
|
||||
std::string name{customList.substr(strpos, seppos-strpos)};
|
||||
auto check_name = [&name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
while(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
|
||||
if(list.size() > 1)
|
||||
{
|
||||
/* Rename entries that have matching names, by appending '#2', '#3',
|
||||
* etc, as needed.
|
||||
*/
|
||||
for(auto curitem = list.begin()+1;curitem != list.end();++curitem)
|
||||
{
|
||||
name = customList.substr(strpos, seppos-strpos);
|
||||
name += " #";
|
||||
name += std::to_string(++count);
|
||||
auto check_match = [curitem](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == curitem->mName; };
|
||||
if(std::find_if(list.begin(), curitem, check_match) != curitem)
|
||||
{
|
||||
std::string name{curitem->mName};
|
||||
size_t count{1};
|
||||
auto check_name = [&name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
do {
|
||||
name = curitem->mName;
|
||||
name += " #";
|
||||
name += std::to_string(++count);
|
||||
} while(std::find_if(list.begin(), curitem, check_name) != curitem);
|
||||
curitem->mName = std::move(name);
|
||||
}
|
||||
}
|
||||
|
||||
++seppos;
|
||||
list.emplace_back(DeviceEntry{std::move(name), customList.substr(seppos, nextpos-seppos)});
|
||||
const auto &entry = list.back();
|
||||
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||
|
||||
if(nextpos != std::string::npos) ++nextpos;
|
||||
strpos = nextpos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct JackPlayback final : public BackendBase {
|
||||
JackPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~JackPlayback() override;
|
||||
|
||||
int processRt(jack_nframes_t numframes) noexcept;
|
||||
static int processRtC(jack_nframes_t numframes, void *arg) noexcept
|
||||
{ return static_cast<JackPlayback*>(arg)->processRt(numframes); }
|
||||
|
||||
int process(jack_nframes_t numframes) noexcept;
|
||||
static int processC(jack_nframes_t numframes, void *arg) noexcept
|
||||
{ return static_cast<JackPlayback*>(arg)->process(numframes); }
|
||||
|
|
@ -227,6 +304,7 @@ struct JackPlayback final : public BackendBase {
|
|||
std::mutex mMutex;
|
||||
|
||||
std::atomic<bool> mPlaying{false};
|
||||
bool mRTMixing{false};
|
||||
RingBufferPtr mRing;
|
||||
al::semaphore mSem;
|
||||
|
||||
|
|
@ -241,16 +319,40 @@ JackPlayback::~JackPlayback()
|
|||
if(!mClient)
|
||||
return;
|
||||
|
||||
std::for_each(mPort.begin(), mPort.end(),
|
||||
[this](jack_port_t *port) -> void
|
||||
{ if(port) jack_port_unregister(mClient, port); }
|
||||
);
|
||||
auto unregister_port = [this](jack_port_t *port) -> void
|
||||
{ if(port) jack_port_unregister(mClient, port); };
|
||||
std::for_each(mPort.begin(), mPort.end(), unregister_port);
|
||||
mPort.fill(nullptr);
|
||||
|
||||
jack_client_close(mClient);
|
||||
mClient = nullptr;
|
||||
}
|
||||
|
||||
|
||||
int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
||||
{
|
||||
std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
|
||||
size_t numchans{0};
|
||||
for(auto port : mPort)
|
||||
{
|
||||
if(!port || numchans == mDevice->RealOut.Buffer.size())
|
||||
break;
|
||||
out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
||||
}
|
||||
|
||||
if LIKELY(mPlaying.load(std::memory_order_acquire))
|
||||
mDevice->renderSamples({out.data(), numchans}, static_cast<uint>(numframes));
|
||||
else
|
||||
{
|
||||
auto clear_buf = [numframes](float *outbuf) -> void
|
||||
{ std::fill_n(outbuf, numframes, 0.0f); };
|
||||
std::for_each(out.begin(), out.begin()+numchans, clear_buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||
{
|
||||
std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
|
||||
|
|
@ -352,49 +454,56 @@ int JackPlayback::mixerProc()
|
|||
|
||||
void JackPlayback::open(const char *name)
|
||||
{
|
||||
mPortPattern.clear();
|
||||
|
||||
if(!name)
|
||||
name = jackDevice;
|
||||
else if(strcmp(name, jackDevice) != 0)
|
||||
if(!mClient)
|
||||
{
|
||||
if(PlaybackList.empty())
|
||||
EnumerateDevices(PlaybackList);
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||
|
||||
jack_status_t status;
|
||||
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
|
||||
if(mClient == nullptr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to open client connection: 0x%02x", status};
|
||||
if((status&JackServerStarted))
|
||||
TRACE("JACK server started\n");
|
||||
if((status&JackNameNotUnique))
|
||||
{
|
||||
client_name = jack_get_client_name(mClient);
|
||||
TRACE("Client name not unique, got '%s' instead\n", client_name);
|
||||
}
|
||||
}
|
||||
|
||||
if(PlaybackList.empty())
|
||||
EnumerateDevices(mClient, PlaybackList);
|
||||
|
||||
if(!name && !PlaybackList.empty())
|
||||
{
|
||||
name = PlaybackList[0].mName.c_str();
|
||||
mPortPattern = PlaybackList[0].mPattern;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto check_name = [name](const DeviceEntry &entry) -> bool
|
||||
{ return entry.mName == name; };
|
||||
auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name);
|
||||
if(iter == PlaybackList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%s\" not found", name?name:""};
|
||||
mPortPattern = iter->mPattern;
|
||||
}
|
||||
|
||||
const char *client_name{"alsoft"};
|
||||
jack_status_t status;
|
||||
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
|
||||
if(mClient == nullptr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to open client connection: 0x%02x", status};
|
||||
|
||||
if((status&JackServerStarted))
|
||||
TRACE("JACK server started\n");
|
||||
if((status&JackNameNotUnique))
|
||||
{
|
||||
client_name = jack_get_client_name(mClient);
|
||||
TRACE("Client name not unique, got '%s' instead\n", client_name);
|
||||
}
|
||||
|
||||
jack_set_process_callback(mClient, &JackPlayback::processC, this);
|
||||
mRTMixing = GetConfigValueBool(name, "jack", "rt-mix", 1);
|
||||
jack_set_process_callback(mClient,
|
||||
mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool JackPlayback::reset()
|
||||
{
|
||||
std::for_each(mPort.begin(), mPort.end(),
|
||||
[this](jack_port_t *port) -> void
|
||||
{ if(port) jack_port_unregister(mClient, port); });
|
||||
auto unregister_port = [this](jack_port_t *port) -> void
|
||||
{ if(port) jack_port_unregister(mClient, port); };
|
||||
std::for_each(mPort.begin(), mPort.end(), unregister_port);
|
||||
mPort.fill(nullptr);
|
||||
|
||||
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
|
||||
|
|
@ -402,28 +511,39 @@ bool JackPlayback::reset()
|
|||
*/
|
||||
mDevice->Frequency = jack_get_sample_rate(mClient);
|
||||
mDevice->UpdateSize = jack_get_buffer_size(mClient);
|
||||
mDevice->BufferSize = mDevice->UpdateSize * 2;
|
||||
|
||||
const char *devname{mDevice->DeviceName.c_str()};
|
||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||
if(mRTMixing)
|
||||
{
|
||||
/* Assume only two periods when directly mixing. Should try to query
|
||||
* the total port latency when connected.
|
||||
*/
|
||||
mDevice->BufferSize = mDevice->UpdateSize * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *devname{mDevice->DeviceName.c_str()};
|
||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||
}
|
||||
|
||||
/* Force 32-bit float output. */
|
||||
mDevice->FmtType = DevFmtFloat;
|
||||
|
||||
int port_num{0};
|
||||
auto ports_end = mPort.begin() + mDevice->channelsFromFmt();
|
||||
auto bad_port = std::find_if_not(mPort.begin(), ports_end,
|
||||
[this](jack_port_t *&port) -> bool
|
||||
{
|
||||
std::string name{"channel_" + std::to_string(&port - &mPort[0] + 1)};
|
||||
port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
return port != nullptr;
|
||||
});
|
||||
auto bad_port = mPort.begin();
|
||||
while(bad_port != ports_end)
|
||||
{
|
||||
std::string name{"channel_" + std::to_string(++port_num)};
|
||||
*bad_port = jack_port_register(mClient, name.c_str(), JackDefaultAudioType,
|
||||
JackPortIsOutput | JackPortIsTerminal, 0);
|
||||
if(!*bad_port) break;
|
||||
++bad_port;
|
||||
}
|
||||
if(bad_port != ports_end)
|
||||
{
|
||||
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(mDevice->FmtChans));
|
||||
ERR("Failed to register enough JACK ports for %s output\n",
|
||||
DevFmtChannelsString(mDevice->FmtChans));
|
||||
if(bad_port == mPort.begin()) return false;
|
||||
|
||||
if(bad_port == mPort.begin()+1)
|
||||
|
|
@ -453,29 +573,25 @@ void JackPlayback::start()
|
|||
const char *devname{mDevice->DeviceName.c_str()};
|
||||
if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
|
||||
{
|
||||
const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), nullptr,
|
||||
JackPortIsPhysical|JackPortIsInput)};
|
||||
if(ports == nullptr)
|
||||
JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JackDefaultAudioType,
|
||||
JackPortIsInput)};
|
||||
if(!pnames)
|
||||
{
|
||||
jack_deactivate(mClient);
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"No physical playback ports found"};
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"};
|
||||
}
|
||||
auto connect_port = [this](const jack_port_t *port, const char *pname) -> bool
|
||||
|
||||
for(size_t i{0};i < al::size(mPort) && mPort[i];++i)
|
||||
{
|
||||
if(!port) return false;
|
||||
if(!pname)
|
||||
if(!pnames[i])
|
||||
{
|
||||
ERR("No physical playback port for \"%s\"\n", jack_port_name(port));
|
||||
return false;
|
||||
ERR("No physical playback port for \"%s\"\n", jack_port_name(mPort[i]));
|
||||
break;
|
||||
}
|
||||
if(jack_connect(mClient, jack_port_name(port), pname))
|
||||
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(port),
|
||||
pname);
|
||||
return true;
|
||||
};
|
||||
std::mismatch(mPort.begin(), mPort.end(), ports, connect_port);
|
||||
jack_free(ports);
|
||||
if(jack_connect(mClient, jack_port_name(mPort[i]), pnames[i]))
|
||||
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(mPort[i]),
|
||||
pnames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reconfigure buffer metrics in case the server changed it since the reset
|
||||
|
|
@ -486,36 +602,45 @@ void JackPlayback::start()
|
|||
mDevice->UpdateSize = jack_get_buffer_size(mClient);
|
||||
mDevice->BufferSize = mDevice->UpdateSize * 2;
|
||||
|
||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||
|
||||
mRing = nullptr;
|
||||
mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
|
||||
|
||||
try {
|
||||
if(mRTMixing)
|
||||
mPlaying.store(true, std::memory_order_release);
|
||||
mKillNow.store(false, std::memory_order_release);
|
||||
mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
jack_deactivate(mClient);
|
||||
mPlaying.store(false, std::memory_order_release);
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to start mixing thread: %s", e.what()};
|
||||
else
|
||||
{
|
||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||
|
||||
mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
|
||||
|
||||
try {
|
||||
mPlaying.store(true, std::memory_order_release);
|
||||
mKillNow.store(false, std::memory_order_release);
|
||||
mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this};
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
jack_deactivate(mClient);
|
||||
mPlaying.store(false, std::memory_order_release);
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to start mixing thread: %s", e.what()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JackPlayback::stop()
|
||||
{
|
||||
if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
|
||||
return;
|
||||
if(mPlaying.load(std::memory_order_acquire))
|
||||
{
|
||||
mKillNow.store(true, std::memory_order_release);
|
||||
if(mThread.joinable())
|
||||
{
|
||||
mSem.post();
|
||||
mThread.join();
|
||||
}
|
||||
|
||||
mSem.post();
|
||||
mThread.join();
|
||||
|
||||
jack_deactivate(mClient);
|
||||
mPlaying.store(false, std::memory_order_release);
|
||||
jack_deactivate(mClient);
|
||||
mPlaying.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -525,7 +650,7 @@ ClockLatency JackPlayback::getClockLatency()
|
|||
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
ret.Latency = std::chrono::seconds{mRing->readSpace()};
|
||||
ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
|
||||
ret.Latency /= mDevice->Frequency;
|
||||
|
||||
return ret;
|
||||
|
|
@ -547,10 +672,13 @@ bool JackBackendFactory::init()
|
|||
if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
|
||||
ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
|
||||
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||
|
||||
void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
|
||||
jack_set_error_function(jack_msg_handler);
|
||||
jack_status_t status;
|
||||
jack_client_t *client{jack_client_open("alsoft", ClientOptions, &status, nullptr)};
|
||||
jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)};
|
||||
jack_set_error_function(old_error_cb);
|
||||
if(!client)
|
||||
{
|
||||
|
|
@ -575,10 +703,20 @@ std::string JackBackendFactory::probe(BackendType type)
|
|||
/* Includes null char. */
|
||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||
};
|
||||
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||
jack_status_t status;
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
EnumerateDevices(PlaybackList);
|
||||
if(jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)})
|
||||
{
|
||||
EnumerateDevices(client, PlaybackList);
|
||||
jack_client_close(client);
|
||||
}
|
||||
else
|
||||
WARN("jack_client_open() failed, 0x%02x\n", status);
|
||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
|
|
@ -587,7 +725,7 @@ std::string JackBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr JackBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr JackBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new JackPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_JACK_H
|
||||
#define BACKENDS_JACK_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct JackBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,18 +20,17 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/loopback.h"
|
||||
#include "loopback.h"
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct LoopbackBackend final : public BackendBase {
|
||||
LoopbackBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
|
||||
void open(const ALCchar *name) override;
|
||||
void open(const char *name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -40,7 +39,7 @@ struct LoopbackBackend final : public BackendBase {
|
|||
};
|
||||
|
||||
|
||||
void LoopbackBackend::open(const ALCchar *name)
|
||||
void LoopbackBackend::open(const char *name)
|
||||
{
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
|
@ -69,7 +68,7 @@ bool LoopbackBackendFactory::querySupport(BackendType)
|
|||
std::string LoopbackBackendFactory::probe(BackendType)
|
||||
{ return std::string{}; }
|
||||
|
||||
BackendPtr LoopbackBackendFactory::createBackend(ALCdevice *device, BackendType)
|
||||
BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
|
||||
{ return BackendPtr{new LoopbackBackend{device}}; }
|
||||
|
||||
BackendFactory &LoopbackBackendFactory::getFactory()
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_LOOPBACK_H
|
||||
#define BACKENDS_LOOPBACK_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct LoopbackBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/null.h"
|
||||
#include "null.h"
|
||||
|
||||
#include <exception>
|
||||
#include <atomic>
|
||||
|
|
@ -30,9 +30,9 @@
|
|||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "core/device.h"
|
||||
#include "almalloc.h"
|
||||
#include "alu.h"
|
||||
#include "core/helpers.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ constexpr char nullDevice[] = "No Output";
|
|||
|
||||
|
||||
struct NullBackend final : public BackendBase {
|
||||
NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
NullBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
|
||||
int mixerProc();
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ std::string NullBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr NullBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new NullBackend{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_NULL_H
|
||||
#define BACKENDS_NULL_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct NullBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
384
Engine/lib/openal-soft/alc/backends/oboe.cpp
Normal file
384
Engine/lib/openal-soft/alc/backends/oboe.cpp
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "oboe.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include "oboe/Oboe.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char device_name[] = "Oboe Default";
|
||||
|
||||
|
||||
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
||||
OboePlayback(DeviceBase *device) : BackendBase{device} { }
|
||||
|
||||
oboe::ManagedStream mStream;
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||
int32_t numFrames) override;
|
||||
|
||||
void open(const char *name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
};
|
||||
|
||||
|
||||
oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||
int32_t numFrames)
|
||||
{
|
||||
assert(numFrames > 0);
|
||||
const int32_t numChannels{oboeStream->getChannelCount()};
|
||||
|
||||
mDevice->renderSamples(audioData, static_cast<uint32_t>(numFrames),
|
||||
static_cast<uint32_t>(numChannels));
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
|
||||
void OboePlayback::open(const char *name)
|
||||
{
|
||||
if(!name)
|
||||
name = device_name;
|
||||
else if(std::strcmp(name, device_name) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
/* Open a basic output stream, just to ensure it can work. */
|
||||
oboe::ManagedStream stream;
|
||||
oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->openManagedStream(stream)};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool OboePlayback::reset()
|
||||
{
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Output);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
/* Don't let Oboe convert. We should be able to handle anything it gives
|
||||
* back.
|
||||
*/
|
||||
builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::None);
|
||||
builder.setChannelConversionAllowed(false);
|
||||
builder.setFormatConversionAllowed(false);
|
||||
builder.setCallback(this);
|
||||
|
||||
if(mDevice->Flags.test(FrequencyRequest))
|
||||
builder.setSampleRate(static_cast<int32_t>(mDevice->Frequency));
|
||||
if(mDevice->Flags.test(ChannelsRequest))
|
||||
{
|
||||
/* Only use mono or stereo at user request. There's no telling what
|
||||
* other counts may be inferred as.
|
||||
*/
|
||||
builder.setChannelCount((mDevice->FmtChans==DevFmtMono) ? oboe::ChannelCount::Mono
|
||||
: (mDevice->FmtChans==DevFmtStereo) ? oboe::ChannelCount::Stereo
|
||||
: oboe::ChannelCount::Unspecified);
|
||||
}
|
||||
if(mDevice->Flags.test(SampleTypeRequest))
|
||||
{
|
||||
oboe::AudioFormat format{oboe::AudioFormat::Unspecified};
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
case DevFmtShort:
|
||||
case DevFmtUShort:
|
||||
format = oboe::AudioFormat::I16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
case DevFmtFloat:
|
||||
format = oboe::AudioFormat::Float;
|
||||
break;
|
||||
}
|
||||
builder.setFormat(format);
|
||||
}
|
||||
|
||||
oboe::Result result{builder.openManagedStream(mStream)};
|
||||
/* If the format failed, try asking for the defaults. */
|
||||
while(result == oboe::Result::ErrorInvalidFormat)
|
||||
{
|
||||
if(builder.getFormat() != oboe::AudioFormat::Unspecified)
|
||||
builder.setFormat(oboe::AudioFormat::Unspecified);
|
||||
else if(builder.getSampleRate() != oboe::kUnspecified)
|
||||
builder.setSampleRate(oboe::kUnspecified);
|
||||
else if(builder.getChannelCount() != oboe::ChannelCount::Unspecified)
|
||||
builder.setChannelCount(oboe::ChannelCount::Unspecified);
|
||||
else
|
||||
break;
|
||||
result = builder.openManagedStream(mStream);
|
||||
}
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
mStream->setBufferSizeInFrames(mini(static_cast<int32_t>(mDevice->BufferSize),
|
||||
mStream->getBufferCapacityInFrames()));
|
||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||
|
||||
if(static_cast<uint>(mStream->getChannelCount()) != mDevice->channelsFromFmt())
|
||||
{
|
||||
if(mStream->getChannelCount() >= 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(mStream->getChannelCount() == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
else
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Got unhandled channel count: %d", mStream->getChannelCount()};
|
||||
}
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
switch(mStream->getFormat())
|
||||
{
|
||||
case oboe::AudioFormat::I16:
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
break;
|
||||
case oboe::AudioFormat::Float:
|
||||
mDevice->FmtType = DevFmtFloat;
|
||||
break;
|
||||
case oboe::AudioFormat::Unspecified:
|
||||
case oboe::AudioFormat::Invalid:
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Got unhandled sample type: %s", oboe::convertToText(mStream->getFormat())};
|
||||
}
|
||||
mDevice->Frequency = static_cast<uint32_t>(mStream->getSampleRate());
|
||||
|
||||
/* Ensure the period size is no less than 10ms. It's possible for FramesPerCallback to be 0
|
||||
* indicating variable updates, but OpenAL should have a reasonable minimum update size set.
|
||||
* FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
|
||||
* update size.
|
||||
*/
|
||||
mDevice->UpdateSize = maxu(mDevice->Frequency / 100,
|
||||
static_cast<uint32_t>(mStream->getFramesPerBurst()));
|
||||
mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
|
||||
static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OboePlayback::start()
|
||||
{
|
||||
const oboe::Result result{mStream->start()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
}
|
||||
|
||||
void OboePlayback::stop()
|
||||
{
|
||||
oboe::Result result{mStream->stop()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
}
|
||||
|
||||
|
||||
struct OboeCapture final : public BackendBase {
|
||||
OboeCapture(DeviceBase *device) : BackendBase{device} { }
|
||||
|
||||
oboe::ManagedStream mStream;
|
||||
|
||||
std::vector<al::byte> mSamples;
|
||||
uint mLastAvail{0u};
|
||||
|
||||
void open(const char *name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
};
|
||||
|
||||
void OboeCapture::open(const char *name)
|
||||
{
|
||||
if(!name)
|
||||
name = device_name;
|
||||
else if(std::strcmp(name, device_name) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Input)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||
->setChannelConversionAllowed(true)
|
||||
->setFormatConversionAllowed(true)
|
||||
->setBufferCapacityInFrames(static_cast<int32_t>(mDevice->BufferSize))
|
||||
->setSampleRate(static_cast<int32_t>(mDevice->Frequency));
|
||||
/* Only use mono or stereo at user request. There's no telling what
|
||||
* other counts may be inferred as.
|
||||
*/
|
||||
switch(mDevice->FmtChans)
|
||||
{
|
||||
case DevFmtMono:
|
||||
builder.setChannelCount(oboe::ChannelCount::Mono);
|
||||
break;
|
||||
case DevFmtStereo:
|
||||
builder.setChannelCount(oboe::ChannelCount::Stereo);
|
||||
break;
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtAmbi3D:
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
||||
DevFmtChannelsString(mDevice->FmtChans)};
|
||||
}
|
||||
|
||||
/* FIXME: This really should support UByte, but Oboe doesn't. We'll need to
|
||||
* use a temp buffer and convert.
|
||||
*/
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtShort:
|
||||
builder.setFormat(oboe::AudioFormat::I16);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
builder.setFormat(oboe::AudioFormat::Float);
|
||||
break;
|
||||
case DevFmtByte:
|
||||
case DevFmtUByte:
|
||||
case DevFmtUShort:
|
||||
case DevFmtInt:
|
||||
case DevFmtUInt:
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
|
||||
}
|
||||
|
||||
oboe::Result result{builder.openManagedStream(mStream)};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
if(static_cast<int32_t>(mDevice->BufferSize) > mStream->getBufferCapacityInFrames())
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Buffer size too large (%u > %d)", mDevice->BufferSize,
|
||||
mStream->getBufferCapacityInFrames()};
|
||||
auto buffer_result = mStream->setBufferSizeInFrames(static_cast<int32_t>(mDevice->BufferSize));
|
||||
if(!buffer_result)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set buffer size: %s", oboe::convertToText(buffer_result.error())};
|
||||
else if(buffer_result.value() < static_cast<int32_t>(mDevice->BufferSize))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set large enough buffer size (%u > %d)", mDevice->BufferSize,
|
||||
buffer_result.value()};
|
||||
mDevice->BufferSize = static_cast<uint>(buffer_result.value());
|
||||
|
||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
void OboeCapture::start()
|
||||
{
|
||||
const oboe::Result result{mStream->start()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
}
|
||||
|
||||
void OboeCapture::stop()
|
||||
{
|
||||
/* Capture any unread samples before stopping. Oboe drops whatever's left
|
||||
* in the stream.
|
||||
*/
|
||||
if(auto availres = mStream->getAvailableFrames())
|
||||
{
|
||||
const auto avail = std::max(static_cast<uint>(availres.value()), mLastAvail);
|
||||
const size_t frame_size{static_cast<uint32_t>(mStream->getBytesPerFrame())};
|
||||
const size_t pos{mSamples.size()};
|
||||
mSamples.resize(pos + avail*frame_size);
|
||||
|
||||
auto result = mStream->read(&mSamples[pos], availres.value(), 0);
|
||||
uint got{bool{result} ? static_cast<uint>(result.value()) : 0u};
|
||||
if(got < avail)
|
||||
std::fill_n(&mSamples[pos + got*frame_size], (avail-got)*frame_size, al::byte{});
|
||||
mLastAvail = 0;
|
||||
}
|
||||
|
||||
const oboe::Result result{mStream->stop()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
}
|
||||
|
||||
uint OboeCapture::availableSamples()
|
||||
{
|
||||
/* Keep track of the max available frame count, to ensure it doesn't go
|
||||
* backwards.
|
||||
*/
|
||||
if(auto result = mStream->getAvailableFrames())
|
||||
mLastAvail = std::max(static_cast<uint>(result.value()), mLastAvail);
|
||||
|
||||
const auto frame_size = static_cast<uint32_t>(mStream->getBytesPerFrame());
|
||||
return static_cast<uint>(mSamples.size()/frame_size) + mLastAvail;
|
||||
}
|
||||
|
||||
void OboeCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{
|
||||
const auto frame_size = static_cast<uint>(mStream->getBytesPerFrame());
|
||||
if(const size_t storelen{mSamples.size()})
|
||||
{
|
||||
const auto instore = static_cast<uint>(storelen / frame_size);
|
||||
const uint tocopy{std::min(samples, instore) * frame_size};
|
||||
std::copy_n(mSamples.begin(), tocopy, buffer);
|
||||
mSamples.erase(mSamples.begin(), mSamples.begin() + tocopy);
|
||||
|
||||
buffer += tocopy;
|
||||
samples -= tocopy/frame_size;
|
||||
if(!samples) return;
|
||||
}
|
||||
|
||||
auto result = mStream->read(buffer, static_cast<int32_t>(samples), 0);
|
||||
uint got{bool{result} ? static_cast<uint>(result.value()) : 0u};
|
||||
if(got < samples)
|
||||
std::fill_n(buffer + got*frame_size, (samples-got)*frame_size, al::byte{});
|
||||
mLastAvail = std::max(mLastAvail, samples) - samples;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool OboeBackendFactory::init() { return true; }
|
||||
|
||||
bool OboeBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||
|
||||
std::string OboeBackendFactory::probe(BackendType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
return std::string{device_name, sizeof(device_name)};
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OboePlayback{device}};
|
||||
if(type == BackendType::Capture)
|
||||
return BackendPtr{new OboeCapture{device}};
|
||||
return BackendPtr{};
|
||||
}
|
||||
|
||||
BackendFactory &OboeBackendFactory::getFactory()
|
||||
{
|
||||
static OboeBackendFactory factory{};
|
||||
return factory;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_OBOE_H
|
||||
#define BACKENDS_OBOE_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct OboeBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/opensl.h"
|
||||
#include "opensl.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
|
|
@ -33,9 +33,9 @@
|
|||
#include <functional>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
|
|
@ -68,9 +68,6 @@ constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
|
|||
case DevFmtX51: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
|
||||
SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_SIDE_LEFT |
|
||||
SL_SPEAKER_SIDE_RIGHT;
|
||||
case DevFmtX51Rear: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
|
||||
SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_LEFT |
|
||||
SL_SPEAKER_BACK_RIGHT;
|
||||
case DevFmtX61: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
|
||||
SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_CENTER |
|
||||
SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT;
|
||||
|
|
@ -105,7 +102,7 @@ constexpr SLuint32 GetTypeRepresentation(DevFmtType type) noexcept
|
|||
|
||||
constexpr SLuint32 GetByteOrderEndianness() noexcept
|
||||
{
|
||||
if_constexpr(al::endian::native == al::endian::little)
|
||||
if(al::endian::native == al::endian::little)
|
||||
return SL_BYTEORDER_LITTLEENDIAN;
|
||||
return SL_BYTEORDER_BIGENDIAN;
|
||||
}
|
||||
|
|
@ -151,7 +148,7 @@ const char *res_str(SLresult result) noexcept
|
|||
|
||||
|
||||
struct OpenSLPlayback final : public BackendBase {
|
||||
OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OpenSLPlayback() override;
|
||||
|
||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||
|
|
@ -316,6 +313,9 @@ void OpenSLPlayback::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
/* There's only one device, so if it's already open, there's nothing to do. */
|
||||
if(mEngineObj) return;
|
||||
|
||||
// create engine
|
||||
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
||||
PRINTERR(result, "slCreateEngine");
|
||||
|
|
@ -629,7 +629,7 @@ ClockLatency OpenSLPlayback::getClockLatency()
|
|||
|
||||
|
||||
struct OpenSLCapture final : public BackendBase {
|
||||
OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OpenSLCapture() override;
|
||||
|
||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||
|
|
@ -810,8 +810,11 @@ void OpenSLCapture::open(const char* name)
|
|||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
const uint chunk_size{mDevice->UpdateSize * mFrameSize};
|
||||
const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
|
||||
|
||||
auto data = mRing->getWriteVector();
|
||||
std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
|
||||
std::fill_n(data.second.buf, data.second.len*chunk_size, silence);
|
||||
for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size);
|
||||
|
|
@ -875,6 +878,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
{
|
||||
const uint update_size{mDevice->UpdateSize};
|
||||
const uint chunk_size{update_size * mFrameSize};
|
||||
const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
|
||||
|
||||
/* Read the desired samples from the ring buffer then advance its read
|
||||
* pointer.
|
||||
|
|
@ -922,15 +926,20 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
{
|
||||
SLresult result{SL_RESULT_SUCCESS};
|
||||
auto wdata = mRing->getWriteVector();
|
||||
std::fill_n(wdata.first.buf, wdata.first.len*chunk_size, silence);
|
||||
for(size_t i{0u};i < wdata.first.len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(wdata.first.buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
for(size_t i{0u};i < wdata.second.len && SL_RESULT_SUCCESS == result;i++)
|
||||
if(wdata.second.len > 0)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(wdata.second.buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
std::fill_n(wdata.second.buf, wdata.second.len*chunk_size, silence);
|
||||
for(size_t i{0u};i < wdata.second.len && SL_RESULT_SUCCESS == result;i++)
|
||||
{
|
||||
result = VCALL(bufferQueue,Enqueue)(wdata.second.buf + chunk_size*i, chunk_size);
|
||||
PRINTERR(result, "bufferQueue->Enqueue");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -959,7 +968,7 @@ std::string OSLBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr OSLBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OpenSLPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_OSL_H
|
||||
#define BACKENDS_OSL_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct OSLBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/oss.h"
|
||||
#include "oss.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
|
@ -41,13 +41,13 @@
|
|||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alconfig.h"
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
|
@ -226,7 +226,7 @@ uint log2i(uint x)
|
|||
|
||||
|
||||
struct OSSPlayback final : public BackendBase {
|
||||
OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OSSPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
|
@ -249,7 +249,7 @@ struct OSSPlayback final : public BackendBase {
|
|||
OSSPlayback::~OSSPlayback()
|
||||
{
|
||||
if(mFd != -1)
|
||||
close(mFd);
|
||||
::close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
|
||||
|
|
@ -328,11 +328,15 @@ void OSSPlayback::open(const char *name)
|
|||
devname = iter->device_name.c_str();
|
||||
}
|
||||
|
||||
mFd = ::open(devname, O_WRONLY);
|
||||
if(mFd == -1)
|
||||
int fd{::open(devname, O_WRONLY)};
|
||||
if(fd == -1)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
|
||||
strerror(errno)};
|
||||
|
||||
if(mFd != -1)
|
||||
::close(mFd);
|
||||
mFd = fd;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +442,7 @@ void OSSPlayback::stop()
|
|||
|
||||
|
||||
struct OSScapture final : public BackendBase {
|
||||
OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OSScapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OSScapture() override;
|
||||
|
||||
int recordProc();
|
||||
|
|
@ -676,7 +680,7 @@ std::string OSSBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr OSSBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OSSPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_OSS_H
|
||||
#define BACKENDS_OSS_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct OSSBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
2008
Engine/lib/openal-soft/alc/backends/pipewire.cpp
Normal file
2008
Engine/lib/openal-soft/alc/backends/pipewire.cpp
Normal file
File diff suppressed because it is too large
Load diff
23
Engine/lib/openal-soft/alc/backends/pipewire.h
Normal file
23
Engine/lib/openal-soft/alc/backends/pipewire.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef BACKENDS_PIPEWIRE_H
|
||||
#define BACKENDS_PIPEWIRE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct DeviceBase;
|
||||
|
||||
struct PipeWireBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_PIPEWIRE_H */
|
||||
|
|
@ -20,15 +20,15 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/portaudio.h"
|
||||
#include "portaudio.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
|
@ -72,7 +72,7 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
|||
|
||||
|
||||
struct PortPlayback final : public BackendBase {
|
||||
PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PortPlayback() override;
|
||||
|
||||
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
|
|
@ -123,53 +123,58 @@ void PortPlayback::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
mUpdateSize = mDevice->UpdateSize;
|
||||
|
||||
PaStreamParameters params{};
|
||||
auto devidopt = ConfigValueInt(nullptr, "port", "device");
|
||||
if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
|
||||
else mParams.device = Pa_GetDefaultOutputDevice();
|
||||
mParams.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||
mParams.hostApiSpecificStreamInfo = nullptr;
|
||||
if(devidopt && *devidopt >= 0) params.device = *devidopt;
|
||||
else params.device = Pa_GetDefaultOutputDevice();
|
||||
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||
params.hostApiSpecificStreamInfo = nullptr;
|
||||
|
||||
mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
mParams.sampleFormat = paInt8;
|
||||
params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
mParams.sampleFormat = paUInt8;
|
||||
params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
mParams.sampleFormat = paInt16;
|
||||
params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
mParams.sampleFormat = paInt32;
|
||||
params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
mParams.sampleFormat = paFloat32;
|
||||
params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
|
||||
PaStream *stream{};
|
||||
PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize,
|
||||
paNoFlag, &PortPlayback::writeCallbackC, this)};
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(mParams.sampleFormat == paFloat32)
|
||||
if(params.sampleFormat == paFloat32)
|
||||
{
|
||||
mParams.sampleFormat = paInt16;
|
||||
params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||
Pa_GetErrorText(err)};
|
||||
}
|
||||
|
||||
Pa_CloseStream(mStream);
|
||||
mStream = stream;
|
||||
mParams = params;
|
||||
mUpdateSize = mDevice->UpdateSize;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +200,7 @@ bool PortPlayback::reset()
|
|||
return false;
|
||||
}
|
||||
|
||||
if(mParams.channelCount == 2)
|
||||
if(mParams.channelCount >= 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(mParams.channelCount == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
|
|
@ -226,7 +231,7 @@ void PortPlayback::stop()
|
|||
|
||||
|
||||
struct PortCapture final : public BackendBase {
|
||||
PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PortCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PortCapture() override;
|
||||
|
||||
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
|
|
@ -426,7 +431,7 @@ std::string PortBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr PortBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new PortPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_PORTAUDIO_H
|
||||
#define BACKENDS_PORTAUDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct PortBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -21,33 +21,49 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/pulseaudio.h"
|
||||
#include "pulseaudio.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <cstring>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "compat.h"
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alspan.h"
|
||||
#include "core/devformat.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "opthelpers.h"
|
||||
#include "strutils.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define PULSE_FUNCS(MAGIC) \
|
||||
MAGIC(pa_mainloop_new); \
|
||||
|
|
@ -220,78 +236,6 @@ constexpr pa_channel_map MonoChanMap{
|
|||
}
|
||||
};
|
||||
|
||||
al::optional<Channel> ChannelFromPulse(pa_channel_position_t chan)
|
||||
{
|
||||
switch(chan)
|
||||
{
|
||||
case PA_CHANNEL_POSITION_INVALID: break;
|
||||
case PA_CHANNEL_POSITION_MONO: return al::make_optional(FrontCenter);
|
||||
case PA_CHANNEL_POSITION_FRONT_LEFT: return al::make_optional(FrontLeft);
|
||||
case PA_CHANNEL_POSITION_FRONT_RIGHT: return al::make_optional(FrontRight);
|
||||
case PA_CHANNEL_POSITION_FRONT_CENTER: return al::make_optional(FrontCenter);
|
||||
case PA_CHANNEL_POSITION_REAR_CENTER: return al::make_optional(BackCenter);
|
||||
case PA_CHANNEL_POSITION_REAR_LEFT: return al::make_optional(BackLeft);
|
||||
case PA_CHANNEL_POSITION_REAR_RIGHT: return al::make_optional(BackRight);
|
||||
case PA_CHANNEL_POSITION_LFE: return al::make_optional(LFE);
|
||||
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: break;
|
||||
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: break;
|
||||
case PA_CHANNEL_POSITION_SIDE_LEFT: return al::make_optional(SideLeft);
|
||||
case PA_CHANNEL_POSITION_SIDE_RIGHT: return al::make_optional(SideRight);
|
||||
case PA_CHANNEL_POSITION_AUX0: break;
|
||||
case PA_CHANNEL_POSITION_AUX1: break;
|
||||
case PA_CHANNEL_POSITION_AUX2: break;
|
||||
case PA_CHANNEL_POSITION_AUX3: break;
|
||||
case PA_CHANNEL_POSITION_AUX4: break;
|
||||
case PA_CHANNEL_POSITION_AUX5: break;
|
||||
case PA_CHANNEL_POSITION_AUX6: break;
|
||||
case PA_CHANNEL_POSITION_AUX7: break;
|
||||
case PA_CHANNEL_POSITION_AUX8: break;
|
||||
case PA_CHANNEL_POSITION_AUX9: break;
|
||||
case PA_CHANNEL_POSITION_AUX10: break;
|
||||
case PA_CHANNEL_POSITION_AUX11: break;
|
||||
case PA_CHANNEL_POSITION_AUX12: break;
|
||||
case PA_CHANNEL_POSITION_AUX13: break;
|
||||
case PA_CHANNEL_POSITION_AUX14: break;
|
||||
case PA_CHANNEL_POSITION_AUX15: break;
|
||||
case PA_CHANNEL_POSITION_AUX16: break;
|
||||
case PA_CHANNEL_POSITION_AUX17: break;
|
||||
case PA_CHANNEL_POSITION_AUX18: break;
|
||||
case PA_CHANNEL_POSITION_AUX19: break;
|
||||
case PA_CHANNEL_POSITION_AUX20: break;
|
||||
case PA_CHANNEL_POSITION_AUX21: break;
|
||||
case PA_CHANNEL_POSITION_AUX22: break;
|
||||
case PA_CHANNEL_POSITION_AUX23: break;
|
||||
case PA_CHANNEL_POSITION_AUX24: break;
|
||||
case PA_CHANNEL_POSITION_AUX25: break;
|
||||
case PA_CHANNEL_POSITION_AUX26: break;
|
||||
case PA_CHANNEL_POSITION_AUX27: break;
|
||||
case PA_CHANNEL_POSITION_AUX28: break;
|
||||
case PA_CHANNEL_POSITION_AUX29: break;
|
||||
case PA_CHANNEL_POSITION_AUX30: break;
|
||||
case PA_CHANNEL_POSITION_AUX31: break;
|
||||
case PA_CHANNEL_POSITION_TOP_CENTER: return al::make_optional(TopCenter);
|
||||
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return al::make_optional(TopFrontLeft);
|
||||
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return al::make_optional(TopFrontRight);
|
||||
case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return al::make_optional(TopFrontCenter);
|
||||
case PA_CHANNEL_POSITION_TOP_REAR_LEFT: return al::make_optional(TopBackLeft);
|
||||
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return al::make_optional(TopBackRight);
|
||||
case PA_CHANNEL_POSITION_TOP_REAR_CENTER: return al::make_optional(TopBackCenter);
|
||||
case PA_CHANNEL_POSITION_MAX: break;
|
||||
}
|
||||
WARN("Unexpected channel enum %d (%s)\n", chan, pa_channel_position_to_string(chan));
|
||||
return al::nullopt;
|
||||
}
|
||||
|
||||
void SetChannelOrderFromMap(ALCdevice *device, const pa_channel_map &chanmap)
|
||||
{
|
||||
device->RealOut.ChannelIndex.fill(INVALID_CHANNEL_INDEX);
|
||||
for(uint i{0};i < chanmap.channels;++i)
|
||||
{
|
||||
if(auto label = ChannelFromPulse(chanmap.map[i]))
|
||||
device->RealOut.ChannelIndex[*label] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* *grumble* Don't use enums for bitflags. */
|
||||
constexpr inline pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs)
|
||||
|
|
@ -497,19 +441,13 @@ public:
|
|||
|
||||
pa_context *PulseMainloop::connectContext(std::unique_lock<std::mutex> &plock)
|
||||
{
|
||||
const char *name{"OpenAL Soft"};
|
||||
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
if(!binname.fname.empty())
|
||||
name = binname.fname.c_str();
|
||||
|
||||
if(!mMainloop)
|
||||
{
|
||||
mThread = std::thread{std::mem_fn(&PulseMainloop::mainloop_proc), this};
|
||||
mCondVar.wait(plock, [this]() noexcept { return mMainloop; });
|
||||
}
|
||||
|
||||
pa_context *context{pa_context_new(pa_mainloop_get_api(mMainloop), name)};
|
||||
pa_context *context{pa_context_new(pa_mainloop_get_api(mMainloop), nullptr)};
|
||||
if(!context) throw al::backend_exception{al::backend_error::OutOfMemory,
|
||||
"pa_context_new() failed"};
|
||||
|
||||
|
|
@ -661,7 +599,7 @@ PulseMainloop gGlobalMainloop;
|
|||
|
||||
|
||||
struct PulsePlayback final : public BackendBase {
|
||||
PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PulsePlayback() override;
|
||||
|
||||
void bufferAttrCallback(pa_stream *stream) noexcept;
|
||||
|
|
@ -698,6 +636,7 @@ struct PulsePlayback final : public BackendBase {
|
|||
|
||||
al::optional<std::string> mDeviceName{al::nullopt};
|
||||
|
||||
bool mIs51Rear{false};
|
||||
pa_buffer_attr mAttr;
|
||||
pa_sample_spec mSpec;
|
||||
|
||||
|
|
@ -770,15 +709,16 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int
|
|||
struct ChannelMap {
|
||||
DevFmtChannels fmt;
|
||||
pa_channel_map map;
|
||||
bool is_51rear;
|
||||
};
|
||||
static constexpr std::array<ChannelMap,7> chanmaps{{
|
||||
{ DevFmtX71, X71ChanMap },
|
||||
{ DevFmtX61, X61ChanMap },
|
||||
{ DevFmtX51, X51ChanMap },
|
||||
{ DevFmtX51Rear, X51RearChanMap },
|
||||
{ DevFmtQuad, QuadChanMap },
|
||||
{ DevFmtStereo, StereoChanMap },
|
||||
{ DevFmtMono, MonoChanMap }
|
||||
{ DevFmtX71, X71ChanMap, false },
|
||||
{ DevFmtX61, X61ChanMap, false },
|
||||
{ DevFmtX51, X51ChanMap, false },
|
||||
{ DevFmtX51, X51RearChanMap, true },
|
||||
{ DevFmtQuad, QuadChanMap, false },
|
||||
{ DevFmtStereo, StereoChanMap, false },
|
||||
{ DevFmtMono, MonoChanMap, false }
|
||||
}};
|
||||
|
||||
if(eol)
|
||||
|
|
@ -795,9 +735,11 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int
|
|||
{
|
||||
if(!mDevice->Flags.test(ChannelsRequest))
|
||||
mDevice->FmtChans = chaniter->fmt;
|
||||
mIs51Rear = chaniter->is_51rear;
|
||||
}
|
||||
else
|
||||
{
|
||||
mIs51Rear = false;
|
||||
char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{};
|
||||
pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map);
|
||||
WARN("Failed to find format for channel map:\n %s\n", chanmap_str);
|
||||
|
|
@ -805,8 +747,8 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int
|
|||
|
||||
if(info->active_port)
|
||||
TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description);
|
||||
mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo
|
||||
&& info->active_port && strcmp(info->active_port->name, "analog-output-headphones") == 0);
|
||||
mDevice->Flags.set(DirectEar, (info->active_port
|
||||
&& strcmp(info->active_port->name, "analog-output-headphones") == 0));
|
||||
}
|
||||
|
||||
void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
|
||||
|
|
@ -846,7 +788,8 @@ void PulsePlayback::open(const char *name)
|
|||
}
|
||||
|
||||
auto plock = mMainloop.getUniqueLock();
|
||||
mContext = mMainloop.connectContext(plock);
|
||||
if(!mContext)
|
||||
mContext = mMainloop.connectContext(plock);
|
||||
|
||||
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
|
||||
PA_STREAM_FIX_CHANNELS};
|
||||
|
|
@ -864,8 +807,18 @@ void PulsePlayback::open(const char *name)
|
|||
if(defname) pulse_name = defname->c_str();
|
||||
}
|
||||
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
||||
mStream = mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
|
||||
BackendType::Playback);
|
||||
pa_stream *stream{mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec,
|
||||
nullptr, BackendType::Playback)};
|
||||
if(mStream)
|
||||
{
|
||||
pa_stream_set_state_callback(mStream, nullptr, nullptr);
|
||||
pa_stream_set_moved_callback(mStream, nullptr, nullptr);
|
||||
pa_stream_set_write_callback(mStream, nullptr, nullptr);
|
||||
pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
|
||||
pa_stream_disconnect(mStream);
|
||||
pa_stream_unref(mStream);
|
||||
}
|
||||
mStream = stream;
|
||||
|
||||
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
|
||||
mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
|
||||
|
|
@ -934,10 +887,7 @@ bool PulsePlayback::reset()
|
|||
chanmap = QuadChanMap;
|
||||
break;
|
||||
case DevFmtX51:
|
||||
chanmap = X51ChanMap;
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
chanmap = X51RearChanMap;
|
||||
chanmap = (mIs51Rear ? X51RearChanMap : X51ChanMap);
|
||||
break;
|
||||
case DevFmtX61:
|
||||
chanmap = X61ChanMap;
|
||||
|
|
@ -946,7 +896,7 @@ bool PulsePlayback::reset()
|
|||
chanmap = X71ChanMap;
|
||||
break;
|
||||
}
|
||||
SetChannelOrderFromMap(mDevice, chanmap);
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
|
|
@ -1029,34 +979,34 @@ void PulsePlayback::start()
|
|||
{
|
||||
auto plock = mMainloop.getUniqueLock();
|
||||
|
||||
pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
|
||||
pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
|
||||
&mMainloop)};
|
||||
|
||||
/* Write some (silent) samples to fill the prebuf amount if needed. */
|
||||
if(size_t prebuf{mAttr.prebuf})
|
||||
/* Write some (silent) samples to fill the buffer before we start feeding
|
||||
* it newly mixed samples.
|
||||
*/
|
||||
if(size_t todo{pa_stream_writable_size(mStream)})
|
||||
{
|
||||
prebuf = minz(prebuf, pa_stream_writable_size(mStream));
|
||||
|
||||
void *buf{pa_xmalloc(prebuf)};
|
||||
void *buf{pa_xmalloc(todo)};
|
||||
switch(mSpec.format)
|
||||
{
|
||||
case PA_SAMPLE_U8:
|
||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x80);
|
||||
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x80);
|
||||
break;
|
||||
case PA_SAMPLE_ALAW:
|
||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0xD5);
|
||||
std::fill_n(static_cast<uint8_t*>(buf), todo, 0xD5);
|
||||
break;
|
||||
case PA_SAMPLE_ULAW:
|
||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x7f);
|
||||
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x7f);
|
||||
break;
|
||||
default:
|
||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x00);
|
||||
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x00);
|
||||
break;
|
||||
}
|
||||
pa_stream_write(mStream, buf, prebuf, pa_xfree, 0, PA_SEEK_RELATIVE);
|
||||
pa_stream_write(mStream, buf, todo, pa_xfree, 0, PA_SEEK_RELATIVE);
|
||||
}
|
||||
|
||||
pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
|
||||
pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
|
||||
&mMainloop)};
|
||||
|
||||
mMainloop.waitForOperation(op, plock);
|
||||
}
|
||||
|
||||
|
|
@ -1103,7 +1053,7 @@ ClockLatency PulsePlayback::getClockLatency()
|
|||
|
||||
|
||||
struct PulseCapture final : public BackendBase {
|
||||
PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PulseCapture() override;
|
||||
|
||||
void streamStateCallback(pa_stream *stream) noexcept;
|
||||
|
|
@ -1217,9 +1167,6 @@ void PulseCapture::open(const char *name)
|
|||
case DevFmtX51:
|
||||
chanmap = X51ChanMap;
|
||||
break;
|
||||
case DevFmtX51Rear:
|
||||
chanmap = X51RearChanMap;
|
||||
break;
|
||||
case DevFmtX61:
|
||||
chanmap = X61ChanMap;
|
||||
break;
|
||||
|
|
@ -1230,7 +1177,7 @@ void PulseCapture::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
||||
DevFmtChannelsString(mDevice->FmtChans)};
|
||||
}
|
||||
SetChannelOrderFromMap(mDevice, chanmap);
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
|
|
@ -1505,7 +1452,7 @@ std::string PulseBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr PulseBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new PulsePlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_PULSEAUDIO_H
|
||||
#define BACKENDS_PULSEAUDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
class PulseBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,16 +20,16 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/sdl2.h"
|
||||
#include "sdl2.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "almalloc.h"
|
||||
#include "alu.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
|
@ -46,7 +46,7 @@ namespace {
|
|||
constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
||||
|
||||
struct Sdl2Backend final : public BackendBase {
|
||||
Sdl2Backend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~Sdl2Backend() override;
|
||||
|
||||
void audioCallback(Uint8 *stream, int len) noexcept;
|
||||
|
|
@ -99,59 +99,64 @@ void Sdl2Backend::open(const char *name)
|
|||
case DevFmtFloat: want.format = AUDIO_F32; break;
|
||||
}
|
||||
want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
|
||||
want.samples = static_cast<Uint16>(mDevice->UpdateSize);
|
||||
want.samples = static_cast<Uint16>(minu(mDevice->UpdateSize, 8192));
|
||||
want.callback = &Sdl2Backend::audioCallbackC;
|
||||
want.userdata = this;
|
||||
|
||||
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
|
||||
* necessarily the first in the list.
|
||||
*/
|
||||
SDL_AudioDeviceID devid;
|
||||
if(!name || strcmp(name, defaultDeviceName) == 0)
|
||||
mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
else
|
||||
{
|
||||
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
||||
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
|
||||
mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
||||
devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
else
|
||||
mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
}
|
||||
if(mDeviceID == 0)
|
||||
if(!devid)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
||||
|
||||
mDevice->Frequency = static_cast<uint>(have.freq);
|
||||
|
||||
if(have.channels == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
else if(have.channels == 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
DevFmtChannels devchans{};
|
||||
if(have.channels >= 2)
|
||||
devchans = DevFmtStereo;
|
||||
else if(have.channels == 1)
|
||||
devchans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
SDL_CloseAudioDevice(devid);
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Unhandled SDL channel count: %d", int{have.channels}};
|
||||
}
|
||||
|
||||
DevFmtType devtype{};
|
||||
switch(have.format)
|
||||
{
|
||||
case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
|
||||
case AUDIO_S8: mDevice->FmtType = DevFmtByte; break;
|
||||
case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break;
|
||||
case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break;
|
||||
case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
|
||||
case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
|
||||
case AUDIO_U8: devtype = DevFmtUByte; break;
|
||||
case AUDIO_S8: devtype = DevFmtByte; break;
|
||||
case AUDIO_U16SYS: devtype = DevFmtUShort; break;
|
||||
case AUDIO_S16SYS: devtype = DevFmtShort; break;
|
||||
case AUDIO_S32SYS: devtype = DevFmtInt; break;
|
||||
case AUDIO_F32SYS: devtype = DevFmtFloat; break;
|
||||
default:
|
||||
SDL_CloseAudioDevice(devid);
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x",
|
||||
have.format};
|
||||
}
|
||||
mDevice->UpdateSize = have.samples;
|
||||
mDevice->BufferSize = have.samples * 2; /* SDL always (tries to) use two periods. */
|
||||
|
||||
mFrameSize = mDevice->frameSizeFromFmt();
|
||||
mFrequency = mDevice->Frequency;
|
||||
mFmtChans = mDevice->FmtChans;
|
||||
mFmtType = mDevice->FmtType;
|
||||
mUpdateSize = mDevice->UpdateSize;
|
||||
if(mDeviceID)
|
||||
SDL_CloseAudioDevice(mDeviceID);
|
||||
mDeviceID = devid;
|
||||
|
||||
mFrameSize = BytesFromDevFmt(devtype) * have.channels;
|
||||
mFrequency = static_cast<uint>(have.freq);
|
||||
mFmtChans = devchans;
|
||||
mFmtType = devtype;
|
||||
mUpdateSize = have.samples;
|
||||
|
||||
mDevice->DeviceName = name ? name : defaultDeviceName;
|
||||
}
|
||||
|
|
@ -162,7 +167,7 @@ bool Sdl2Backend::reset()
|
|||
mDevice->FmtChans = mFmtChans;
|
||||
mDevice->FmtType = mFmtType;
|
||||
mDevice->UpdateSize = mUpdateSize;
|
||||
mDevice->BufferSize = mUpdateSize * 2;
|
||||
mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */
|
||||
setDefaultWFXChannelOrder();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -208,7 +213,7 @@ std::string SDL2BackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr SDL2BackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr SDL2BackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new Sdl2Backend{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_SDL2_H
|
||||
#define BACKENDS_SDL2_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct SDL2BackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,8 +20,9 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/sndio.h"
|
||||
#include "sndio.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -29,8 +30,9 @@
|
|||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
|
@ -43,9 +45,14 @@ namespace {
|
|||
|
||||
static const char sndio_device[] = "SndIO Default";
|
||||
|
||||
struct SioPar : public sio_par {
|
||||
SioPar() { sio_initpar(this); }
|
||||
|
||||
void clear() { sio_initpar(this); }
|
||||
};
|
||||
|
||||
struct SndioPlayback final : public BackendBase {
|
||||
SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~SndioPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
|
@ -56,6 +63,7 @@ struct SndioPlayback final : public BackendBase {
|
|||
void stop() override;
|
||||
|
||||
sio_hdl *mSndHandle{nullptr};
|
||||
uint mFrameStep{};
|
||||
|
||||
al::vector<al::byte> mBuffer;
|
||||
|
||||
|
|
@ -74,16 +82,8 @@ SndioPlayback::~SndioPlayback()
|
|||
|
||||
int SndioPlayback::mixerProc()
|
||||
{
|
||||
sio_par par;
|
||||
sio_initpar(&par);
|
||||
if(!sio_getpar(mSndHandle, &par))
|
||||
{
|
||||
mDevice->handleDisconnect("Failed to get device parameters");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const size_t frameStep{par.pchan};
|
||||
const size_t frameSize{frameStep * par.bps};
|
||||
const size_t frameStep{mFrameStep};
|
||||
const size_t frameSize{frameStep * mDevice->bytesFromFmt()};
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
|
|
@ -91,22 +91,20 @@ int SndioPlayback::mixerProc()
|
|||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
al::byte *WritePtr{mBuffer.data()};
|
||||
size_t len{mBuffer.size()};
|
||||
al::span<al::byte> buffer{mBuffer};
|
||||
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(len/frameSize), frameStep);
|
||||
while(len > 0 && !mKillNow.load(std::memory_order_acquire))
|
||||
mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize),
|
||||
frameStep);
|
||||
while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire))
|
||||
{
|
||||
size_t wrote{sio_write(mSndHandle, WritePtr, len)};
|
||||
size_t wrote{sio_write(mSndHandle, buffer.data(), buffer.size())};
|
||||
if(wrote == 0)
|
||||
{
|
||||
ERR("sio_write failed\n");
|
||||
mDevice->handleDisconnect("Failed to write playback samples");
|
||||
break;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
buffer = buffer.subspan(wrote);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,34 +120,24 @@ void SndioPlayback::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
|
||||
if(mSndHandle == nullptr)
|
||||
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
|
||||
if(!sndHandle)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
|
||||
|
||||
if(mSndHandle)
|
||||
sio_close(mSndHandle);
|
||||
mSndHandle = sndHandle;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool SndioPlayback::reset()
|
||||
{
|
||||
sio_par par;
|
||||
sio_initpar(&par);
|
||||
SioPar par;
|
||||
|
||||
par.rate = mDevice->Frequency;
|
||||
switch(mDevice->FmtChans)
|
||||
{
|
||||
case DevFmtMono : par.pchan = 1; break;
|
||||
case DevFmtQuad : par.pchan = 4; break;
|
||||
case DevFmtX51Rear: // fall-through - "Similar to 5.1, except using rear channels instead of sides"
|
||||
case DevFmtX51 : par.pchan = 6; break;
|
||||
case DevFmtX61 : par.pchan = 7; break;
|
||||
case DevFmtX71 : par.pchan = 8; break;
|
||||
|
||||
// fall back to stereo for Ambi3D
|
||||
case DevFmtAmbi3D : // fall-through
|
||||
case DevFmtStereo : par.pchan = 2; break;
|
||||
}
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
auto tryfmt = mDevice->FmtType;
|
||||
retry_params:
|
||||
switch(tryfmt)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bits = 8;
|
||||
|
|
@ -159,7 +147,6 @@ bool SndioPlayback::reset()
|
|||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtShort:
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
|
|
@ -168,6 +155,7 @@ bool SndioPlayback::reset()
|
|||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtInt:
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
|
|
@ -177,70 +165,64 @@ bool SndioPlayback::reset()
|
|||
par.sig = 0;
|
||||
break;
|
||||
}
|
||||
par.bps = SIO_BPS(par.bits);
|
||||
par.le = SIO_LE_NATIVE;
|
||||
par.msb = 1;
|
||||
|
||||
par.rate = mDevice->Frequency;
|
||||
par.pchan = mDevice->channelsFromFmt();
|
||||
|
||||
par.round = mDevice->UpdateSize;
|
||||
par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
|
||||
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
|
||||
|
||||
if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return false;
|
||||
try {
|
||||
if(!sio_setpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set device parameters"};
|
||||
|
||||
par.clear();
|
||||
if(!sio_getpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to get device parameters"};
|
||||
|
||||
if(par.bps > 1 && par.le != SIO_LE_NATIVE)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"%s-endian samples not supported", par.le ? "Little" : "Big"};
|
||||
if(par.bits < par.bps*8 && !par.msb)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8};
|
||||
if(par.pchan < 1)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"No playback channels on device"};
|
||||
}
|
||||
catch(al::backend_exception &e) {
|
||||
if(tryfmt == DevFmtShort)
|
||||
throw;
|
||||
par.clear();
|
||||
tryfmt = DevFmtShort;
|
||||
goto retry_params;
|
||||
}
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
{
|
||||
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
|
||||
return false;
|
||||
}
|
||||
if(par.le != SIO_LE_NATIVE)
|
||||
{
|
||||
ERR("Non-native-endian samples not supported (got %s-endian)\n",
|
||||
par.le ? "little" : "big");
|
||||
return false;
|
||||
}
|
||||
|
||||
mDevice->Frequency = par.rate;
|
||||
|
||||
if(par.pchan < 2)
|
||||
{
|
||||
if(mDevice->FmtChans != DevFmtMono)
|
||||
{
|
||||
WARN("Got %u channel for %s\n", par.pchan, DevFmtChannelsString(mDevice->FmtChans));
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
}
|
||||
}
|
||||
else if((par.pchan == 2 && mDevice->FmtChans != DevFmtStereo)
|
||||
|| par.pchan == 3
|
||||
|| (par.pchan == 4 && mDevice->FmtChans != DevFmtQuad)
|
||||
|| par.pchan == 5
|
||||
|| (par.pchan == 6 && mDevice->FmtChans != DevFmtX51 && mDevice->FmtChans != DevFmtX51Rear)
|
||||
|| (par.pchan == 7 && mDevice->FmtChans != DevFmtX61)
|
||||
|| (par.pchan == 8 && mDevice->FmtChans != DevFmtX71)
|
||||
|| par.pchan > 8)
|
||||
{
|
||||
WARN("Got %u channels for %s\n", par.pchan, DevFmtChannelsString(mDevice->FmtChans));
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
}
|
||||
|
||||
if(par.bits == 8 && par.sig == 1)
|
||||
mDevice->FmtType = DevFmtByte;
|
||||
else if(par.bits == 8 && par.sig == 0)
|
||||
mDevice->FmtType = DevFmtUByte;
|
||||
else if(par.bits == 16 && par.sig == 1)
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
else if(par.bits == 16 && par.sig == 0)
|
||||
mDevice->FmtType = DevFmtUShort;
|
||||
else if(par.bits == 32 && par.sig == 1)
|
||||
mDevice->FmtType = DevFmtInt;
|
||||
else if(par.bits == 32 && par.sig == 0)
|
||||
mDevice->FmtType = DevFmtUInt;
|
||||
if(par.bps == 1)
|
||||
mDevice->FmtType = (par.sig==1) ? DevFmtByte : DevFmtUByte;
|
||||
else if(par.bps == 2)
|
||||
mDevice->FmtType = (par.sig==1) ? DevFmtShort : DevFmtUShort;
|
||||
else if(par.bps == 4)
|
||||
mDevice->FmtType = (par.sig==1) ? DevFmtInt : DevFmtUInt;
|
||||
else
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Unhandled sample format: %s %u-bit", (par.sig?"signed":"unsigned"), par.bps*8};
|
||||
|
||||
mFrameStep = par.pchan;
|
||||
if(par.pchan != mDevice->channelsFromFmt())
|
||||
{
|
||||
ERR("Unhandled sample format: %s %u-bit\n", (par.sig?"signed":"unsigned"), par.bits);
|
||||
return false;
|
||||
WARN("Got %u channel%s for %s\n", par.pchan, (par.pchan==1)?"":"s",
|
||||
DevFmtChannelsString(mDevice->FmtChans));
|
||||
if(par.pchan < 2) mDevice->FmtChans = DevFmtMono;
|
||||
else mDevice->FmtChans = DevFmtStereo;
|
||||
}
|
||||
mDevice->Frequency = par.rate;
|
||||
|
||||
setDefaultChannelOrder();
|
||||
|
||||
|
|
@ -287,8 +269,13 @@ void SndioPlayback::stop()
|
|||
}
|
||||
|
||||
|
||||
/* TODO: This could be improved by avoiding the ring buffer and record thread,
|
||||
* counting the available samples with the sio_onmove callback and reading
|
||||
* directly from the device. However, this depends on reasonable support for
|
||||
* capture buffer sizes apps may request.
|
||||
*/
|
||||
struct SndioCapture final : public BackendBase {
|
||||
SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~SndioCapture() override;
|
||||
|
||||
int recordProc();
|
||||
|
|
@ -323,40 +310,65 @@ int SndioCapture::recordProc()
|
|||
|
||||
const uint frameSize{mDevice->frameSizeFromFmt()};
|
||||
|
||||
int nfds_pre{sio_nfds(mSndHandle)};
|
||||
if(nfds_pre <= 0)
|
||||
{
|
||||
mDevice->handleDisconnect("Incorrect return value from sio_nfds(): %d", nfds_pre);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto fds = std::make_unique<pollfd[]>(static_cast<uint>(nfds_pre));
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
auto data = mRing->getWriteVector();
|
||||
size_t todo{data.first.len + data.second.len};
|
||||
if(todo == 0)
|
||||
/* Wait until there's some samples to read. */
|
||||
const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)};
|
||||
if(nfds <= 0)
|
||||
{
|
||||
static char junk[4096];
|
||||
sio_read(mSndHandle, junk,
|
||||
minz(sizeof(junk)/frameSize, mDevice->UpdateSize)*frameSize);
|
||||
mDevice->handleDisconnect("Failed to get polling fds: %d", nfds);
|
||||
break;
|
||||
}
|
||||
int pollres{::poll(fds.get(), static_cast<uint>(nfds), 2000)};
|
||||
if(pollres < 0)
|
||||
{
|
||||
if(errno == EINTR) continue;
|
||||
mDevice->handleDisconnect("Poll error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if(pollres == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t total{0u};
|
||||
data.first.len *= frameSize;
|
||||
data.second.len *= frameSize;
|
||||
todo = minz(todo, mDevice->UpdateSize) * frameSize;
|
||||
while(total < todo)
|
||||
const int revents{sio_revents(mSndHandle, fds.get())};
|
||||
if((revents&POLLHUP))
|
||||
{
|
||||
if(!data.first.len)
|
||||
data.first = data.second;
|
||||
|
||||
size_t got{sio_read(mSndHandle, data.first.buf, minz(todo-total, data.first.len))};
|
||||
if(!got)
|
||||
{
|
||||
mDevice->handleDisconnect("Failed to read capture samples");
|
||||
break;
|
||||
}
|
||||
|
||||
data.first.buf += got;
|
||||
data.first.len -= got;
|
||||
total += got;
|
||||
mDevice->handleDisconnect("Got POLLHUP from poll events");
|
||||
break;
|
||||
}
|
||||
if(!(revents&POLLIN))
|
||||
continue;
|
||||
|
||||
auto data = mRing->getWriteVector();
|
||||
al::span<al::byte> buffer{data.first.buf, data.first.len*frameSize};
|
||||
while(!buffer.empty())
|
||||
{
|
||||
size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
|
||||
if(got == 0) break;
|
||||
|
||||
mRing->writeAdvance(got / frameSize);
|
||||
buffer = buffer.subspan(got);
|
||||
if(buffer.empty())
|
||||
{
|
||||
data = mRing->getWriteVector();
|
||||
buffer = {data.first.buf, data.first.len*frameSize};
|
||||
}
|
||||
}
|
||||
if(buffer.empty())
|
||||
{
|
||||
/* Got samples to read, but no place to store it. Drop it. */
|
||||
static char junk[4096];
|
||||
sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize));
|
||||
}
|
||||
mRing->writeAdvance(total / frameSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -371,76 +383,80 @@ void SndioCapture::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
mSndHandle = sio_open(nullptr, SIO_REC, 0);
|
||||
mSndHandle = sio_open(nullptr, SIO_REC, true);
|
||||
if(mSndHandle == nullptr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
|
||||
|
||||
sio_par par;
|
||||
sio_initpar(&par);
|
||||
|
||||
SioPar par;
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bps = 1;
|
||||
par.bits = 8;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bps = 1;
|
||||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
par.bps = 2;
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bps = 2;
|
||||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
par.bps = 4;
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bps = 4;
|
||||
par.bits = 32;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
|
||||
}
|
||||
par.bits = par.bps * 8;
|
||||
par.bps = SIO_BPS(par.bits);
|
||||
par.le = SIO_LE_NATIVE;
|
||||
par.msb = SIO_LE_NATIVE ? 0 : 1;
|
||||
par.msb = 1;
|
||||
par.rchan = mDevice->channelsFromFmt();
|
||||
par.rate = mDevice->Frequency;
|
||||
|
||||
par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
|
||||
par.round = minu(par.appbufsz, mDevice->Frequency/40);
|
||||
|
||||
mDevice->UpdateSize = par.round;
|
||||
mDevice->BufferSize = par.appbufsz;
|
||||
par.round = minu(par.appbufsz/2, mDevice->Frequency/40);
|
||||
|
||||
if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set device praameters"};
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
if(par.bps > 1 && par.le != SIO_LE_NATIVE)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"%s-endian samples not supported", par.le ? "Little" : "Big"};
|
||||
if(par.bits < par.bps*8 && !par.msb)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Padded samples not supported (got %u of %u bits)", par.bits, par.bps*8};
|
||||
|
||||
if(!((mDevice->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0)
|
||||
|| (mDevice->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0)
|
||||
|| (mDevice->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0)
|
||||
|| (mDevice->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0)
|
||||
|| (mDevice->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0)
|
||||
|| (mDevice->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0))
|
||||
|| mDevice->channelsFromFmt() != par.rchan || mDevice->Frequency != par.rate)
|
||||
auto match_fmt = [](DevFmtType fmttype, const sio_par &p) -> bool
|
||||
{
|
||||
return (fmttype == DevFmtByte && p.bps == 1 && p.sig != 0)
|
||||
|| (fmttype == DevFmtUByte && p.bps == 1 && p.sig == 0)
|
||||
|| (fmttype == DevFmtShort && p.bps == 2 && p.sig != 0)
|
||||
|| (fmttype == DevFmtUShort && p.bps == 2 && p.sig == 0)
|
||||
|| (fmttype == DevFmtInt && p.bps == 4 && p.sig != 0)
|
||||
|| (fmttype == DevFmtUInt && p.bps == 4 && p.sig == 0);
|
||||
};
|
||||
if(!match_fmt(mDevice->FmtType, par) || mDevice->channelsFromFmt() != par.rchan
|
||||
|| mDevice->Frequency != par.rate)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead",
|
||||
DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
|
||||
mDevice->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate};
|
||||
mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate};
|
||||
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false);
|
||||
mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
|
||||
mDevice->UpdateSize = par.round;
|
||||
|
||||
setDefaultChannelOrder();
|
||||
|
||||
|
|
@ -507,7 +523,7 @@ std::string SndIOBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new SndioPlayback{device}};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_SNDIO_H
|
||||
#define BACKENDS_SNDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct SndIOBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/solaris.h"
|
||||
#include "solaris.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
|
@ -34,15 +34,15 @@
|
|||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "albyte.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "compat.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
|
@ -58,7 +58,7 @@ std::string solaris_driver{"/dev/audio"};
|
|||
|
||||
|
||||
struct SolarisBackend final : public BackendBase {
|
||||
SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~SolarisBackend() override;
|
||||
|
||||
int mixerProc();
|
||||
|
|
@ -70,6 +70,7 @@ struct SolarisBackend final : public BackendBase {
|
|||
|
||||
int mFd{-1};
|
||||
|
||||
uint mFrameStep{};
|
||||
al::vector<al::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
|
|
@ -147,11 +148,15 @@ void SolarisBackend::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
|
||||
mFd = ::open(solaris_driver.c_str(), O_WRONLY);
|
||||
if(mFd == -1)
|
||||
int fd{::open(solaris_driver.c_str(), O_WRONLY)};
|
||||
if(fd == -1)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s",
|
||||
solaris_driver.c_str(), strerror(errno)};
|
||||
|
||||
if(mFd != -1)
|
||||
::close(mFd);
|
||||
mFd = fd;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
|
@ -161,12 +166,7 @@ bool SolarisBackend::reset()
|
|||
AUDIO_INITINFO(&info);
|
||||
|
||||
info.play.sample_rate = mDevice->Frequency;
|
||||
|
||||
if(mDevice->FmtChans != DevFmtMono)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
uint numChannels{mDevice->channelsFromFmt()};
|
||||
info.play.channels = numChannels;
|
||||
|
||||
info.play.channels = mDevice->channelsFromFmt();
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
|
|
@ -188,9 +188,7 @@ bool SolarisBackend::reset()
|
|||
info.play.encoding = AUDIO_ENCODING_LINEAR;
|
||||
break;
|
||||
}
|
||||
|
||||
uint frameSize{numChannels * mDevice->bytesFromFmt()};
|
||||
info.play.buffer_size = mDevice->BufferSize * frameSize;
|
||||
info.play.buffer_size = mDevice->BufferSize * mDevice->frameSizeFromFmt();
|
||||
|
||||
if(ioctl(mFd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
|
|
@ -200,28 +198,39 @@ bool SolarisBackend::reset()
|
|||
|
||||
if(mDevice->channelsFromFmt() != info.play.channels)
|
||||
{
|
||||
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(mDevice->FmtChans),
|
||||
info.play.channels);
|
||||
return false;
|
||||
if(info.play.channels >= 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(info.play.channels == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
else
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Got %u device channels", info.play.channels};
|
||||
}
|
||||
|
||||
if(!((info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8 && mDevice->FmtType == DevFmtUByte) ||
|
||||
(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtByte) ||
|
||||
(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtShort) ||
|
||||
(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR && mDevice->FmtType == DevFmtInt)))
|
||||
if(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8)
|
||||
mDevice->FmtType = DevFmtUByte;
|
||||
else if(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR)
|
||||
mDevice->FmtType = DevFmtByte;
|
||||
else if(info.play.precision == 16 && info.play.encoding == AUDIO_ENCODING_LINEAR)
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
else if(info.play.precision == 32 && info.play.encoding == AUDIO_ENCODING_LINEAR)
|
||||
mDevice->FmtType = DevFmtInt;
|
||||
else
|
||||
{
|
||||
ERR("Could not set %s samples, got %d (0x%x)\n", DevFmtTypeString(mDevice->FmtType),
|
||||
info.play.precision, info.play.encoding);
|
||||
ERR("Got unhandled sample type: %d (0x%x)\n", info.play.precision, info.play.encoding);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint frame_size{mDevice->bytesFromFmt() * info.play.channels};
|
||||
mFrameStep = info.play.channels;
|
||||
mDevice->Frequency = info.play.sample_rate;
|
||||
mDevice->BufferSize = info.play.buffer_size / frameSize;
|
||||
mDevice->BufferSize = info.play.buffer_size / frame_size;
|
||||
/* How to get the actual period size/count? */
|
||||
mDevice->UpdateSize = mDevice->BufferSize / 2;
|
||||
|
||||
setDefaultChannelOrder();
|
||||
|
||||
mBuffer.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
|
||||
mBuffer.resize(mDevice->UpdateSize * size_t{frame_size});
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
|
||||
|
||||
return true;
|
||||
|
|
@ -286,7 +295,7 @@ std::string SolarisBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new SolarisBackend{device}};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue