1 /*******************************************************************************
2  * Copyright 2009-2016 Jörg Müller
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 #include "SequenceHandle.h"
18 #include "sequence/SequenceEntry.h"
19 #include "devices/ReadDevice.h"
20 #include "Exception.h"
21 
22 #include <mutex>
23 
24 #define KEEP_TIME 10
25 
26 AUD_NAMESPACE_BEGIN
27 
start()28 void SequenceHandle::start()
29 {
30 	// we already tried to start, aborting
31 	if(!m_valid)
32 		return;
33 
34 	// in case the sound is playing, we need to stop first
35 	stop();
36 
37 	std::lock_guard<ILockable> lock(*m_entry);
38 
39 	// let's try playing
40 	if(m_entry->m_sound.get())
41 	{
42 		try
43 		{
44 			m_handle = m_device.play(m_entry->m_sound, true);
45 			m_3dhandle = std::dynamic_pointer_cast<I3DHandle>(m_handle);
46 		}
47 		catch(Exception&)
48 		{
49 			// handle stays invalid in case we get an exception
50 		}
51 
52 		// after starting we have to set the properties, so let's ensure that
53 		m_status--;
54 	}
55 
56 	// if the sound could not be played, we invalidate
57 	m_valid = m_handle.get();
58 }
59 
updatePosition(double position)60 bool SequenceHandle::updatePosition(double position)
61 {
62 	std::lock_guard<ILockable> lock(*m_entry);
63 
64 	if(m_handle.get())
65 	{
66 		// we currently have a handle, let's check where we are
67 		if(position >= m_entry->m_end)
68 		{
69 			if(position >= m_entry->m_end + KEEP_TIME)
70 				// far end, stopping
71 				stop();
72 			else
73 			{
74 				// close end, just pausing
75 				m_handle->pause();
76 				return true;
77 			}
78 		}
79 		else if(position >= m_entry->m_begin)
80 		{
81 			// inside, resuming
82 			m_handle->resume();
83 			return true;
84 		}
85 		else
86 		{
87 			if(position < m_entry->m_begin - KEEP_TIME)
88 				// far beginning, stopping
89 				stop();
90 			else
91 			{
92 				// close beginning, just pausing
93 				m_handle->pause();
94 				return true;
95 			}
96 		}
97 	}
98 	else
99 	{
100 		// we don't have a handle, let's start if we should be playing
101 		if(position >= m_entry->m_begin && position <= m_entry->m_end)
102 		{
103 			start();
104 			return m_valid;
105 		}
106 	}
107 
108 	return false;
109 }
110 
SequenceHandle(std::shared_ptr<SequenceEntry> entry,ReadDevice & device)111 SequenceHandle::SequenceHandle(std::shared_ptr<SequenceEntry> entry, ReadDevice& device) :
112 	m_entry(entry),
113 	m_valid(true),
114 	m_status(0),
115 	m_pos_status(0),
116 	m_sound_status(0),
117 	m_device(device)
118 {
119 }
120 
~SequenceHandle()121 SequenceHandle::~SequenceHandle()
122 {
123 	stop();
124 }
125 
compare(std::shared_ptr<SequenceEntry> entry) const126 int SequenceHandle::compare(std::shared_ptr<SequenceEntry> entry) const
127 {
128 	if(m_entry->getID() < entry->getID())
129 		return -1;
130 	else if(m_entry->getID() == entry->getID())
131 		return 0;
132 	return 1;
133 }
134 
stop()135 void SequenceHandle::stop()
136 {
137 	if(m_handle.get())
138 		m_handle->stop();
139 	m_handle = nullptr;
140 	m_3dhandle = nullptr;
141 }
142 
update(double position,float frame,float fps)143 void SequenceHandle::update(double position, float frame, float fps)
144 {
145 	if(m_sound_status != m_entry->m_sound_status)
146 	{
147 		// if a new sound has been set, it's possible to get valid again!
148 		m_sound_status = m_entry->m_sound_status;
149 		m_valid = true;
150 
151 		// stop whatever sound has been playing
152 		stop();
153 
154 		// seek starts and seeks to the correct position
155 		if(!seek(position))
156 			// no handle, aborting
157 			return;
158 	}
159 	else
160 	{
161 		if(!m_valid)
162 			// invalid, aborting
163 			return;
164 
165 		if(m_handle.get())
166 		{
167 			// we have a handle, let's update the position
168 			if(!updatePosition(position))
169 				// lost handle, aborting
170 				return;
171 		}
172 		else
173 		{
174 			// we don't have a handle, let's see if we can start
175 			if(!seek(position))
176 				return;
177 		}
178 	}
179 
180 	std::lock_guard<ILockable> lock(*m_entry);
181 	if(m_pos_status != m_entry->m_pos_status)
182 	{
183 		m_pos_status = m_entry->m_pos_status;
184 
185 		// position changed, need to seek
186 		if(!seek(position))
187 			// lost handle, aborting
188 			return;
189 	}
190 
191 	// so far everything alright and handle is there, let's keep going
192 
193 	if(m_status != m_entry->m_status)
194 	{
195 		m_3dhandle->setRelative(m_entry->m_relative);
196 		m_3dhandle->setVolumeMaximum(m_entry->m_volume_max);
197 		m_3dhandle->setVolumeMinimum(m_entry->m_volume_min);
198 		m_3dhandle->setDistanceMaximum(m_entry->m_distance_max);
199 		m_3dhandle->setDistanceReference(m_entry->m_distance_reference);
200 		m_3dhandle->setAttenuation(m_entry->m_attenuation);
201 		m_3dhandle->setConeAngleOuter(m_entry->m_cone_angle_outer);
202 		m_3dhandle->setConeAngleInner(m_entry->m_cone_angle_inner);
203 		m_3dhandle->setConeVolumeOuter(m_entry->m_cone_volume_outer);
204 
205 		m_status = m_entry->m_status;
206 	}
207 
208 	float value;
209 
210 	m_entry->m_volume.read(frame, &value);
211 	m_handle->setVolume(value);
212 	m_entry->m_pitch.read(frame, &value);
213 	m_handle->setPitch(value);
214 	m_entry->m_panning.read(frame, &value);
215 	SoftwareDevice::setPanning(m_handle.get(), value);
216 
217 	Vector3 v, v2;
218 	Quaternion q;
219 
220 	m_entry->m_orientation.read(frame, q.get());
221 	m_3dhandle->setOrientation(q);
222 	m_entry->m_location.read(frame, v.get());
223 	m_3dhandle->setLocation(v);
224 	m_entry->m_location.read(frame + 1, v2.get());
225 	v2 -= v;
226 	m_3dhandle->setVelocity(v2 * fps);
227 
228 	if(m_entry->m_muted)
229 		m_handle->setVolume(0);
230 }
231 
seek(double position)232 bool SequenceHandle::seek(double position)
233 {
234 	if(!m_valid)
235 		// sound not valid, aborting
236 		return false;
237 
238 	if(!updatePosition(position))
239 		// no handle, aborting
240 		return false;
241 
242 	std::lock_guard<ILockable> lock(*m_entry);
243 	double seekpos = position - m_entry->m_begin;
244 	if(seekpos < 0)
245 		seekpos = 0;
246 	seekpos += m_entry->m_skip;
247 	m_handle->setPitch(1.0f);
248 	m_handle->seek(seekpos);
249 
250 	return true;
251 }
252 
253 AUD_NAMESPACE_END
254