1 // sound_handler_sdl.cpp: Sound handling using standard SDL
2 //
3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 //   Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 //
20 
21 // Based on sound_handler_sdl.cpp by Thatcher Ulrich http://tulrich.com 2003
22 // which has been donated to the Public Domain.
23 
24 #include "sound_handler_sdl.h"
25 #include "SoundInfo.h"
26 #include "EmbedSound.h"
27 #include "AuxStream.h" // for use..
28 #include "GnashSleep.h"
29 
30 #include "log.h" // will import boost::format too
31 #include "GnashException.h" // for SoundException
32 
33 #include <vector>
34 #include <SDL.h>
35 
36 // Define this to get debugging call about pausing/unpausing audio
37 //#define GNASH_DEBUG_SDL_AUDIO_PAUSING
38 
39 // Mixing and decoding debugging
40 //#define GNASH_DEBUG_MIXING
41 
42 
43 namespace gnash {
44 namespace sound {
45 
46 
47 void
initAudio()48 SDL_sound_handler::initAudio()
49 {
50     // NOTE: we open and close the audio card for the sole purpose
51     //       of throwing an exception on error (unavailable audio
52     //       card). Normally we'd want to open the audio card only
53     //       when needed (it has a cost in number of wakeups).
54     openAudio();
55 
56 #ifdef WIN32
57     // SDL can hang on windows if SDL_CloseAudio() is called immediately
58     // after SDL_OpenAudio(). It's evidently to do with threading, but
59     // internal to SDL. This is a tacky solution, but it's only windows.
60     gnashSleep(1);
61 #endif
62 
63     closeAudio();
64 
65 }
66 
67 void
openAudio()68 SDL_sound_handler::openAudio()
69 {
70     if (_audioOpened) return; // nothing to do
71 
72     // This is our sound settings
73     audioSpec.freq = 44100;
74 
75     // Each sample is a signed 16-bit audio in system-endian format
76     audioSpec.format = AUDIO_S16SYS;
77 
78     // We want to be pulling samples for 2 channels:
79     // {left,right},{left,right},...
80     audioSpec.channels = 2;
81 
82     audioSpec.callback = SDL_sound_handler::sdl_audio_callback;
83 
84     audioSpec.userdata = this;
85 
86     //512 - not enough for  videostream
87     audioSpec.samples = 1024;
88 
89     if (SDL_OpenAudio(&audioSpec, nullptr) < 0) {
90             boost::format fmt = boost::format(_("Couldn't open SDL audio: %s"))
91                 % SDL_GetError();
92         throw SoundException(fmt.str());
93     }
94 
95     _audioOpened = true;
96 }
97 
98 void
closeAudio()99 SDL_sound_handler::closeAudio()
100 {
101     SDL_CloseAudio();
102     _audioOpened = false;
103 }
104 
105 
SDL_sound_handler(media::MediaHandler * m)106 SDL_sound_handler::SDL_sound_handler(media::MediaHandler* m)
107     :
108     sound_handler(m),
109     _audioOpened(false)
110 {
111     initAudio();
112 }
113 
114 void
reset()115 SDL_sound_handler::reset()
116 {
117     std::lock_guard<std::mutex> lock(_mutex);
118     sound_handler::stop_all_sounds();
119 }
120 
~SDL_sound_handler()121 SDL_sound_handler::~SDL_sound_handler()
122 {
123     std::lock_guard<std::mutex> lock(_mutex);
124 
125 #ifdef GNASH_DEBUG_SDL_AUDIO_PAUSING
126     log_debug("Pausing SDL Audio on destruction");
127 #endif
128     SDL_PauseAudio(1);
129 
130     // this one takes 2 seconds on my machine ...
131     SDL_CloseAudio();
132 
133 }
134 
135 int
createStreamingSound(const media::SoundInfo & sinfo)136 SDL_sound_handler::createStreamingSound(const media::SoundInfo& sinfo)
137 {
138     std::lock_guard<std::mutex> lock(_mutex);
139     return sound_handler::createStreamingSound(sinfo);
140 }
141 
142 int
create_sound(std::unique_ptr<SimpleBuffer> data,const media::SoundInfo & sinfo)143 SDL_sound_handler::create_sound(std::unique_ptr<SimpleBuffer> data,
144                                 const media::SoundInfo& sinfo)
145 {
146     std::lock_guard<std::mutex> lock(_mutex);
147     return sound_handler::create_sound(std::move(data), sinfo);
148 }
149 
150 sound_handler::StreamBlockId
addSoundBlock(SimpleBuffer buf,size_t sampleCount,int seekSamples,int handle)151 SDL_sound_handler::addSoundBlock(SimpleBuffer buf,
152         size_t sampleCount, int seekSamples, int handle)
153 {
154 
155     std::lock_guard<std::mutex> lock(_mutex);
156     return sound_handler::addSoundBlock(std::move(buf), sampleCount, seekSamples, handle);
157 }
158 
159 
160 void
stopEventSound(int soundHandle)161 SDL_sound_handler::stopEventSound(int soundHandle)
162 {
163     std::lock_guard<std::mutex> lock(_mutex);
164     sound_handler::stopEventSound(soundHandle);
165 }
166 
167 void
stopAllEventSounds()168 SDL_sound_handler::stopAllEventSounds()
169 {
170     std::lock_guard<std::mutex> lock(_mutex);
171     sound_handler::stopAllEventSounds();
172 }
173 
174 void
stopStreamingSound(int soundHandle)175 SDL_sound_handler::stopStreamingSound(int soundHandle)
176 {
177     std::lock_guard<std::mutex> lock(_mutex);
178     sound_handler::stopStreamingSound(soundHandle);
179 }
180 
181 
182 void
delete_sound(int soundHandle)183 SDL_sound_handler::delete_sound(int soundHandle)
184 {
185     std::lock_guard<std::mutex> lock(_mutex);
186     sound_handler::delete_sound(soundHandle);
187 }
188 
189 void
stop_all_sounds()190 SDL_sound_handler::stop_all_sounds()
191 {
192     std::lock_guard<std::mutex> lock(_mutex);
193     sound_handler::stop_all_sounds();
194 }
195 
196 
197 int
get_volume(int soundHandle) const198 SDL_sound_handler::get_volume(int soundHandle) const
199 {
200     std::lock_guard<std::mutex> lock(_mutex);
201     return sound_handler::get_volume(soundHandle);
202 }
203 
204 
205 void
set_volume(int soundHandle,int volume)206 SDL_sound_handler::set_volume(int soundHandle, int volume)
207 {
208     std::lock_guard<std::mutex> lock(_mutex);
209     sound_handler::set_volume(soundHandle, volume);
210 }
211 
212 media::SoundInfo*
get_sound_info(int soundHandle) const213 SDL_sound_handler::get_sound_info(int soundHandle) const
214 {
215     std::lock_guard<std::mutex> lock(_mutex);
216     return sound_handler::get_sound_info(soundHandle);
217 }
218 
219 unsigned int
get_duration(int soundHandle) const220 SDL_sound_handler::get_duration(int soundHandle) const
221 {
222     std::lock_guard<std::mutex> lock(_mutex);
223     return sound_handler::get_duration(soundHandle);
224 }
225 
226 unsigned int
tell(int soundHandle) const227 SDL_sound_handler::tell(int soundHandle) const
228 {
229     std::lock_guard<std::mutex> lock(_mutex);
230     return sound_handler::tell(soundHandle);
231 }
232 
233 sound_handler*
create_sound_handler_sdl(media::MediaHandler * m)234 create_sound_handler_sdl(media::MediaHandler* m)
235 {
236     return new SDL_sound_handler(m);
237 }
238 
239 void
fetchSamples(std::int16_t * to,unsigned int nSamples)240 SDL_sound_handler::fetchSamples(std::int16_t* to, unsigned int nSamples)
241 {
242     std::lock_guard<std::mutex> lock(_mutex);
243     sound_handler::fetchSamples(to, nSamples);
244 
245     // If nothing is left to play there is no reason to keep polling.
246     if ( ! hasInputStreams() )
247     {
248 #ifdef GNASH_DEBUG_SDL_AUDIO_PAUSING
249         log_debug("Pausing SDL Audio...");
250 #endif
251         SDL_PauseAudio(1);
252     }
253 }
254 
255 // Callback invoked by the SDL audio thread.
256 void
sdl_audio_callback(void * udata,Uint8 * buf,int bufLenIn)257 SDL_sound_handler::sdl_audio_callback(void *udata, Uint8 *buf, int bufLenIn)
258 {
259     if (bufLenIn < 0) {
260         log_error(_("Negative buffer length in sdl_audio_callback (%d)"),
261                 bufLenIn);
262         return;
263     }
264 
265     if (bufLenIn == 0) {
266         log_error(_("Zero buffer length in sdl_audio_callback"));
267         return;
268     }
269 
270     unsigned int bufLen = static_cast<unsigned int>(bufLenIn);
271     std::int16_t* samples = reinterpret_cast<std::int16_t*>(buf);
272 
273     // 16 bit per sample, 2 channels == 4 bytes per fetch ?
274     assert(!(bufLen%4));
275 
276     unsigned int nSamples = bufLen/2;
277 
278     //log_debug("Fetching %d bytes (%d samples)", bufLen, nSamples);
279 
280     // Get the soundhandler
281     SDL_sound_handler* handler = static_cast<SDL_sound_handler*>(udata);
282     handler->fetchSamples(samples, nSamples);
283 }
284 
285 void
mix(std::int16_t * outSamples,std::int16_t * inSamples,unsigned int nSamples,float volume)286 SDL_sound_handler::mix(std::int16_t* outSamples, std::int16_t* inSamples,
287             unsigned int nSamples, float volume)
288 {
289     Uint8* out = reinterpret_cast<Uint8*>(outSamples);
290     Uint8* in = reinterpret_cast<Uint8*>(inSamples);
291     unsigned int nBytes = nSamples*2;
292 
293     SDL_MixAudio(out, in, nBytes, SDL_MIX_MAXVOLUME*volume);
294 }
295 
296 void
plugInputStream(std::unique_ptr<InputStream> newStreamer)297 SDL_sound_handler::plugInputStream(std::unique_ptr<InputStream> newStreamer)
298 {
299     std::lock_guard<std::mutex> lock(_mutex);
300 
301     sound_handler::plugInputStream(std::move(newStreamer));
302 
303     { // TODO: this whole block should only be executed when adding
304       // the first stream.
305 
306 #ifdef GNASH_DEBUG_SDL_AUDIO_PAUSING
307         log_debug("Unpausing SDL Audio on inpust stream plug...");
308 #endif
309         openAudio(); // lazy sound card initialization
310         SDL_PauseAudio(0); // start polling data from us
311     }
312 }
313 
314 void
pause()315 SDL_sound_handler::pause()
316 {
317     closeAudio();
318     sound_handler::pause();
319 }
320 
321 void
unpause()322 SDL_sound_handler::unpause()
323 {
324     if (hasInputStreams()) {
325         openAudio();
326         SDL_PauseAudio(0);
327     }
328 
329     sound_handler::unpause();
330 }
331 
332 void
unplugInputStream(InputStream * id)333 SDL_sound_handler::unplugInputStream(InputStream* id)
334 {
335     std::lock_guard<std::mutex> lock(_mutex);
336 
337     sound_handler::unplugInputStream(id);
338 }
339 
340 } // gnash.sound namespace
341 } // namespace gnash
342 
343 // Local Variables:
344 // mode: C++
345 // End:
346 
347