SoundStream.cpp
1 //
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 
34 namespace sf
35 {
40 myIsStreaming (false),
41 myChannelsCount (0),
42 mySampleRate (0),
43 myFormat (0),
44 myLoop (false),
45 mySamplesProcessed(0)
46 {
47 
48 }
49 
50 
55 {
56  // Stop the sound if it was playing
57  Stop();
58 }
59 
60 
64 void 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  {
97  Sound::Play();
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 
126 unsigned int SoundStream::GetChannelsCount() const
127 {
128  return myChannelsCount;
129 }
130 
131 
135 unsigned 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 
171 void SoundStream::SetLoop(bool Loop)
172 {
173  myLoop = Loop;
174 }
175 
176 
181 {
182  return myLoop;
183 }
184 
185 
189 void 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 
281 bool 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 
328 bool 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 
345 void 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 
361 bool SoundStream::OnStart()
362 {
363  // Does nothing by default
364 
365  return true;
366 }
367 
368 } // namespace sf
void Stop()
Stop the sound.
Definition: Sound.cpp:121
void Play()
Play the sound.
Definition: Sound.cpp:103
float GetPlayingOffset() const
Get the current playing position of the stream.
unsigned int GetSampleRate() const
Get the stream sample rate.
Sound is not playing.
Definition: Sound.hpp:54
Status GetStatus() const
Get the status of the sound (stopped, paused, playing)
Definition: Sound.cpp:343
void Wait()
Wait until the thread finishes.
Definition: Thread.cpp:89
void SetLoop(bool Loop)
Set the stream loop state.
SoundStream()
Default constructor.
Definition: SoundStream.cpp:39
unsigned int GetChannelsCount() const
Return the number of channels (1 = mono, 2 = stereo)
const Int16 * Samples
Pointer to the audio samples.
Definition: SoundStream.hpp:71
float GetPlayingOffset() const
Get the current playing position of the sound.
Definition: Sound.cpp:331
Sound is playing.
Definition: Sound.hpp:56
Structure defining a chunk of audio data to stream.
Definition: SoundStream.hpp:69
void Initialize(unsigned int ChannelsCount, unsigned int SampleRate)
Set the audio stream parameters, you must call it before Play()
Definition: SoundStream.cpp:64
Status GetStatus() const
Get the status of the stream (stopped, paused, playing)
std::size_t NbSamples
Number of samples pointed by Samples.
Definition: SoundStream.hpp:72
void Stop()
Stop playing the audio stream.
bool GetLoop() const
Tell whether or not the stream is looping.
Status
Enumeration of the sound states.
Definition: Sound.hpp:52
virtual ~SoundStream()
Virtual destructor.
Definition: SoundStream.cpp:54
void Launch()
Create and run the thread.
Definition: Thread.cpp:72
void Play()
Start playing the audio stream.
Definition: SoundStream.cpp:85