1 /**
2  * Copyright (c) 2006-2019 LOVE Development Team
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  **/
20 
21 #include "Pool.h"
22 
23 #include "Source.h"
24 
25 namespace love
26 {
27 namespace audio
28 {
29 namespace openal
30 {
31 
Pool()32 Pool::Pool()
33 	: sources()
34 	, totalSources(0)
35 {
36 	// Clear errors.
37 	alGetError();
38 
39 	// Generate sources.
40 	for (int i = 0; i < MAX_SOURCES; i++)
41 	{
42 		alGenSources(1, &sources[i]);
43 
44 		// We might hit an implementation-dependent limit on the total number
45 		// of sources before reaching MAX_SOURCES.
46 		if (alGetError() != AL_NO_ERROR)
47 			break;
48 
49 		totalSources++;
50 	}
51 
52 	if (totalSources < 4)
53 		throw love::Exception("Could not generate sources.");
54 
55 #ifdef AL_SOFT_direct_channels
56 	ALboolean hasext = alIsExtensionPresent("AL_SOFT_direct_channels");
57 #endif
58 
59 	// Make all sources available initially.
60 	for (int i = 0; i < totalSources; i++)
61 	{
62 #ifdef AL_SOFT_direct_channels
63 		if (hasext)
64 		{
65 			// Bypass virtualization of speakers for multi-channel sources in OpenAL Soft.
66 			alSourcei(sources[i], AL_DIRECT_CHANNELS_SOFT, AL_TRUE);
67 		}
68 #endif
69 
70 		available.push(sources[i]);
71 	}
72 }
73 
~Pool()74 Pool::~Pool()
75 {
76 	Source::stop(this);
77 
78 	// Free all sources.
79 	alDeleteSources(totalSources, sources);
80 }
81 
isAvailable() const82 bool Pool::isAvailable() const
83 {
84 	bool has = false;
85 	{
86 		thread::Lock lock(mutex);
87 		has = !available.empty();
88 	}
89 	return has;
90 }
91 
isPlaying(Source * s)92 bool Pool::isPlaying(Source *s)
93 {
94 	bool p = false;
95 	{
96 		thread::Lock lock(mutex);
97 		p = (playing.find(s) != playing.end());
98 	}
99 	return p;
100 }
101 
update()102 void Pool::update()
103 {
104 	thread::Lock lock(mutex);
105 
106 	std::vector<Source *> torelease;
107 
108 	for (const auto &i : playing)
109 	{
110 		if (!i.first->update())
111 			torelease.push_back(i.first);
112 	}
113 
114 	for (Source *s : torelease)
115 		releaseSource(s);
116 }
117 
getActiveSourceCount() const118 int Pool::getActiveSourceCount() const
119 {
120 	return (int) playing.size();
121 }
122 
getMaxSources() const123 int Pool::getMaxSources() const
124 {
125 	return totalSources;
126 }
127 
assignSource(Source * source,ALuint & out,char & wasPlaying)128 bool Pool::assignSource(Source *source, ALuint &out, char &wasPlaying)
129 {
130 	out = 0;
131 
132 	if (findSource(source, out))
133 		return wasPlaying = true;
134 
135 	wasPlaying = false;
136 
137 	if (available.empty())
138 		return false;
139 
140 	out = available.front();
141 	available.pop();
142 
143 	playing.insert(std::make_pair(source, out));
144 	source->retain();
145 	return true;
146 }
147 
releaseSource(Source * source,bool stop)148 bool Pool::releaseSource(Source *source, bool stop)
149 {
150 	ALuint s;
151 
152 	if (findSource(source, s))
153 	{
154 		if (stop)
155 			source->stopAtomic();
156 		source->release();
157 		available.push(s);
158 		playing.erase(source);
159 		return true;
160 	}
161 
162 	return false;
163 }
164 
findSource(Source * source,ALuint & out)165 bool Pool::findSource(Source *source, ALuint &out)
166 {
167 	std::map<Source *, ALuint>::const_iterator i = playing.find(source);
168 
169 	if (i == playing.end())
170 		return false;
171 
172 	out = i->second;
173 	return true;
174 }
175 
lock()176 thread::Lock Pool::lock()
177 {
178 	return thread::Lock(mutex);
179 }
180 
getPlayingSources()181 std::vector<love::audio::Source*> Pool::getPlayingSources()
182 {
183 	std::vector<love::audio::Source*> sources;
184 	sources.reserve(playing.size());
185 	for (auto &i : playing)
186 		sources.push_back(i.first);
187 	return sources;
188 }
189 
190 } // openal
191 } // audio
192 } // love
193