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)
float GetPlayingOffset() const
Get the current playing position of the sound.
Definition: Sound.cpp:331
Sound is playing.
Definition: Sound.hpp:56
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)
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