Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'extern/audaspace/src/sequence')
-rw-r--r--extern/audaspace/src/sequence/AnimateableProperty.cpp217
-rw-r--r--extern/audaspace/src/sequence/Double.cpp35
-rw-r--r--extern/audaspace/src/sequence/DoubleReader.cpp102
-rw-r--r--extern/audaspace/src/sequence/PingPong.cpp36
-rw-r--r--extern/audaspace/src/sequence/Sequence.cpp113
-rw-r--r--extern/audaspace/src/sequence/SequenceData.cpp172
-rw-r--r--extern/audaspace/src/sequence/SequenceEntry.cpp256
-rw-r--r--extern/audaspace/src/sequence/SequenceHandle.cpp253
-rw-r--r--extern/audaspace/src/sequence/SequenceHandle.h117
-rw-r--r--extern/audaspace/src/sequence/SequenceReader.cpp198
-rw-r--r--extern/audaspace/src/sequence/Superpose.cpp35
-rw-r--r--extern/audaspace/src/sequence/SuperposeReader.cpp95
12 files changed, 1629 insertions, 0 deletions
diff --git a/extern/audaspace/src/sequence/AnimateableProperty.cpp b/extern/audaspace/src/sequence/AnimateableProperty.cpp
new file mode 100644
index 00000000000..306ba8e07f5
--- /dev/null
+++ b/extern/audaspace/src/sequence/AnimateableProperty.cpp
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * 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 "sequence/AnimateableProperty.h"
+
+#include <cstring>
+#include <cmath>
+#include <mutex>
+
+AUD_NAMESPACE_BEGIN
+
+AnimateableProperty::AnimateableProperty(int count) :
+ Buffer(count * sizeof(float)), m_count(count), m_isAnimated(false)
+{
+ std::memset(getBuffer(), 0, count * sizeof(float));
+}
+
+AnimateableProperty::AnimateableProperty(int count, float value) :
+ Buffer(count * sizeof(float)), m_count(count), m_isAnimated(false)
+{
+ sample_t* buf = getBuffer();
+
+ for(int i = 0; i < count; i++)
+ buf[i] = value;
+}
+
+void AnimateableProperty::updateUnknownCache(int start, int end)
+{
+ float* buf = getBuffer();
+
+ // we could do a better interpolation than zero order, but that doesn't work with Blender's animation system
+ // as frames are only written when changing, so to support jumps, we need zero order interpolation here.
+ for(int i = start; i <= end; i++)
+ std::memcpy(buf + i * m_count, buf + (start - 1) * m_count, m_count * sizeof(float));
+}
+
+AnimateableProperty::~AnimateableProperty()
+{
+}
+
+int AnimateableProperty::getCount() const
+{
+ return m_count;
+}
+
+void AnimateableProperty::write(const float* data)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_isAnimated = false;
+ m_unknown.clear();
+ std::memcpy(getBuffer(), data, m_count * sizeof(float));
+}
+
+void AnimateableProperty::write(const float* data, int position, int count)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ int pos = getSize() / (sizeof(float) * m_count);
+
+ if(!m_isAnimated)
+ pos = 0;
+
+ m_isAnimated = true;
+
+ assureSize((count + position) * m_count * sizeof(float), true);
+
+ float* buf = getBuffer();
+
+ std::memcpy(buf + position * m_count, data, count * m_count * sizeof(float));
+
+ // have to fill up space between?
+ if(pos < position)
+ {
+ m_unknown.push_back(Unknown(pos, position - 1));
+
+ // if the buffer was not animated before, we copy the previous static value
+ if(pos == 0)
+ pos = 1;
+
+ updateUnknownCache(pos, position - 1);
+ }
+ // otherwise it's not at the end, let's check if some unknown part got filled
+ else
+ {
+ bool erased = false;
+
+ for(auto it = m_unknown.begin(); it != m_unknown.end(); erased ? it : it++)
+ {
+ erased = false;
+
+ // unknown area before position
+ if(it->end < position)
+ continue;
+
+ // we're after the new area, let's stop
+ if(it->start >= position + count)
+ break;
+
+ // we have an intersection, now 4 cases:
+ // the start is included
+ if(position <= it->start)
+ {
+ // the end is included
+ if(position + count > it->end)
+ {
+ // simply delete
+ it = m_unknown.erase(it);
+ erased = true;
+ }
+ // the end is excluded, a second part remains
+ else
+ {
+ // update second part
+ it->start = position + count;
+ updateUnknownCache(it->start, it->end);
+ break;
+ }
+ }
+ // start is excluded, a first part remains
+ else
+ {
+ // the end is included
+ if(position + count > it->end)
+ {
+ // update first part
+ it->end = position - 1;
+ }
+ // the end is excluded, a second part remains
+ else
+ {
+ // add another item and update both parts
+ m_unknown.insert(it, Unknown(it->start, position - 1));
+ it->start = position + count;
+ updateUnknownCache(it->start, it->end);
+ }
+ }
+ }
+ }
+}
+
+void AnimateableProperty::read(float position, float* out)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ if(!m_isAnimated)
+ {
+ std::memcpy(out, getBuffer(), m_count * sizeof(float));
+ return;
+ }
+
+ int last = getSize() / (sizeof(float) * m_count) - 1;
+ float t = position - std::floor(position);
+
+ if(position >= last)
+ {
+ position = last;
+ t = 0;
+ }
+
+ if(t == 0)
+ {
+ std::memcpy(out, getBuffer() + int(std::floor(position)) * m_count, m_count * sizeof(float));
+ }
+ else
+ {
+ int pos = int(std::floor(position)) * m_count;
+ float t2 = t * t;
+ float t3 = t2 * t;
+ float m0, m1;
+ float* p0;
+ float* p1 = getBuffer() + pos;
+ float* p2;
+ float* p3;
+ last *= m_count;
+
+ if(pos == 0)
+ p0 = p1;
+ else
+ p0 = p1 - m_count;
+
+ p2 = p1 + m_count;
+ if(pos + m_count == last)
+ p3 = p2;
+ else
+ p3 = p2 + m_count;
+
+ for(int i = 0; i < m_count; i++)
+ {
+ m0 = (p2[i] - p0[i]) / 2.0f;
+ m1 = (p3[i] - p1[i]) / 2.0f;
+
+ out[i] = (2 * t3 - 3 * t2 + 1) * p0[i] + (-2 * t3 + 3 * t2) * p1[i] +
+ (t3 - 2 * t2 + t) * m0 + (t3 - t2) * m1;
+ }
+ }
+}
+
+bool AnimateableProperty::isAnimated() const
+{
+ return m_isAnimated;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/Double.cpp b/extern/audaspace/src/sequence/Double.cpp
new file mode 100644
index 00000000000..1086be84a11
--- /dev/null
+++ b/extern/audaspace/src/sequence/Double.cpp
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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 "sequence/Double.h"
+#include "sequence/DoubleReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+Double::Double(std::shared_ptr<ISound> sound1, std::shared_ptr<ISound> sound2) :
+ m_sound1(sound1), m_sound2(sound2)
+{
+}
+
+std::shared_ptr<IReader> Double::createReader()
+{
+ std::shared_ptr<IReader> reader1 = m_sound1->createReader();
+ std::shared_ptr<IReader> reader2 = m_sound2->createReader();
+
+ return std::shared_ptr<IReader>(new DoubleReader(reader1, reader2));
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/DoubleReader.cpp b/extern/audaspace/src/sequence/DoubleReader.cpp
new file mode 100644
index 00000000000..33ab34ce366
--- /dev/null
+++ b/extern/audaspace/src/sequence/DoubleReader.cpp
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * 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 "sequence/DoubleReader.h"
+
+#include <cstring>
+
+AUD_NAMESPACE_BEGIN
+
+DoubleReader::DoubleReader(std::shared_ptr<IReader> reader1, std::shared_ptr<IReader> reader2) :
+ m_reader1(reader1), m_reader2(reader2), m_finished1(false)
+{
+ Specs s1, s2;
+ s1 = reader1->getSpecs();
+ s2 = reader2->getSpecs();
+}
+
+DoubleReader::~DoubleReader()
+{
+}
+
+bool DoubleReader::isSeekable() const
+{
+ return m_reader1->isSeekable() && m_reader2->isSeekable();
+}
+
+void DoubleReader::seek(int position)
+{
+ m_reader1->seek(position);
+
+ int pos1 = m_reader1->getPosition();
+
+ if((m_finished1 = (pos1 < position)))
+ m_reader2->seek(position - pos1);
+ else
+ m_reader2->seek(0);
+}
+
+int DoubleReader::getLength() const
+{
+ int len1 = m_reader1->getLength();
+ int len2 = m_reader2->getLength();
+ if(len1 < 0 || len2 < 0)
+ return -1;
+ return len1 + len2;
+}
+
+int DoubleReader::getPosition() const
+{
+ return m_reader1->getPosition() + m_reader2->getPosition();
+}
+
+Specs DoubleReader::getSpecs() const
+{
+ return m_finished1 ? m_reader1->getSpecs() : m_reader2->getSpecs();
+}
+
+void DoubleReader::read(int& length, bool& eos, sample_t* buffer)
+{
+ eos = false;
+
+ if(!m_finished1)
+ {
+ int len = length;
+
+ m_reader1->read(len, m_finished1, buffer);
+
+ if(len < length)
+ {
+ Specs specs1, specs2;
+ specs1 = m_reader1->getSpecs();
+ specs2 = m_reader2->getSpecs();
+ if(AUD_COMPARE_SPECS(specs1, specs2))
+ {
+ int len2 = length - len;
+ m_reader2->read(len2, eos, buffer + specs1.channels * len);
+ length = len + len2;
+ }
+ else
+ length = len;
+ }
+ }
+ else
+ {
+ m_reader2->read(length, eos, buffer);
+ }
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/PingPong.cpp b/extern/audaspace/src/sequence/PingPong.cpp
new file mode 100644
index 00000000000..19bfb10fc2d
--- /dev/null
+++ b/extern/audaspace/src/sequence/PingPong.cpp
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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 "sequence/PingPong.h"
+#include "sequence/DoubleReader.h"
+#include "fx/ReverseReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+PingPong::PingPong(std::shared_ptr<ISound> sound) :
+ Effect(sound)
+{
+}
+
+std::shared_ptr<IReader> PingPong::createReader()
+{
+ std::shared_ptr<IReader> reader = getReader();
+ std::shared_ptr<IReader> reader2 = std::shared_ptr<IReader>(new ReverseReader(getReader()));
+
+ return std::shared_ptr<IReader>(new DoubleReader(reader, reader2));
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/Sequence.cpp b/extern/audaspace/src/sequence/Sequence.cpp
new file mode 100644
index 00000000000..eaec4d84ae1
--- /dev/null
+++ b/extern/audaspace/src/sequence/Sequence.cpp
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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 "sequence/Sequence.h"
+#include "sequence/SequenceReader.h"
+#include "sequence/SequenceData.h"
+
+AUD_NAMESPACE_BEGIN
+
+Sequence::Sequence(Specs specs, float fps, bool muted)
+{
+ m_sequence = std::shared_ptr<SequenceData>(new SequenceData(specs, fps, muted));
+}
+
+Specs Sequence::getSpecs()
+{
+ return m_sequence->getSpecs();
+}
+
+void Sequence::setSpecs(Specs specs)
+{
+ m_sequence->setSpecs(specs);
+}
+
+float Sequence::getFPS() const
+{
+ return m_sequence->getFPS();
+}
+
+void Sequence::setFPS(float fps)
+{
+ m_sequence->setFPS(fps);
+}
+
+void Sequence::mute(bool muted)
+{
+ m_sequence->mute(muted);
+}
+
+bool Sequence::isMuted() const
+{
+ return m_sequence->isMuted();
+}
+
+float Sequence::getSpeedOfSound() const
+{
+ return m_sequence->getSpeedOfSound();
+}
+
+void Sequence::setSpeedOfSound(float speed)
+{
+ m_sequence->setSpeedOfSound(speed);
+}
+
+float Sequence::getDopplerFactor() const
+{
+ return m_sequence->getDopplerFactor();
+}
+
+void Sequence::setDopplerFactor(float factor)
+{
+ m_sequence->setDopplerFactor(factor);
+}
+
+DistanceModel Sequence::getDistanceModel() const
+{
+ return m_sequence->getDistanceModel();
+}
+
+void Sequence::setDistanceModel(DistanceModel model)
+{
+ m_sequence->setDistanceModel(model);
+}
+
+AnimateableProperty* Sequence::getAnimProperty(AnimateablePropertyType type)
+{
+ return m_sequence->getAnimProperty(type);
+}
+
+std::shared_ptr<SequenceEntry> Sequence::add(std::shared_ptr<ISound> sound, float begin, float end, float skip)
+{
+ return m_sequence->add(sound, begin, end, skip);
+}
+
+void Sequence::remove(std::shared_ptr<SequenceEntry> entry)
+{
+ m_sequence->remove(entry);
+}
+
+std::shared_ptr<IReader> Sequence::createQualityReader()
+{
+ return std::shared_ptr<IReader>(new SequenceReader(m_sequence, true));
+}
+
+std::shared_ptr<IReader> Sequence::createReader()
+{
+ return std::shared_ptr<IReader>(new SequenceReader(m_sequence));
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/SequenceData.cpp b/extern/audaspace/src/sequence/SequenceData.cpp
new file mode 100644
index 00000000000..fb920acc1a8
--- /dev/null
+++ b/extern/audaspace/src/sequence/SequenceData.cpp
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * 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 "sequence/SequenceData.h"
+#include "sequence/SequenceReader.h"
+#include "sequence/SequenceEntry.h"
+
+#include <mutex>
+
+AUD_NAMESPACE_BEGIN
+
+SequenceData::SequenceData(Specs specs, float fps, bool muted) :
+ m_specs(specs),
+ m_status(0),
+ m_entry_status(0),
+ m_id(0),
+ m_muted(muted),
+ m_fps(fps),
+ m_speed_of_sound(343.3f),
+ m_doppler_factor(1),
+ m_distance_model(DISTANCE_MODEL_INVERSE_CLAMPED),
+ m_volume(1, 1.0f),
+ m_location(3),
+ m_orientation(4)
+{
+ Quaternion q;
+ m_orientation.write(q.get());
+ float f = 1;
+ m_volume.write(&f);
+}
+
+SequenceData::~SequenceData()
+{
+}
+
+void SequenceData::lock()
+{
+ m_mutex.lock();
+}
+
+void SequenceData::unlock()
+{
+ m_mutex.unlock();
+}
+
+Specs SequenceData::getSpecs()
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ return m_specs;
+}
+
+void SequenceData::setSpecs(Specs specs)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_specs = specs;
+ m_status++;
+}
+
+float SequenceData::getFPS() const
+{
+ return m_fps;
+}
+
+void SequenceData::setFPS(float fps)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_fps = fps;
+}
+
+void SequenceData::mute(bool muted)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_muted = muted;
+}
+
+bool SequenceData::isMuted() const
+{
+ return m_muted;
+}
+
+float SequenceData::getSpeedOfSound() const
+{
+ return m_speed_of_sound;
+}
+
+void SequenceData::setSpeedOfSound(float speed)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_speed_of_sound = speed;
+ m_status++;
+}
+
+float SequenceData::getDopplerFactor() const
+{
+ return m_doppler_factor;
+}
+
+void SequenceData::setDopplerFactor(float factor)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_doppler_factor = factor;
+ m_status++;
+}
+
+DistanceModel SequenceData::getDistanceModel() const
+{
+ return m_distance_model;
+}
+
+void SequenceData::setDistanceModel(DistanceModel model)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_distance_model = model;
+ m_status++;
+}
+
+AnimateableProperty* SequenceData::getAnimProperty(AnimateablePropertyType type)
+{
+ switch(type)
+ {
+ case AP_VOLUME:
+ return &m_volume;
+ case AP_LOCATION:
+ return &m_location;
+ case AP_ORIENTATION:
+ return &m_orientation;
+ default:
+ return nullptr;
+ }
+}
+
+std::shared_ptr<SequenceEntry> SequenceData::add(std::shared_ptr<ISound> sound, float begin, float end, float skip)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ std::shared_ptr<SequenceEntry> entry = std::shared_ptr<SequenceEntry>(new SequenceEntry(sound, begin, end, skip, m_id++));
+
+ m_entries.push_back(entry);
+ m_entry_status++;
+
+ return entry;
+}
+
+void SequenceData::remove(std::shared_ptr<SequenceEntry> entry)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_entries.remove(entry);
+ m_entry_status++;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/SequenceEntry.cpp b/extern/audaspace/src/sequence/SequenceEntry.cpp
new file mode 100644
index 00000000000..de538199d7d
--- /dev/null
+++ b/extern/audaspace/src/sequence/SequenceEntry.cpp
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * 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 "sequence/SequenceEntry.h"
+#include "sequence/SequenceReader.h"
+
+#include <limits>
+#include <mutex>
+
+AUD_NAMESPACE_BEGIN
+
+SequenceEntry::SequenceEntry(std::shared_ptr<ISound> sound, float begin, float end, float skip, int id) :
+ m_status(0),
+ m_pos_status(1),
+ m_sound_status(0),
+ m_id(id),
+ m_sound(sound),
+ m_begin(begin),
+ m_end(end),
+ m_skip(skip),
+ m_muted(false),
+ 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(360),
+ m_cone_angle_inner(360),
+ m_cone_volume_outer(0),
+ m_volume(1, 1.0f),
+ m_pitch(1, 1.0f),
+ m_location(3),
+ m_orientation(4)
+{
+ Quaternion q;
+ m_orientation.write(q.get());
+ float f = 1;
+ m_volume.write(&f);
+ m_pitch.write(&f);
+}
+
+SequenceEntry::~SequenceEntry()
+{
+}
+
+void SequenceEntry::lock()
+{
+ m_mutex.lock();
+}
+
+void SequenceEntry::unlock()
+{
+ m_mutex.unlock();
+}
+
+std::shared_ptr<ISound> SequenceEntry::getSound()
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ return m_sound;
+}
+
+void SequenceEntry::setSound(std::shared_ptr<ISound> sound)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ if(m_sound.get() != sound.get())
+ {
+ m_sound = sound;
+ m_sound_status++;
+ }
+}
+
+void SequenceEntry::move(float begin, float end, float skip)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ if(m_begin != begin || m_skip != skip || m_end != end)
+ {
+ m_begin = begin;
+ m_skip = skip;
+ m_end = end;
+ m_pos_status++;
+ }
+}
+
+bool SequenceEntry::isMuted()
+{
+ return m_muted;
+}
+
+void SequenceEntry::mute(bool mute)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_muted = mute;
+}
+
+int SequenceEntry::getID() const
+{
+ return m_id;
+}
+
+AnimateableProperty* SequenceEntry::getAnimProperty(AnimateablePropertyType type)
+{
+ switch(type)
+ {
+ case AP_VOLUME:
+ return &m_volume;
+ case AP_PITCH:
+ return &m_pitch;
+ case AP_PANNING:
+ return &m_panning;
+ case AP_LOCATION:
+ return &m_location;
+ case AP_ORIENTATION:
+ return &m_orientation;
+ default:
+ return nullptr;
+ }
+}
+
+bool SequenceEntry::isRelative()
+{
+ return m_relative;
+}
+
+void SequenceEntry::setRelative(bool relative)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ if(m_relative != relative)
+ {
+ m_relative = relative;
+ m_status++;
+ }
+}
+
+float SequenceEntry::getVolumeMaximum()
+{
+ return m_volume_max;
+}
+
+void SequenceEntry::setVolumeMaximum(float volume)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_volume_max = volume;
+ m_status++;
+}
+
+float SequenceEntry::getVolumeMinimum()
+{
+ return m_volume_min;
+}
+
+void SequenceEntry::setVolumeMinimum(float volume)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_volume_min = volume;
+ m_status++;
+}
+
+float SequenceEntry::getDistanceMaximum()
+{
+ return m_distance_max;
+}
+
+void SequenceEntry::setDistanceMaximum(float distance)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_distance_max = distance;
+ m_status++;
+}
+
+float SequenceEntry::getDistanceReference()
+{
+ return m_distance_reference;
+}
+
+void SequenceEntry::setDistanceReference(float distance)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_distance_reference = distance;
+ m_status++;
+}
+
+float SequenceEntry::getAttenuation()
+{
+ return m_attenuation;
+}
+
+void SequenceEntry::setAttenuation(float factor)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_attenuation = factor;
+ m_status++;
+}
+
+float SequenceEntry::getConeAngleOuter()
+{
+ return m_cone_angle_outer;
+}
+
+void SequenceEntry::setConeAngleOuter(float angle)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_cone_angle_outer = angle;
+ m_status++;
+}
+
+float SequenceEntry::getConeAngleInner()
+{
+ return m_cone_angle_inner;
+}
+
+void SequenceEntry::setConeAngleInner(float angle)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_cone_angle_inner = angle;
+ m_status++;
+}
+
+float SequenceEntry::getConeVolumeOuter()
+{
+ return m_cone_volume_outer;
+}
+
+void SequenceEntry::setConeVolumeOuter(float volume)
+{
+ std::lock_guard<std::recursive_mutex> lock(m_mutex);
+
+ m_cone_volume_outer = volume;
+ m_status++;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/SequenceHandle.cpp b/extern/audaspace/src/sequence/SequenceHandle.cpp
new file mode 100644
index 00000000000..140b1fbd94a
--- /dev/null
+++ b/extern/audaspace/src/sequence/SequenceHandle.cpp
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * 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 "SequenceHandle.h"
+#include "sequence/SequenceEntry.h"
+#include "devices/ReadDevice.h"
+#include "Exception.h"
+
+#include <mutex>
+
+#define KEEP_TIME 10
+
+AUD_NAMESPACE_BEGIN
+
+void SequenceHandle::start()
+{
+ // we already tried to start, aborting
+ if(!m_valid)
+ return;
+
+ // in case the sound is playing, we need to stop first
+ stop();
+
+ std::lock_guard<ILockable> lock(*m_entry);
+
+ // let's try playing
+ if(m_entry->m_sound.get())
+ {
+ try
+ {
+ m_handle = m_device.play(m_entry->m_sound, true);
+ m_3dhandle = std::dynamic_pointer_cast<I3DHandle>(m_handle);
+ }
+ catch(Exception&)
+ {
+ // handle stays invalid in case we get an exception
+ }
+
+ // after starting we have to set the properties, so let's ensure that
+ m_status--;
+ }
+
+ // if the sound could not be played, we invalidate
+ m_valid = m_handle.get();
+}
+
+bool SequenceHandle::updatePosition(float position)
+{
+ std::lock_guard<ILockable> lock(*m_entry);
+
+ if(m_handle.get())
+ {
+ // we currently have a handle, let's check where we are
+ if(position >= m_entry->m_end)
+ {
+ if(position >= m_entry->m_end + KEEP_TIME)
+ // far end, stopping
+ stop();
+ else
+ {
+ // close end, just pausing
+ m_handle->pause();
+ return true;
+ }
+ }
+ else if(position >= m_entry->m_begin)
+ {
+ // inside, resuming
+ m_handle->resume();
+ return true;
+ }
+ else
+ {
+ if(position < m_entry->m_begin - KEEP_TIME)
+ // far beginning, stopping
+ stop();
+ else
+ {
+ // close beginning, just pausing
+ m_handle->pause();
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // we don't have a handle, let's start if we should be playing
+ if(position >= m_entry->m_begin && position <= m_entry->m_end)
+ {
+ start();
+ return m_valid;
+ }
+ }
+
+ return false;
+}
+
+SequenceHandle::SequenceHandle(std::shared_ptr<SequenceEntry> entry, ReadDevice& device) :
+ m_entry(entry),
+ m_valid(true),
+ m_status(0),
+ m_pos_status(0),
+ m_sound_status(0),
+ m_device(device)
+{
+}
+
+SequenceHandle::~SequenceHandle()
+{
+ stop();
+}
+
+int SequenceHandle::compare(std::shared_ptr<SequenceEntry> entry) const
+{
+ if(m_entry->getID() < entry->getID())
+ return -1;
+ else if(m_entry->getID() == entry->getID())
+ return 0;
+ return 1;
+}
+
+void SequenceHandle::stop()
+{
+ if(m_handle.get())
+ m_handle->stop();
+ m_handle = nullptr;
+ m_3dhandle = nullptr;
+}
+
+void SequenceHandle::update(float position, float frame, float fps)
+{
+ if(m_sound_status != m_entry->m_sound_status)
+ {
+ // if a new sound has been set, it's possible to get valid again!
+ m_sound_status = m_entry->m_sound_status;
+ m_valid = true;
+
+ // stop whatever sound has been playing
+ stop();
+
+ // seek starts and seeks to the correct position
+ if(!seek(position))
+ // no handle, aborting
+ return;
+ }
+ else
+ {
+ if(!m_valid)
+ // invalid, aborting
+ return;
+
+ if(m_handle.get())
+ {
+ // we have a handle, let's update the position
+ if(!updatePosition(position))
+ // lost handle, aborting
+ return;
+ }
+ else
+ {
+ // we don't have a handle, let's see if we can start
+ if(!seek(position))
+ return;
+ }
+ }
+
+ std::lock_guard<ILockable> lock(*m_entry);
+ if(m_pos_status != m_entry->m_pos_status)
+ {
+ m_pos_status = m_entry->m_pos_status;
+
+ // position changed, need to seek
+ if(!seek(position))
+ // lost handle, aborting
+ return;
+ }
+
+ // so far everything alright and handle is there, let's keep going
+
+ if(m_status != m_entry->m_status)
+ {
+ m_3dhandle->setRelative(m_entry->m_relative);
+ m_3dhandle->setVolumeMaximum(m_entry->m_volume_max);
+ m_3dhandle->setVolumeMinimum(m_entry->m_volume_min);
+ m_3dhandle->setDistanceMaximum(m_entry->m_distance_max);
+ m_3dhandle->setDistanceReference(m_entry->m_distance_reference);
+ m_3dhandle->setAttenuation(m_entry->m_attenuation);
+ m_3dhandle->setConeAngleOuter(m_entry->m_cone_angle_outer);
+ m_3dhandle->setConeAngleInner(m_entry->m_cone_angle_inner);
+ m_3dhandle->setConeVolumeOuter(m_entry->m_cone_volume_outer);
+
+ m_status = m_entry->m_status;
+ }
+
+ float value;
+
+ m_entry->m_volume.read(frame, &value);
+ m_handle->setVolume(value);
+ m_entry->m_pitch.read(frame, &value);
+ m_handle->setPitch(value);
+ m_entry->m_panning.read(frame, &value);
+ SoftwareDevice::setPanning(m_handle.get(), value);
+
+ Vector3 v, v2;
+ Quaternion q;
+
+ m_entry->m_orientation.read(frame, q.get());
+ m_3dhandle->setOrientation(q);
+ m_entry->m_location.read(frame, v.get());
+ m_3dhandle->setLocation(v);
+ m_entry->m_location.read(frame + 1, v2.get());
+ v2 -= v;
+ m_3dhandle->setVelocity(v2 * fps);
+
+ if(m_entry->m_muted)
+ m_handle->setVolume(0);
+}
+
+bool SequenceHandle::seek(float position)
+{
+ if(!m_valid)
+ // sound not valid, aborting
+ return false;
+
+ if(!updatePosition(position))
+ // no handle, aborting
+ return false;
+
+ std::lock_guard<ILockable> lock(*m_entry);
+ float seekpos = position - m_entry->m_begin;
+ if(seekpos < 0)
+ seekpos = 0;
+ seekpos += m_entry->m_skip;
+ m_handle->setPitch(1.0f);
+ m_handle->seek(seekpos);
+
+ return true;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/SequenceHandle.h b/extern/audaspace/src/sequence/SequenceHandle.h
new file mode 100644
index 00000000000..9a77489a8f8
--- /dev/null
+++ b/extern/audaspace/src/sequence/SequenceHandle.h
@@ -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.
+ ******************************************************************************/
+
+#pragma once
+
+#include "Audaspace.h"
+
+#include <memory>
+
+AUD_NAMESPACE_BEGIN
+
+class ReadDevice;
+class IHandle;
+class I3DHandle;
+class SequenceEntry;
+
+/**
+ * Represents a playing sequenced entry.
+ */
+class SequenceHandle
+{
+private:
+ /// The entry this handle belongs to.
+ std::shared_ptr<SequenceEntry> m_entry;
+
+ /// The handle in the read device.
+ std::shared_ptr<IHandle> m_handle;
+
+ /// The 3D handle in the read device.
+ std::shared_ptr<I3DHandle> m_3dhandle;
+
+ /// Whether the sound is playable.
+ bool m_valid;
+
+ /// The last read status from the entry.
+ int m_status;
+
+ /// The last position status from the entry.
+ int m_pos_status;
+
+ /// The last sound status from the entry.
+ int m_sound_status;
+
+ /// The read device this handle is played on.
+ ReadDevice& m_device;
+
+ // delete copy constructor and operator=
+ SequenceHandle(const SequenceHandle&) = delete;
+ SequenceHandle& operator=(const SequenceHandle&) = delete;
+
+ /**
+ * Starts playing back the handle.
+ */
+ void start();
+
+ /**
+ * Updates the handle state depending on position.
+ * \param position Current playback position in seconds.
+ * \return Whether the handle is valid.
+ */
+ bool updatePosition(float position);
+
+public:
+ /**
+ * Creates a new sequenced handle.
+ * \param entry The entry this handle plays.
+ * \param device The read device to play on.
+ */
+ SequenceHandle(std::shared_ptr<SequenceEntry> entry, ReadDevice& device);
+
+ /**
+ * Destroys the handle.
+ */
+ ~SequenceHandle();
+
+ /**
+ * Compares whether this handle is playing the same entry as supplied.
+ * \param entry The entry to compare to.
+ * \return Whether the entries ID is smaller, equal or bigger.
+ */
+ int compare(std::shared_ptr<SequenceEntry> entry) const;
+
+ /**
+ * Stops playing back the handle.
+ */
+ void stop();
+
+ /**
+ * Updates the handle for playback.
+ * \param position The current time during playback.
+ * \param frame The current frame during playback.
+ * \param fps The animation frames per second.
+ */
+ void update(float position, float frame, float fps);
+
+ /**
+ * Seeks the handle to a specific time position.
+ * \param position The time to seek to.
+ * \return Whether the handle is valid.
+ */
+ bool seek(float position);
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/SequenceReader.cpp b/extern/audaspace/src/sequence/SequenceReader.cpp
new file mode 100644
index 00000000000..38647aaeadf
--- /dev/null
+++ b/extern/audaspace/src/sequence/SequenceReader.cpp
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ * 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 "sequence/SequenceReader.h"
+#include "sequence/SequenceData.h"
+#include "Exception.h"
+#include "SequenceHandle.h"
+
+#include <algorithm>
+#include <mutex>
+#include <cmath>
+
+AUD_NAMESPACE_BEGIN
+
+SequenceReader::SequenceReader(std::shared_ptr<SequenceData> sequence, bool quality) :
+ m_position(0), m_device(sequence->m_specs), m_sequence(sequence), m_status(0), m_entry_status(0)
+{
+ m_device.setQuality(quality);
+}
+
+SequenceReader::~SequenceReader()
+{
+}
+
+bool SequenceReader::isSeekable() const
+{
+ return true;
+}
+
+void SequenceReader::seek(int position)
+{
+ if(position < 0)
+ return;
+
+ m_position = position;
+
+ for(auto& handle : m_handles)
+ {
+ handle->seek(position / m_sequence->m_specs.rate);
+ }
+}
+
+int SequenceReader::getLength() const
+{
+ return -1;
+}
+
+int SequenceReader::getPosition() const
+{
+ return m_position;
+}
+
+Specs SequenceReader::getSpecs() const
+{
+ return m_sequence->m_specs;
+}
+
+void SequenceReader::read(int& length, bool& eos, sample_t* buffer)
+{
+ std::lock_guard<ILockable> lock(*m_sequence);
+
+ if(m_sequence->m_status != m_status)
+ {
+ m_device.changeSpecs(m_sequence->m_specs);
+ m_device.setSpeedOfSound(m_sequence->m_speed_of_sound);
+ m_device.setDistanceModel(m_sequence->m_distance_model);
+ m_device.setDopplerFactor(m_sequence->m_doppler_factor);
+
+ m_status = m_sequence->m_status;
+ }
+
+ if(m_sequence->m_entry_status != m_entry_status)
+ {
+ std::list<std::shared_ptr<SequenceHandle> > handles;
+
+ auto hit = m_handles.begin();
+ auto eit = m_sequence->m_entries.begin();
+
+ int result;
+ std::shared_ptr<SequenceHandle> handle;
+
+ while(hit != m_handles.end() && eit != m_sequence->m_entries.end())
+ {
+ handle = *hit;
+ std::shared_ptr<SequenceEntry> entry = *eit;
+
+ result = handle->compare(entry);
+
+ if(result < 0)
+ {
+ try
+ {
+ handle = std::shared_ptr<SequenceHandle>(new SequenceHandle(entry, m_device));
+ handles.push_back(handle);
+ }
+ catch(Exception&)
+ {
+ }
+ eit++;
+ }
+ else if(result == 0)
+ {
+ handles.push_back(handle);
+ hit++;
+ eit++;
+ }
+ else
+ {
+ handle->stop();
+ hit++;
+ }
+ }
+
+ while(hit != m_handles.end())
+ {
+ (*hit)->stop();
+ hit++;
+ }
+
+ while(eit != m_sequence->m_entries.end())
+ {
+ try
+ {
+ handle = std::shared_ptr<SequenceHandle>(new SequenceHandle(*eit, m_device));
+ handles.push_back(handle);
+ }
+ catch(Exception&)
+ {
+ }
+ eit++;
+ }
+
+ m_handles = handles;
+
+ m_entry_status = m_sequence->m_entry_status;
+ }
+
+ Specs specs = m_sequence->m_specs;
+ int pos = 0;
+ float time = float(m_position) / float(specs.rate);
+ float volume, frame;
+ int len, cfra;
+ Vector3 v, v2;
+ Quaternion q;
+
+
+ while(pos < length)
+ {
+ frame = time * m_sequence->m_fps;
+ cfra = int(std::floor(frame));
+
+ len = int(std::ceil((cfra + 1) / m_sequence->m_fps * specs.rate)) - m_position;
+ len = std::min(length - pos, len);
+ len = std::max(len, 1);
+
+ for(auto& handle : m_handles)
+ {
+ handle->update(time, frame, m_sequence->m_fps);
+ }
+
+ m_sequence->m_volume.read(frame, &volume);
+ if(m_sequence->m_muted)
+ volume = 0.0f;
+ m_device.setVolume(volume);
+
+ m_sequence->m_orientation.read(frame, q.get());
+ m_device.setListenerOrientation(q);
+ m_sequence->m_location.read(frame, v.get());
+ m_device.setListenerLocation(v);
+ m_sequence->m_location.read(frame + 1, v2.get());
+ v2 -= v;
+ m_device.setListenerVelocity(v2 * m_sequence->m_fps);
+
+ m_device.read(reinterpret_cast<data_t*>(buffer + specs.channels * pos), len);
+
+ pos += len;
+ time += float(len) / float(specs.rate);
+ }
+
+ m_position += length;
+
+ eos = false;
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/Superpose.cpp b/extern/audaspace/src/sequence/Superpose.cpp
new file mode 100644
index 00000000000..3d13f7db627
--- /dev/null
+++ b/extern/audaspace/src/sequence/Superpose.cpp
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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 "sequence/Superpose.h"
+#include "sequence/SuperposeReader.h"
+
+AUD_NAMESPACE_BEGIN
+
+Superpose::Superpose(std::shared_ptr<ISound> sound1, std::shared_ptr<ISound> sound2) :
+ m_sound1(sound1), m_sound2(sound2)
+{
+}
+
+std::shared_ptr<IReader> Superpose::createReader()
+{
+ std::shared_ptr<IReader> reader1 = m_sound1->createReader();
+ std::shared_ptr<IReader> reader2 = m_sound2->createReader();
+
+ return std::shared_ptr<IReader>(new SuperposeReader(reader1, reader2));
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/src/sequence/SuperposeReader.cpp b/extern/audaspace/src/sequence/SuperposeReader.cpp
new file mode 100644
index 00000000000..9206a7a96ef
--- /dev/null
+++ b/extern/audaspace/src/sequence/SuperposeReader.cpp
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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 "sequence/SuperposeReader.h"
+#include "Exception.h"
+
+#include <algorithm>
+#include <cstring>
+
+AUD_NAMESPACE_BEGIN
+
+SuperposeReader::SuperposeReader(std::shared_ptr<IReader> reader1, std::shared_ptr<IReader> reader2) :
+ m_reader1(reader1), m_reader2(reader2)
+{
+}
+
+SuperposeReader::~SuperposeReader()
+{
+}
+
+bool SuperposeReader::isSeekable() const
+{
+ return m_reader1->isSeekable() && m_reader2->isSeekable();
+}
+
+void SuperposeReader::seek(int position)
+{
+ m_reader1->seek(position);
+ m_reader2->seek(position);
+}
+
+int SuperposeReader::getLength() const
+{
+ int len1 = m_reader1->getLength();
+ int len2 = m_reader2->getLength();
+ if((len1 < 0) || (len2 < 0))
+ return -1;
+ return std::min(len1, len2);
+}
+
+int SuperposeReader::getPosition() const
+{
+ int pos1 = m_reader1->getPosition();
+ int pos2 = m_reader2->getPosition();
+ return std::max(pos1, pos2);
+}
+
+Specs SuperposeReader::getSpecs() const
+{
+ return m_reader1->getSpecs();
+}
+
+void SuperposeReader::read(int& length, bool& eos, sample_t* buffer)
+{
+ Specs specs = m_reader1->getSpecs();
+ Specs s2 = m_reader2->getSpecs();
+ if(!AUD_COMPARE_SPECS(specs, s2))
+ AUD_THROW(StateException, "Two readers with different specifiactions cannot be superposed.");
+
+ int samplesize = AUD_SAMPLE_SIZE(specs);
+
+ m_buffer.assureSize(length * samplesize);
+
+ int len1 = length;
+ m_reader1->read(len1, eos, buffer);
+
+ if(len1 < length)
+ std::memset(buffer + len1 * specs.channels, 0, (length - len1) * samplesize);
+
+ int len2 = length;
+ bool eos2;
+ sample_t* buf = m_buffer.getBuffer();
+ m_reader2->read(len2, eos2, buf);
+
+ for(int i = 0; i < len2 * specs.channels; i++)
+ buffer[i] += buf[i];
+
+ length = std::max(len1, len2);
+ eos &= eos2;
+}
+
+AUD_NAMESPACE_END