diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/llaudio/llstreamingaudio_fmod.cpp | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/linden/indra/llaudio/llstreamingaudio_fmod.cpp b/linden/indra/llaudio/llstreamingaudio_fmod.cpp new file mode 100644 index 0000000..a71a872 --- /dev/null +++ b/linden/indra/llaudio/llstreamingaudio_fmod.cpp | |||
@@ -0,0 +1,362 @@ | |||
1 | /** | ||
2 | * @file streamingaudio_fmod.cpp | ||
3 | * @brief LLStreamingAudio_FMOD implementation | ||
4 | * | ||
5 | * $LicenseInfo:firstyear=2009&license=viewergpl$ | ||
6 | * | ||
7 | * Copyright (c) 2009, Linden Research, Inc. | ||
8 | * | ||
9 | * Second Life Viewer Source Code | ||
10 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
11 | * to you under the terms of the GNU General Public License, version 2.0 | ||
12 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
13 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
15 | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 | ||
16 | * | ||
17 | * There are special exceptions to the terms and conditions of the GPL as | ||
18 | * it is applied to this Source Code. View the full text of the exception | ||
19 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
20 | * online at | ||
21 | * http://secondlifegrid.net/programs/open_source/licensing/flossexception | ||
22 | * | ||
23 | * By copying, modifying or distributing this software, you acknowledge | ||
24 | * that you have read and understood your obligations described above, | ||
25 | * and agree to abide by those obligations. | ||
26 | * | ||
27 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
28 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
29 | * COMPLETENESS OR PERFORMANCE. | ||
30 | * $/LicenseInfo$ | ||
31 | */ | ||
32 | |||
33 | #include "linden_common.h" | ||
34 | |||
35 | #include "llmath.h" | ||
36 | |||
37 | #include "fmod.h" | ||
38 | #include "fmod_errors.h" | ||
39 | |||
40 | #include "llstreamingaudio_fmod.h" | ||
41 | |||
42 | |||
43 | class LLAudioStreamManagerFMOD | ||
44 | { | ||
45 | public: | ||
46 | LLAudioStreamManagerFMOD(const std::string& url); | ||
47 | int startStream(); | ||
48 | bool stopStream(); // Returns true if the stream was successfully stopped. | ||
49 | bool ready(); | ||
50 | |||
51 | const std::string& getURL() { return mInternetStreamURL; } | ||
52 | |||
53 | int getOpenState(); | ||
54 | protected: | ||
55 | FSOUND_STREAM* mInternetStream; | ||
56 | bool mReady; | ||
57 | |||
58 | std::string mInternetStreamURL; | ||
59 | }; | ||
60 | |||
61 | |||
62 | |||
63 | //--------------------------------------------------------------------------- | ||
64 | // Internet Streaming | ||
65 | //--------------------------------------------------------------------------- | ||
66 | LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() : | ||
67 | mCurrentInternetStreamp(NULL), | ||
68 | mFMODInternetStreamChannel(-1), | ||
69 | mGain(1.0f) | ||
70 | { | ||
71 | // Number of milliseconds of audio to buffer for the audio card. | ||
72 | // Must be larger than the usual Second Life frame stutter time. | ||
73 | FSOUND_Stream_SetBufferSize(200); | ||
74 | |||
75 | // Here's where we set the size of the network buffer and some buffering | ||
76 | // parameters. In this case we want a network buffer of 16k, we want it | ||
77 | // to prebuffer 40% of that when we first connect, and we want it | ||
78 | // to rebuffer 80% of that whenever we encounter a buffer underrun. | ||
79 | |||
80 | // Leave the net buffer properties at the default. | ||
81 | //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); | ||
82 | } | ||
83 | |||
84 | |||
85 | LLStreamingAudio_FMOD::~LLStreamingAudio_FMOD() | ||
86 | { | ||
87 | // nothing interesting/safe to do. | ||
88 | } | ||
89 | |||
90 | |||
91 | void LLStreamingAudio_FMOD::start(const std::string& url) | ||
92 | { | ||
93 | //if (!mInited) | ||
94 | //{ | ||
95 | // llwarns << "startInternetStream before audio initialized" << llendl; | ||
96 | // return; | ||
97 | //} | ||
98 | |||
99 | // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL | ||
100 | stop(); | ||
101 | |||
102 | if (!url.empty()) | ||
103 | { | ||
104 | llinfos << "Starting internet stream: " << url << llendl; | ||
105 | mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url); | ||
106 | mURL = url; | ||
107 | } | ||
108 | else | ||
109 | { | ||
110 | llinfos << "Set internet stream to null" << llendl; | ||
111 | mURL.clear(); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | |||
116 | void LLStreamingAudio_FMOD::update() | ||
117 | { | ||
118 | // Kill dead internet streams, if possible | ||
119 | std::list<LLAudioStreamManagerFMOD *>::iterator iter; | ||
120 | for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) | ||
121 | { | ||
122 | LLAudioStreamManagerFMOD *streamp = *iter; | ||
123 | if (streamp->stopStream()) | ||
124 | { | ||
125 | llinfos << "Closed dead stream" << llendl; | ||
126 | delete streamp; | ||
127 | mDeadStreams.erase(iter++); | ||
128 | } | ||
129 | else | ||
130 | { | ||
131 | iter++; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | // Don't do anything if there are no streams playing | ||
136 | if (!mCurrentInternetStreamp) | ||
137 | { | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | int open_state = mCurrentInternetStreamp->getOpenState(); | ||
142 | |||
143 | if (!open_state) | ||
144 | { | ||
145 | // Stream is live | ||
146 | |||
147 | // start the stream if it's ready | ||
148 | if (mFMODInternetStreamChannel < 0) | ||
149 | { | ||
150 | mFMODInternetStreamChannel = mCurrentInternetStreamp->startStream(); | ||
151 | |||
152 | if (mFMODInternetStreamChannel != -1) | ||
153 | { | ||
154 | // Reset volume to previously set volume | ||
155 | setGain(getGain()); | ||
156 | FSOUND_SetPaused(mFMODInternetStreamChannel, false); | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | switch(open_state) | ||
162 | { | ||
163 | default: | ||
164 | case 0: | ||
165 | // success | ||
166 | break; | ||
167 | case -1: | ||
168 | // stream handle is invalid | ||
169 | llwarns << "InternetStream - invalid handle" << llendl; | ||
170 | stop(); | ||
171 | return; | ||
172 | case -2: | ||
173 | // opening | ||
174 | break; | ||
175 | case -3: | ||
176 | // failed to open, file not found, perhaps | ||
177 | llwarns << "InternetSteam - failed to open" << llendl; | ||
178 | stop(); | ||
179 | return; | ||
180 | case -4: | ||
181 | // connecting | ||
182 | break; | ||
183 | case -5: | ||
184 | // buffering | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | } | ||
189 | |||
190 | void LLStreamingAudio_FMOD::stop() | ||
191 | { | ||
192 | if (mFMODInternetStreamChannel != -1) | ||
193 | { | ||
194 | FSOUND_SetPaused(mFMODInternetStreamChannel, true); | ||
195 | FSOUND_SetPriority(mFMODInternetStreamChannel, 0); | ||
196 | mFMODInternetStreamChannel = -1; | ||
197 | } | ||
198 | |||
199 | if (mCurrentInternetStreamp) | ||
200 | { | ||
201 | llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl; | ||
202 | if (mCurrentInternetStreamp->stopStream()) | ||
203 | { | ||
204 | delete mCurrentInternetStreamp; | ||
205 | } | ||
206 | else | ||
207 | { | ||
208 | llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl; | ||
209 | mDeadStreams.push_back(mCurrentInternetStreamp); | ||
210 | } | ||
211 | mCurrentInternetStreamp = NULL; | ||
212 | //mURL.clear(); | ||
213 | } | ||
214 | } | ||
215 | |||
216 | void LLStreamingAudio_FMOD::pause(int pauseopt) | ||
217 | { | ||
218 | if (pauseopt < 0) | ||
219 | { | ||
220 | pauseopt = mCurrentInternetStreamp ? 1 : 0; | ||
221 | } | ||
222 | |||
223 | if (pauseopt) | ||
224 | { | ||
225 | if (mCurrentInternetStreamp) | ||
226 | { | ||
227 | stop(); | ||
228 | } | ||
229 | } | ||
230 | else | ||
231 | { | ||
232 | start(getURL()); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | |||
237 | // A stream is "playing" if it has been requested to start. That | ||
238 | // doesn't necessarily mean audio is coming out of the speakers. | ||
239 | int LLStreamingAudio_FMOD::isPlaying() | ||
240 | { | ||
241 | if (mCurrentInternetStreamp) | ||
242 | { | ||
243 | return 1; // Active and playing | ||
244 | } | ||
245 | else if (!mURL.empty()) | ||
246 | { | ||
247 | return 2; // "Paused" | ||
248 | } | ||
249 | else | ||
250 | { | ||
251 | return 0; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | |||
256 | F32 LLStreamingAudio_FMOD::getGain() | ||
257 | { | ||
258 | return mGain; | ||
259 | } | ||
260 | |||
261 | |||
262 | std::string LLStreamingAudio_FMOD::getURL() | ||
263 | { | ||
264 | return mURL; | ||
265 | } | ||
266 | |||
267 | |||
268 | void LLStreamingAudio_FMOD::setGain(F32 vol) | ||
269 | { | ||
270 | mGain = vol; | ||
271 | |||
272 | if (mFMODInternetStreamChannel != -1) | ||
273 | { | ||
274 | vol = llclamp(vol, 0.f, 1.f); | ||
275 | int vol_int = llround(vol * 255.f); | ||
276 | FSOUND_SetVolumeAbsolute(mFMODInternetStreamChannel, vol_int); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | |||
281 | /////////////////////////////////////////////////////// | ||
282 | // manager of possibly-multiple internet audio streams | ||
283 | |||
284 | LLAudioStreamManagerFMOD::LLAudioStreamManagerFMOD(const std::string& url) : | ||
285 | mInternetStream(NULL), | ||
286 | mReady(false) | ||
287 | { | ||
288 | mInternetStreamURL = url; | ||
289 | mInternetStream = FSOUND_Stream_Open(url.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0); | ||
290 | if (!mInternetStream) | ||
291 | { | ||
292 | llwarns << "Couldn't open fmod stream, error " | ||
293 | << FMOD_ErrorString(FSOUND_GetError()) | ||
294 | << llendl; | ||
295 | mReady = false; | ||
296 | return; | ||
297 | } | ||
298 | |||
299 | mReady = true; | ||
300 | } | ||
301 | |||
302 | int LLAudioStreamManagerFMOD::startStream() | ||
303 | { | ||
304 | // We need a live and opened stream before we try and play it. | ||
305 | if (!mInternetStream || getOpenState()) | ||
306 | { | ||
307 | llwarns << "No internet stream to start playing!" << llendl; | ||
308 | return -1; | ||
309 | } | ||
310 | |||
311 | // Make sure the stream is set to 2D mode. | ||
312 | FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D); | ||
313 | |||
314 | return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, true); | ||
315 | } | ||
316 | |||
317 | bool LLAudioStreamManagerFMOD::stopStream() | ||
318 | { | ||
319 | if (mInternetStream) | ||
320 | { | ||
321 | int read_percent = 0; | ||
322 | int status = 0; | ||
323 | int bitrate = 0; | ||
324 | unsigned int flags = 0x0; | ||
325 | FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags); | ||
326 | |||
327 | bool close = true; | ||
328 | switch (status) | ||
329 | { | ||
330 | case FSOUND_STREAM_NET_CONNECTING: | ||
331 | close = false; | ||
332 | break; | ||
333 | case FSOUND_STREAM_NET_NOTCONNECTED: | ||
334 | case FSOUND_STREAM_NET_BUFFERING: | ||
335 | case FSOUND_STREAM_NET_READY: | ||
336 | case FSOUND_STREAM_NET_ERROR: | ||
337 | default: | ||
338 | close = true; | ||
339 | } | ||
340 | |||
341 | if (close) | ||
342 | { | ||
343 | FSOUND_Stream_Close(mInternetStream); | ||
344 | mInternetStream = NULL; | ||
345 | return true; | ||
346 | } | ||
347 | else | ||
348 | { | ||
349 | return false; | ||
350 | } | ||
351 | } | ||
352 | else | ||
353 | { | ||
354 | return true; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | int LLAudioStreamManagerFMOD::getOpenState() | ||
359 | { | ||
360 | int open_state = FSOUND_Stream_GetOpenState(mInternetStream); | ||
361 | return open_state; | ||
362 | } | ||