diff options
Diffstat (limited to 'extern/audaspace/src/devices')
-rw-r--r-- | extern/audaspace/src/devices/DefaultSynchronizer.cpp | 49 | ||||
-rw-r--r-- | extern/audaspace/src/devices/DeviceManager.cpp | 117 | ||||
-rw-r--r-- | extern/audaspace/src/devices/NULLDevice.cpp | 193 | ||||
-rw-r--r-- | extern/audaspace/src/devices/ReadDevice.cpp | 69 | ||||
-rw-r--r-- | extern/audaspace/src/devices/SoftwareDevice.cpp | 989 |
5 files changed, 1417 insertions, 0 deletions
diff --git a/extern/audaspace/src/devices/DefaultSynchronizer.cpp b/extern/audaspace/src/devices/DefaultSynchronizer.cpp new file mode 100644 index 00000000000..aa8945dadaa --- /dev/null +++ b/extern/audaspace/src/devices/DefaultSynchronizer.cpp @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/DefaultSynchronizer.h" +#include "devices/IHandle.h" + +AUD_NAMESPACE_BEGIN + +void DefaultSynchronizer::seek(std::shared_ptr<IHandle> handle, float time) +{ + handle->seek(time); +} + +float DefaultSynchronizer::getPosition(std::shared_ptr<IHandle> handle) +{ + return handle->getPosition(); +} + +void DefaultSynchronizer::play() +{ +} + +void DefaultSynchronizer::stop() +{ +} + +void DefaultSynchronizer::setSyncCallback(ISynchronizer::syncFunction function, void* data) +{ +} + +int DefaultSynchronizer::isPlaying() +{ + return -1; +} + +AUD_NAMESPACE_END diff --git a/extern/audaspace/src/devices/DeviceManager.cpp b/extern/audaspace/src/devices/DeviceManager.cpp new file mode 100644 index 00000000000..2ebc3d58c86 --- /dev/null +++ b/extern/audaspace/src/devices/DeviceManager.cpp @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/DeviceManager.h" +#include "devices/IDeviceFactory.h" +#include "devices/IDevice.h" +#include "devices/I3DDevice.h" + +#include <limits> +#include <string> +#include <algorithm> + +AUD_NAMESPACE_BEGIN + +std::unordered_map<std::string, std::shared_ptr<IDeviceFactory>> DeviceManager::m_factories; +std::shared_ptr<IDevice> DeviceManager::m_device; + +void DeviceManager::registerDevice(std::string name, std::shared_ptr<IDeviceFactory> factory) +{ + m_factories[name] = factory; +} + +std::shared_ptr<IDeviceFactory> DeviceManager::getDeviceFactory(std::string name) +{ + return m_factories[name]; +} + +std::shared_ptr<IDeviceFactory> DeviceManager::getDefaultDeviceFactory() +{ + int min = std::numeric_limits<int>::min(); + + std::shared_ptr<IDeviceFactory> result; + + for(auto factory : m_factories) + { + if(factory.second->getPriority() >= min) + { + result = factory.second; + min = result->getPriority(); + } + } + + return result; +} + +void DeviceManager::setDevice(std::shared_ptr<IDevice> device) +{ + m_device = device; +} + +void DeviceManager::openDevice(std::string name) +{ + setDevice(getDeviceFactory(name)->openDevice()); +} + +void DeviceManager::openDefaultDevice() +{ + setDevice(getDefaultDeviceFactory()->openDevice()); +} + +void DeviceManager::releaseDevice() +{ + m_device = nullptr; +} + +std::shared_ptr<IDevice> DeviceManager::getDevice() +{ + return m_device; +} + +std::shared_ptr<I3DDevice> DeviceManager::get3DDevice() +{ + return std::dynamic_pointer_cast<I3DDevice>(m_device); +} + +std::vector<std::string> DeviceManager::getAvailableDeviceNames() +{ + struct DeviceNamePriority { + std::string name; + int priority; + }; + + std::vector<DeviceNamePriority> devices; + devices.reserve(m_factories.size()); + + for(const auto& pair : m_factories) + devices.push_back({pair.first, pair.second->getPriority()}); + + auto sort = [](const DeviceNamePriority& lhs, const DeviceNamePriority& rhs){ + return lhs.priority > rhs.priority; + }; + + std::sort(devices.begin(), devices.end(), sort); + + std::vector<std::string> names; + names.reserve(devices.size()); + + for(const auto& device : devices) + names.push_back(device.name); + + return names; +} + +AUD_NAMESPACE_END diff --git a/extern/audaspace/src/devices/NULLDevice.cpp b/extern/audaspace/src/devices/NULLDevice.cpp new file mode 100644 index 00000000000..a82537f43b2 --- /dev/null +++ b/extern/audaspace/src/devices/NULLDevice.cpp @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/NULLDevice.h" +#include "devices/DeviceManager.h" +#include "devices/IDeviceFactory.h" + +#include <limits> +#include <string> + +AUD_NAMESPACE_BEGIN + +NULLDevice::NULLHandle::NULLHandle() +{ +} + +bool NULLDevice::NULLHandle::pause() +{ + return false; +} + +bool NULLDevice::NULLHandle::resume() +{ + return false; +} + +bool NULLDevice::NULLHandle::stop() +{ + return false; +} + +bool NULLDevice::NULLHandle::getKeep() +{ + return false; +} + +bool NULLDevice::NULLHandle::setKeep(bool keep) +{ + return false; +} + +bool NULLDevice::NULLHandle::seek(float position) +{ + return false; +} + +float NULLDevice::NULLHandle::getPosition() +{ + return std::numeric_limits<float>::quiet_NaN(); +} + +Status NULLDevice::NULLHandle::getStatus() +{ + return STATUS_INVALID; +} + +float NULLDevice::NULLHandle::getVolume() +{ + return std::numeric_limits<float>::quiet_NaN(); +} + +bool NULLDevice::NULLHandle::setVolume(float volume) +{ + return false; +} + +float NULLDevice::NULLHandle::getPitch() +{ + return std::numeric_limits<float>::quiet_NaN(); +} + +bool NULLDevice::NULLHandle::setPitch(float pitch) +{ + return false; +} + +int NULLDevice::NULLHandle::getLoopCount() +{ + return 0; +} + +bool NULLDevice::NULLHandle::setLoopCount(int count) +{ + return false; +} + +bool NULLDevice::NULLHandle::setStopCallback(stopCallback callback, void* data) +{ + return false; +} + +NULLDevice::NULLDevice() +{ +} + +NULLDevice::~NULLDevice() +{ +} + +DeviceSpecs NULLDevice::getSpecs() const +{ + DeviceSpecs specs; + specs.channels = CHANNELS_INVALID; + specs.format = FORMAT_INVALID; + specs.rate = RATE_INVALID; + return specs; +} + +std::shared_ptr<IHandle> NULLDevice::play(std::shared_ptr<IReader> reader, bool keep) +{ + return std::shared_ptr<IHandle>(new NULLHandle()); +} + +std::shared_ptr<IHandle> NULLDevice::play(std::shared_ptr<ISound> sound, bool keep) +{ + return std::shared_ptr<IHandle>(new NULLHandle()); +} + +void NULLDevice::stopAll() +{ +} + +void NULLDevice::lock() +{ +} + +void NULLDevice::unlock() +{ +} + +float NULLDevice::getVolume() const +{ + return std::numeric_limits<float>::quiet_NaN(); +} + +void NULLDevice::setVolume(float volume) +{ +} + +ISynchronizer* NULLDevice::getSynchronizer() +{ + return nullptr; +} + +class NULLDeviceFactory : public IDeviceFactory +{ +public: + NULLDeviceFactory() + { + } + + virtual std::shared_ptr<IDevice> openDevice() + { + return std::shared_ptr<IDevice>(new NULLDevice()); + } + + virtual int getPriority() + { + return std::numeric_limits<int>::min(); + } + + virtual void setSpecs(DeviceSpecs specs) + { + } + + virtual void setBufferSize(int buffersize) + { + } + + virtual void setName(std::string name) + { + } +}; + +void NULLDevice::registerPlugin() +{ + DeviceManager::registerDevice("Null", std::shared_ptr<IDeviceFactory>(new NULLDeviceFactory)); +} + +AUD_NAMESPACE_END diff --git a/extern/audaspace/src/devices/ReadDevice.cpp b/extern/audaspace/src/devices/ReadDevice.cpp new file mode 100644 index 00000000000..487fee1f59a --- /dev/null +++ b/extern/audaspace/src/devices/ReadDevice.cpp @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/ReadDevice.h" +#include "IReader.h" + +#include <cstring> + +AUD_NAMESPACE_BEGIN + +ReadDevice::ReadDevice(DeviceSpecs specs) : + m_playing(false) +{ + m_specs = specs; + + create(); +} + +ReadDevice::ReadDevice(Specs specs) : + m_playing(false) +{ + m_specs.specs = specs; + m_specs.format = FORMAT_FLOAT32; + + create(); +} + +ReadDevice::~ReadDevice() +{ + destroy(); +} + +bool ReadDevice::read(data_t* buffer, int length) +{ + if(m_playing) + mix(buffer, length); + else + if(m_specs.format == FORMAT_U8) + std::memset(buffer, 0x80, length * AUD_DEVICE_SAMPLE_SIZE(m_specs)); + else + std::memset(buffer, 0, length * AUD_DEVICE_SAMPLE_SIZE(m_specs)); + return m_playing; +} + +void ReadDevice::changeSpecs(Specs specs) +{ + if(!AUD_COMPARE_SPECS(specs, m_specs.specs)) + setSpecs(specs); +} + +void ReadDevice::playing(bool playing) +{ + m_playing = playing; +} + +AUD_NAMESPACE_END diff --git a/extern/audaspace/src/devices/SoftwareDevice.cpp b/extern/audaspace/src/devices/SoftwareDevice.cpp new file mode 100644 index 00000000000..c944b9ed12d --- /dev/null +++ b/extern/audaspace/src/devices/SoftwareDevice.cpp @@ -0,0 +1,989 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/SoftwareDevice.h" +#include "fx/PitchReader.h" +#include "respec/ChannelMapperReader.h" +#include "respec/JOSResampleReader.h" +#include "respec/LinearResampleReader.h" +#include "respec/Mixer.h" +#include "Exception.h" +#include "ISound.h" + +#include <algorithm> +#include <cmath> +#include <cstring> +#include <iostream> +#include <limits> +#include <mutex> + +AUD_NAMESPACE_BEGIN + +enum RenderFlags +{ + RENDER_DISTANCE = 0x01, + RENDER_DOPPLER = 0x02, + RENDER_CONE = 0x04, + RENDER_VOLUME = 0x08 +}; + +#define PITCH_MAX 10 + +/******************************************************************************/ +/********************** SoftwareHandle Handle Code ************************/ +/******************************************************************************/ + +bool SoftwareDevice::SoftwareHandle::pause(bool keep) +{ + if(m_status) + { + std::lock_guard<ILockable> lock(*m_device); + + if(m_status == STATUS_PLAYING) + { + for(auto it = m_device->m_playingSounds.begin(); it != m_device->m_playingSounds.end(); it++) + { + if(it->get() == this) + { + std::shared_ptr<SoftwareHandle> This = *it; + + m_device->m_playingSounds.erase(it); + m_device->m_pausedSounds.push_back(This); + + if(m_device->m_playingSounds.empty()) + m_device->playing(m_device->m_playback = false); + + m_status = keep ? STATUS_STOPPED : STATUS_PAUSED; + + return true; + } + } + } + } + + return false; +} + +SoftwareDevice::SoftwareHandle::SoftwareHandle(SoftwareDevice* device, std::shared_ptr<IReader> reader, std::shared_ptr<PitchReader> pitch, std::shared_ptr<ResampleReader> resampler, std::shared_ptr<ChannelMapperReader> mapper, bool keep) : + m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(1.0f), m_old_volume(0), m_loopcount(0), + m_relative(true), m_volume_max(1.0f), m_volume_min(0), m_distance_max(std::numeric_limits<float>::max()), + m_distance_reference(1.0f), m_attenuation(1.0f), m_cone_angle_outer(M_PI), m_cone_angle_inner(M_PI), m_cone_volume_outer(0), + m_flags(RENDER_CONE), m_stop(nullptr), m_stop_data(nullptr), m_status(STATUS_PLAYING), m_device(device) +{ +} + +void SoftwareDevice::SoftwareHandle::update() +{ + int flags = 0; + + m_old_volume = m_volume; + + Vector3 SL; + if(m_relative) + SL = -m_location; + else + SL = m_device->m_location - m_location; + float distance = SL * SL; + + if(distance > 0) + distance = sqrt(distance); + else + flags |= RENDER_DOPPLER | RENDER_DISTANCE; + + if(m_pitch->getSpecs().channels != CHANNELS_MONO) + { + m_volume = m_user_volume; + m_pitch->setPitch(m_user_pitch); + return; + } + + flags = ~(flags | m_flags | m_device->m_flags); + + // Doppler and Pitch + + if(flags & RENDER_DOPPLER) + { + float vls; + if(m_relative) + vls = 0; + else + vls = SL * m_device->m_velocity / distance; + float vss = SL * m_velocity / distance; + float max = m_device->m_speed_of_sound / m_device->m_doppler_factor; + if(vss >= max) + { + m_pitch->setPitch(PITCH_MAX); + } + else + { + if(vls > max) + vls = max; + + m_pitch->setPitch((m_device->m_speed_of_sound - m_device->m_doppler_factor * vls) / (m_device->m_speed_of_sound - m_device->m_doppler_factor * vss) * m_user_pitch); + } + } + else + m_pitch->setPitch(m_user_pitch); + + if(flags & RENDER_VOLUME) + { + // Distance + + if(flags & RENDER_DISTANCE) + { + if(m_device->m_distance_model == DISTANCE_MODEL_INVERSE_CLAMPED || + m_device->m_distance_model == DISTANCE_MODEL_LINEAR_CLAMPED || + m_device->m_distance_model == DISTANCE_MODEL_EXPONENT_CLAMPED) + { + distance = std::max(std::min(m_distance_max, distance), m_distance_reference); + } + + switch(m_device->m_distance_model) + { + case DISTANCE_MODEL_INVERSE: + case DISTANCE_MODEL_INVERSE_CLAMPED: + m_volume = m_distance_reference / (m_distance_reference + m_attenuation * (distance - m_distance_reference)); + break; + case DISTANCE_MODEL_LINEAR: + case DISTANCE_MODEL_LINEAR_CLAMPED: + { + float temp = m_distance_max - m_distance_reference; + if(temp == 0) + { + if(distance > m_distance_reference) + m_volume = 0.0f; + else + m_volume = 1.0f; + } + else + m_volume = 1.0f - m_attenuation * (distance - m_distance_reference) / (m_distance_max - m_distance_reference); + break; + } + case DISTANCE_MODEL_EXPONENT: + case DISTANCE_MODEL_EXPONENT_CLAMPED: + if(m_distance_reference == 0) + m_volume = 0; + else + m_volume = std::pow(distance / m_distance_reference, -m_attenuation); + break; + default: + m_volume = 1.0f; + } + } + else + m_volume = 1.0f; + + // Cone + + if(flags & RENDER_CONE) + { + Vector3 SZ = m_orientation.getLookAt(); + + float phi = std::acos(float(SZ * SL / (SZ.length() * SL.length()))); + float t = (phi - m_cone_angle_inner)/(m_cone_angle_outer - m_cone_angle_inner); + + if(t > 0) + { + if(t > 1) + m_volume *= m_cone_volume_outer; + else + m_volume *= 1 + t * (m_cone_volume_outer - 1); + } + } + + if(m_volume > m_volume_max) + m_volume = m_volume_max; + else if(m_volume < m_volume_min) + m_volume = m_volume_min; + + // Volume + + m_volume *= m_user_volume; + } + + // 3D Cue + + Quaternion orientation; + + if(!m_relative) + orientation = m_device->m_orientation; + + Vector3 Z = orientation.getLookAt(); + Vector3 N = orientation.getUp(); + Vector3 A = N * ((SL * N) / (N * N)) - SL; + + float Asquare = A * A; + + if(Asquare > 0) + { + float phi = std::acos(float(Z * A / (Z.length() * std::sqrt(Asquare)))); + if(N.cross(Z) * A > 0) + phi = -phi; + + m_mapper->setMonoAngle(phi); + } + else + m_mapper->setMonoAngle(m_relative ? m_user_pan * M_PI / 2.0 : 0); +} + +void SoftwareDevice::SoftwareHandle::setSpecs(Specs specs) +{ + m_mapper->setChannels(specs.channels); + m_resampler->setRate(specs.rate); +} + +bool SoftwareDevice::SoftwareHandle::pause() +{ + return pause(false); +} + +bool SoftwareDevice::SoftwareHandle::resume() +{ + if(m_status) + { + std::lock_guard<ILockable> lock(*m_device); + + if(m_status == STATUS_PAUSED) + { + for(auto it = m_device->m_pausedSounds.begin(); it != m_device->m_pausedSounds.end(); it++) + { + if(it->get() == this) + { + std::shared_ptr<SoftwareHandle> This = *it; + + m_device->m_pausedSounds.erase(it); + + m_device->m_playingSounds.push_back(This); + + if(!m_device->m_playback) + m_device->playing(m_device->m_playback = true); + m_status = STATUS_PLAYING; + + return true; + } + } + } + + } + + return false; +} + +bool SoftwareDevice::SoftwareHandle::stop() +{ + if(!m_status) + return false; + + std::lock_guard<ILockable> lock(*m_device); + + if(!m_status) + return false; + + m_status = STATUS_INVALID; + + for(auto it = m_device->m_playingSounds.begin(); it != m_device->m_playingSounds.end(); it++) + { + if(it->get() == this) + { + std::shared_ptr<SoftwareHandle> This = *it; + + m_device->m_playingSounds.erase(it); + + if(m_device->m_playingSounds.empty()) + m_device->playing(m_device->m_playback = false); + + return true; + } + } + + for(auto it = m_device->m_pausedSounds.begin(); it != m_device->m_pausedSounds.end(); it++) + { + if(it->get() == this) + { + std::shared_ptr<SoftwareHandle> This = *it; + + m_device->m_pausedSounds.erase(it); + + return true; + } + } + + return false; +} + +bool SoftwareDevice::SoftwareHandle::getKeep() +{ + if(m_status) + return m_keep; + + return false; +} + +bool SoftwareDevice::SoftwareHandle::setKeep(bool keep) +{ + if(!m_status) + return false; + + std::lock_guard<ILockable> lock(*m_device); + + if(!m_status) + return false; + + m_keep = keep; + + return true; +} + +bool SoftwareDevice::SoftwareHandle::seek(float position) +{ + if(!m_status) + return false; + + std::lock_guard<ILockable> lock(*m_device); + + if(!m_status) + return false; + + m_pitch->setPitch(m_user_pitch); + m_reader->seek((int)(position * m_reader->getSpecs().rate)); + + if(m_status == STATUS_STOPPED) + m_status = STATUS_PAUSED; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getPosition() +{ + if(!m_status) + return false; + + std::lock_guard<ILockable> lock(*m_device); + + if(!m_status) + return 0.0f; + + float position = m_reader->getPosition() / (float)m_device->m_specs.rate; + + return position; +} + +Status SoftwareDevice::SoftwareHandle::getStatus() +{ + return m_status; +} + +float SoftwareDevice::SoftwareHandle::getVolume() +{ + return m_user_volume; +} + +bool SoftwareDevice::SoftwareHandle::setVolume(float volume) +{ + if(!m_status) + return false; + m_user_volume = volume; + + if(volume == 0) + { + m_old_volume = m_volume = volume; + m_flags |= RENDER_VOLUME; + } + else + m_flags &= ~RENDER_VOLUME; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getPitch() +{ + return m_user_pitch; +} + +bool SoftwareDevice::SoftwareHandle::setPitch(float pitch) +{ + if(!m_status) + return false; + if(pitch > 0.0f) + m_user_pitch = pitch; + return true; +} + +int SoftwareDevice::SoftwareHandle::getLoopCount() +{ + if(!m_status) + return 0; + return m_loopcount; +} + +bool SoftwareDevice::SoftwareHandle::setLoopCount(int count) +{ + if(!m_status) + return false; + + if(m_status == STATUS_STOPPED && (count > m_loopcount || count < 0)) + m_status = STATUS_PAUSED; + + m_loopcount = count; + + return true; +} + +bool SoftwareDevice::SoftwareHandle::setStopCallback(stopCallback callback, void* data) +{ + if(!m_status) + return false; + + std::lock_guard<ILockable> lock(*m_device); + + if(!m_status) + return false; + + m_stop = callback; + m_stop_data = data; + + return true; +} + + + +/******************************************************************************/ +/******************** SoftwareHandle 3DHandle Code ************************/ +/******************************************************************************/ + +Vector3 SoftwareDevice::SoftwareHandle::getLocation() +{ + if(!m_status) + return Vector3(); + + return m_location; +} + +bool SoftwareDevice::SoftwareHandle::setLocation(const Vector3& location) +{ + if(!m_status) + return false; + + m_location = location; + + return true; +} + +Vector3 SoftwareDevice::SoftwareHandle::getVelocity() +{ + if(!m_status) + return Vector3(); + + return m_velocity; +} + +bool SoftwareDevice::SoftwareHandle::setVelocity(const Vector3& velocity) +{ + if(!m_status) + return false; + + m_velocity = velocity; + + return true; +} + +Quaternion SoftwareDevice::SoftwareHandle::getOrientation() +{ + if(!m_status) + return Quaternion(); + + return m_orientation; +} + +bool SoftwareDevice::SoftwareHandle::setOrientation(const Quaternion& orientation) +{ + if(!m_status) + return false; + + m_orientation = orientation; + + return true; +} + +bool SoftwareDevice::SoftwareHandle::isRelative() +{ + if(!m_status) + return false; + + return m_relative; +} + +bool SoftwareDevice::SoftwareHandle::setRelative(bool relative) +{ + if(!m_status) + return false; + + m_relative = relative; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getVolumeMaximum() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_volume_max; +} + +bool SoftwareDevice::SoftwareHandle::setVolumeMaximum(float volume) +{ + if(!m_status) + return false; + + m_volume_max = volume; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getVolumeMinimum() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_volume_min; +} + +bool SoftwareDevice::SoftwareHandle::setVolumeMinimum(float volume) +{ + if(!m_status) + return false; + + m_volume_min = volume; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getDistanceMaximum() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_distance_max; +} + +bool SoftwareDevice::SoftwareHandle::setDistanceMaximum(float distance) +{ + if(!m_status) + return false; + + m_distance_max = distance; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getDistanceReference() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_distance_reference; +} + +bool SoftwareDevice::SoftwareHandle::setDistanceReference(float distance) +{ + if(!m_status) + return false; + + m_distance_reference = distance; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getAttenuation() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_attenuation; +} + +bool SoftwareDevice::SoftwareHandle::setAttenuation(float factor) +{ + if(!m_status) + return false; + + m_attenuation = factor; + + if(factor == 0) + m_flags |= RENDER_DISTANCE; + else + m_flags &= ~RENDER_DISTANCE; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getConeAngleOuter() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_cone_angle_outer * 360.0f / M_PI; +} + +bool SoftwareDevice::SoftwareHandle::setConeAngleOuter(float angle) +{ + if(!m_status) + return false; + + m_cone_angle_outer = angle * M_PI / 360.0f; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getConeAngleInner() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_cone_angle_inner * 360.0f / M_PI; +} + +bool SoftwareDevice::SoftwareHandle::setConeAngleInner(float angle) +{ + if(!m_status) + return false; + + if(angle >= 360) + m_flags |= RENDER_CONE; + else + m_flags &= ~RENDER_CONE; + + m_cone_angle_inner = angle * M_PI / 360.0f; + + return true; +} + +float SoftwareDevice::SoftwareHandle::getConeVolumeOuter() +{ + if(!m_status) + return std::numeric_limits<float>::quiet_NaN(); + + return m_cone_volume_outer; +} + +bool SoftwareDevice::SoftwareHandle::setConeVolumeOuter(float volume) +{ + if(!m_status) + return false; + + m_cone_volume_outer = volume; + + return true; +} + +/******************************************************************************/ +/**************************** IDevice Code ************************************/ +/******************************************************************************/ + +void SoftwareDevice::create() +{ + m_playback = false; + m_volume = 1.0f; + m_mixer = std::shared_ptr<Mixer>(new Mixer(m_specs)); + m_speed_of_sound = 343.3f; + m_doppler_factor = 1.0f; + m_distance_model = DISTANCE_MODEL_INVERSE_CLAMPED; + m_flags = 0; + m_quality = false; +} + +void SoftwareDevice::destroy() +{ + if(m_playback) + playing(m_playback = false); + + while(!m_playingSounds.empty()) + m_playingSounds.front()->stop(); + + while(!m_pausedSounds.empty()) + m_pausedSounds.front()->stop(); +} + +void SoftwareDevice::mix(data_t* buffer, int length) +{ + m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs)); + + std::lock_guard<std::recursive_mutex> lock(m_mutex); + + { + std::shared_ptr<SoftwareDevice::SoftwareHandle> sound; + int len; + int pos; + bool eos; + std::list<std::shared_ptr<SoftwareDevice::SoftwareHandle> > stopSounds; + std::list<std::shared_ptr<SoftwareDevice::SoftwareHandle> > pauseSounds; + sample_t* buf = m_buffer.getBuffer(); + + m_mixer->clear(length); + + // for all sounds + for(auto& sound : m_playingSounds) + { + // get the buffer from the source + pos = 0; + len = length; + + // update 3D Info + sound->update(); + + try + { + sound->m_reader->read(len, eos, buf); + + // in case of looping + while(pos + len < length && sound->m_loopcount && eos) + { + m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume); + + pos += len; + + if(sound->m_loopcount > 0) + sound->m_loopcount--; + + sound->m_reader->seek(0); + + len = length - pos; + sound->m_reader->read(len, eos, buf); + + // prevent endless loop + if(!len) + break; + } + } + catch(Exception& e) + { + len = 0; + std::cerr << "Caught exception while reading sound data during playback with software mixing: " << e.getMessage() << std::endl; + } + + m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume); + + // in case the end of the sound is reached + if(eos && !sound->m_loopcount) + { + if(sound->m_stop) + sound->m_stop(sound->m_stop_data); + + if(sound->m_keep) + pauseSounds.push_back(sound); + else + stopSounds.push_back(sound); + } + } + + // superpose + m_mixer->read(buffer, m_volume); + + // cleanup + for(auto& sound : pauseSounds) + sound->pause(true); + + for(auto& sound : stopSounds) + sound->stop(); + + pauseSounds.clear(); + stopSounds.clear(); + } +} + +void SoftwareDevice::setPanning(IHandle* handle, float pan) +{ + SoftwareDevice::SoftwareHandle* h = dynamic_cast<SoftwareDevice::SoftwareHandle*>(handle); + h->m_user_pan = pan; +} + +void SoftwareDevice::setQuality(bool quality) +{ + m_quality = quality; +} + +void SoftwareDevice::setSpecs(Specs specs) +{ + m_specs.specs = specs; + m_mixer->setSpecs(specs); + + for(auto& sound : m_playingSounds) + { + sound->setSpecs(specs); + } +} + +SoftwareDevice::SoftwareDevice() +{ +} + +DeviceSpecs SoftwareDevice::getSpecs() const +{ + return m_specs; +} + +std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<IReader> reader, bool keep) +{ + // prepare the reader + // pitch + + std::shared_ptr<PitchReader> pitch = std::shared_ptr<PitchReader>(new PitchReader(reader, 1)); + reader = std::shared_ptr<IReader>(pitch); + + std::shared_ptr<ResampleReader> resampler; + + // resample + if(m_quality) + resampler = std::shared_ptr<ResampleReader>(new JOSResampleReader(reader, m_specs.rate)); + else + resampler = std::shared_ptr<ResampleReader>(new LinearResampleReader(reader, m_specs.rate)); + reader = std::shared_ptr<IReader>(resampler); + + // rechannel + std::shared_ptr<ChannelMapperReader> mapper = std::shared_ptr<ChannelMapperReader>(new ChannelMapperReader(reader, m_specs.channels)); + reader = std::shared_ptr<IReader>(mapper); + + if(!reader.get()) + return std::shared_ptr<IHandle>(); + + // play sound + std::shared_ptr<SoftwareDevice::SoftwareHandle> sound = std::shared_ptr<SoftwareDevice::SoftwareHandle>(new SoftwareDevice::SoftwareHandle(this, reader, pitch, resampler, mapper, keep)); + + std::lock_guard<std::recursive_mutex> lock(m_mutex); + + m_playingSounds.push_back(sound); + + if(!m_playback) + playing(m_playback = true); + + return std::shared_ptr<IHandle>(sound); +} + +std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<ISound> sound, bool keep) +{ + return play(sound->createReader(), keep); +} + +void SoftwareDevice::stopAll() +{ + std::lock_guard<std::recursive_mutex> lock(m_mutex); + + while(!m_playingSounds.empty()) + m_playingSounds.front()->stop(); + + while(!m_pausedSounds.empty()) + m_pausedSounds.front()->stop(); +} + +void SoftwareDevice::lock() +{ + m_mutex.lock(); +} + +void SoftwareDevice::unlock() +{ + m_mutex.unlock(); +} + +float SoftwareDevice::getVolume() const +{ + return m_volume; +} + +void SoftwareDevice::setVolume(float volume) +{ + m_volume = volume; +} + +ISynchronizer* SoftwareDevice::getSynchronizer() +{ + return &m_synchronizer; +} + +/******************************************************************************/ +/**************************** 3D Device Code **********************************/ +/******************************************************************************/ + +Vector3 SoftwareDevice::getListenerLocation() const +{ + return m_location; +} + +void SoftwareDevice::setListenerLocation(const Vector3& location) +{ + m_location = location; +} + +Vector3 SoftwareDevice::getListenerVelocity() const +{ + return m_velocity; +} + +void SoftwareDevice::setListenerVelocity(const Vector3& velocity) +{ + m_velocity = velocity; +} + +Quaternion SoftwareDevice::getListenerOrientation() const +{ + return m_orientation; +} + +void SoftwareDevice::setListenerOrientation(const Quaternion& orientation) +{ + m_orientation = orientation; +} + +float SoftwareDevice::getSpeedOfSound() const +{ + return m_speed_of_sound; +} + +void SoftwareDevice::setSpeedOfSound(float speed) +{ + m_speed_of_sound = speed; +} + +float SoftwareDevice::getDopplerFactor() const +{ + return m_doppler_factor; +} + +void SoftwareDevice::setDopplerFactor(float factor) +{ + m_doppler_factor = factor; + if(factor == 0) + m_flags |= RENDER_DOPPLER; + else + m_flags &= ~RENDER_DOPPLER; +} + +DistanceModel SoftwareDevice::getDistanceModel() const +{ + return m_distance_model; +} + +void SoftwareDevice::setDistanceModel(DistanceModel model) +{ + m_distance_model = model; + if(model == DISTANCE_MODEL_INVALID) + m_flags |= RENDER_DISTANCE; + else + m_flags &= ~RENDER_DISTANCE; +} + +AUD_NAMESPACE_END |