SoundStream.cpp
1
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
26// Headers
28#include <SFML/Audio/SoundStream.hpp>
29#include <SFML/Audio/AudioDevice.hpp>
30#include <SFML/Audio/OpenAL.hpp>
31#include <SFML/System/Sleep.hpp>
32
33
34namespace sf
35{
40myIsStreaming (false),
41myChannelsCount (0),
42mySampleRate (0),
43myFormat (0),
44myLoop (false),
45mySamplesProcessed(0)
46{
47
48}
49
50
55{
56 // Stop the sound if it was playing
57 Stop();
58}
59
60
64void SoundStream::Initialize(unsigned int ChannelsCount, unsigned int SampleRate)
65{
66 myChannelsCount = ChannelsCount;
67 mySampleRate = SampleRate;
68
69 // Deduce the format from the number of channels
70 myFormat = priv::AudioDevice::GetInstance().GetFormatFromChannelsCount(ChannelsCount);
71
72 // Check if the format is valid
73 if (myFormat == 0)
74 {
75 myChannelsCount = 0;
76 mySampleRate = 0;
77 std::cerr << "Unsupported number of channels (" << myChannelsCount << ")" << std::endl;
78 }
79}
80
81
86{
87 // Check if the sound parameters have been set
88 if (myFormat == 0)
89 {
90 std::cerr << "Failed to play audio stream : sound parameters have not been initialized (call Initialize first)" << std::endl;
91 return;
92 }
93
94 // If the sound is already playing (probably paused), just resume it
95 if (myIsStreaming)
96 {
98 return;
99 }
100
101 // Notify the derived class
102 if (OnStart())
103 {
104 // Start updating the stream in a separate thread to avoid blocking the application
105 mySamplesProcessed = 0;
106 myIsStreaming = true;
107 Launch();
108 }
109}
110
111
116{
117 // Wait for the thread to terminate
118 myIsStreaming = false;
119 Wait();
120}
121
122
127{
128 return myChannelsCount;
129}
130
131
135unsigned int SoundStream::GetSampleRate() const
136{
137 return mySampleRate;
138}
139
140
145{
147
148 // To compensate for the lag between Play() and alSourcePlay()
149 if ((Status == Stopped) && myIsStreaming)
150 Status = Playing;
151
152 return Status;
153}
154
155
163{
164 return Sound::GetPlayingOffset() + static_cast<float>(mySamplesProcessed) / mySampleRate / myChannelsCount;
165}
166
167
171void SoundStream::SetLoop(bool Loop)
172{
173 myLoop = Loop;
174}
175
176
181{
182 return myLoop;
183}
184
185
189void SoundStream::Run()
190{
191 // Create the buffers
192 ALCheck(alGenBuffers(BuffersCount, myBuffers));
193 for (int i = 0; i < BuffersCount; ++i)
194 myEndBuffers[i] = false;
195
196 // Fill the queue
197 bool RequestStop = FillQueue();
198
199 // Play the sound
200 Sound::Play();
201
202 while (myIsStreaming)
203 {
204 // The stream has been interrupted !
205 if (Sound::GetStatus() == Stopped)
206 {
207 if (!RequestStop)
208 {
209 // Just continue
210 Sound::Play();
211 }
212 else
213 {
214 // End streaming
215 myIsStreaming = false;
216 }
217 }
218
219 // Get the number of buffers that have been processed (ie. ready for reuse)
220 ALint NbProcessed;
221 ALCheck(alGetSourcei(Sound::mySource, AL_BUFFERS_PROCESSED, &NbProcessed));
222
223 while (NbProcessed--)
224 {
225 // Pop the first unused buffer from the queue
226 ALuint Buffer;
227 ALCheck(alSourceUnqueueBuffers(Sound::mySource, 1, &Buffer));
228
229 // Find its number
230 unsigned int BufferNum = 0;
231 for (int i = 0; i < BuffersCount; ++i)
232 if (myBuffers[i] == Buffer)
233 {
234 BufferNum = i;
235 break;
236 }
237
238 // Retrieve its size and add it to the samples count
239 if (myEndBuffers[BufferNum])
240 {
241 // This was the last buffer: reset the sample count
242 mySamplesProcessed = 0;
243 myEndBuffers[BufferNum] = false;
244 }
245 else
246 {
247 ALint Size;
248 ALCheck(alGetBufferi(Buffer, AL_SIZE, &Size));
249 mySamplesProcessed += Size / sizeof(Int16);
250 }
251
252 // Fill it and push it back into the playing queue
253 if (!RequestStop)
254 {
255 if (FillAndPushBuffer(BufferNum))
256 RequestStop = true;
257 }
258 }
259
260 // Leave some time for the other threads if the stream is still playing
261 if (Sound::GetStatus() != Stopped)
262 Sleep(0.1f);
263 }
264
265 // Stop the playback
266 Sound::Stop();
267
268 // Unqueue any buffer left in the queue
269 ClearQueue();
270
271 // Delete the buffers
272 ALCheck(alSourcei(Sound::mySource, AL_BUFFER, 0));
273 ALCheck(alDeleteBuffers(BuffersCount, myBuffers));
274}
275
276
281bool SoundStream::FillAndPushBuffer(unsigned int BufferNum)
282{
283 bool RequestStop = false;
284
285 // Acquire audio data
286 Chunk Data = {NULL, 0};
287 if (!OnGetData(Data))
288 {
289 // Mark the buffer as the last one (so that we know when to reset the playing position)
290 myEndBuffers[BufferNum] = true;
291
292 // Check if the stream must loop or stop
293 if (myLoop && OnStart())
294 {
295 // If we succeeded to restart and we previously had no data, try to fill the buffer once again
296 if (!Data.Samples || (Data.NbSamples == 0))
297 {
298 return FillAndPushBuffer(BufferNum);
299 }
300 }
301 else
302 {
303 // Not looping or restart failed: request stop
304 RequestStop = true;
305 }
306 }
307
308 // Fill the buffer if some data was returned
309 if (Data.Samples && Data.NbSamples)
310 {
311 unsigned int Buffer = myBuffers[BufferNum];
312
313 // Fill the buffer
314 ALsizei Size = static_cast<ALsizei>(Data.NbSamples) * sizeof(Int16);
315 ALCheck(alBufferData(Buffer, myFormat, Data.Samples, Size, mySampleRate));
316
317 // Push it into the sound queue
318 ALCheck(alSourceQueueBuffers(Sound::mySource, 1, &Buffer));
319 }
320
321 return RequestStop;
322}
323
324
328bool SoundStream::FillQueue()
329{
330 // Fill and enqueue all the available buffers
331 bool RequestStop = false;
332 for (int i = 0; (i < BuffersCount) && !RequestStop; ++i)
333 {
334 if (FillAndPushBuffer(i))
335 RequestStop = true;
336 }
337
338 return RequestStop;
339}
340
341
345void SoundStream::ClearQueue()
346{
347 // Get the number of buffers still in the queue
348 ALint NbQueued;
349 ALCheck(alGetSourcei(Sound::mySource, AL_BUFFERS_QUEUED, &NbQueued));
350
351 // Unqueue them all
352 ALuint Buffer;
353 for (ALint i = 0; i < NbQueued; ++i)
354 ALCheck(alSourceUnqueueBuffers(Sound::mySource, 1, &Buffer));
355}
356
357
361bool SoundStream::OnStart()
362{
363 // Does nothing by default
364
365 return true;
366}
367
368} // namespace sf
void SetLoop(bool Loop)
Set the stream loop state.
@ Playing
Sound is playing.
Definition Sound.hpp:56
Status
Enumeration of the sound states.
Definition Sound.hpp:53
@ Stopped
Sound is not playing.
Definition Sound.hpp:54
void Play()
Start playing the audio stream.
void Stop()
Stop playing the audio stream.
float GetPlayingOffset() const
Get the current playing position of the stream.
virtual ~SoundStream()
Virtual destructor.
Status GetStatus() const
Get the status of the stream (stopped, paused, playing).
SoundStream()
Default constructor.
unsigned int GetSampleRate() const
Get the stream sample rate.
bool GetLoop() const
Tell whether or not the stream is looping.
void Initialize(unsigned int ChannelsCount, unsigned int SampleRate)
Set the audio stream parameters, you must call it before Play().
unsigned int GetChannelsCount() const
Return the number of channels (1 = mono, 2 = stereo).
float GetPlayingOffset() const
Get the current playing position of the sound.
Definition Sound.cpp:331
Status
Enumeration of the sound states.
Definition Sound.hpp:53
void Stop()
Stop the sound.
Definition Sound.cpp:121
Status GetStatus() const
Get the status of the sound (stopped, paused, playing).
Definition Sound.cpp:343
void Play()
Play the sound.
Definition Sound.cpp:103
void Launch()
Create and run the thread.
Definition Thread.cpp:72
void Wait()
Wait until the thread finishes.
Definition Thread.cpp:89
Structure defining a chunk of audio data to stream.