1 // *sigh*, looking forward to VS.NET...
2 #ifdef _MSC_VER
3 #pragma warning(disable : 4786)
4 #endif
5 
6 
7 #include <string>
8 #include "audiere.h"
9 #include "debug.h"
10 #include "device_null.h"
11 #include "internal.h"
12 #include "threads.h"
13 
14 #ifdef _MSC_VER
15 
16   #include <windows.h>
17   #include <mmsystem.h>
18   #include "device_ds.h"
19   #include "device_mm.h"
20 
21 #endif
22 
23 #ifdef HAVE_OSS
24   #include "device_oss.h"
25 #endif
26 
27 #ifdef HAVE_AL
28   #include "device_al.h"
29 #endif
30 
31 #ifdef HAVE_DSOUND
32   #include "device_ds.h"
33 #endif
34 
35 #ifdef HAVE_WINMM
36   #include "device_mm.h"
37 #endif
38 
39 
40 namespace audiere {
41 
AbstractDevice()42   AbstractDevice::AbstractDevice() {
43     m_thread_exists = false;
44     m_thread_should_die = false;
45 
46     bool result = AI_CreateThread(eventThread, this, 2);
47     if (!result) {
48       ADR_LOG("THREAD CREATION FAILED");
49     }
50   }
51 
~AbstractDevice()52   AbstractDevice::~AbstractDevice() {
53     m_thread_should_die = true;
54 
55     // Trick the thread into no longer waiting.
56     m_events_available.notify();
57 
58     while (m_thread_exists) {
59       AI_Sleep(50);
60     }
61   }
62 
registerCallback(Callback * callback)63   void AbstractDevice::registerCallback(Callback* callback) {
64     m_callbacks.push_back(callback);
65   }
66 
unregisterCallback(Callback * callback)67   void AbstractDevice::unregisterCallback(Callback* callback) {
68     for (size_t i = 0; i < m_callbacks.size(); ++i) {
69       if (m_callbacks[i] == callback) {
70         m_callbacks.erase(m_callbacks.begin() + i);
71         return;
72       }
73     }
74   }
75 
clearCallbacks()76   void AbstractDevice::clearCallbacks() {
77     m_callbacks.clear();
78   }
79 
fireStopEvent(OutputStream * stream,StopEvent::Reason reason)80   void AbstractDevice::fireStopEvent(OutputStream* stream, StopEvent::Reason reason) {
81     StopEventPtr event = new StopEventImpl(stream, reason);
82     fireStopEvent(event);
83   }
84 
fireStopEvent(const StopEventPtr & event)85   void AbstractDevice::fireStopEvent(const StopEventPtr& event) {
86     m_event_mutex.lock();
87     m_events.push(event.get());
88     m_event_mutex.unlock();
89     m_events_available.notify();
90   }
91 
eventThread(void * arg)92   void AbstractDevice::eventThread(void* arg) {
93     ADR_GUARD("AbstractDevice::eventThread[static]");
94     ADR_LOG(arg ? "arg is valid" : "arg is not valid");
95 
96     AbstractDevice* This = static_cast<AbstractDevice*>(arg);
97     This->eventThread();
98   }
99 
eventThread()100   void AbstractDevice::eventThread() {
101     ADR_GUARD("AbstractDevice::eventThread");
102     m_thread_exists = true;
103     while (!m_thread_should_die) {
104       m_event_mutex.lock();
105       while (m_events.empty()) {
106         m_events_available.wait(m_event_mutex, 1);
107         if (m_thread_should_die) {
108           break;
109         }
110       }
111       if (m_thread_should_die) {
112         m_event_mutex.unlock();
113         break;
114       }
115 
116       // Make a local copy of the events so they can be processed without
117       // leaving the mutex locked.
118       EventQueue events = m_events;
119 
120       // Queues don't support clear().  o_o
121       while (!m_events.empty()) {
122         m_events.pop();
123       }
124 
125       m_event_mutex.unlock();
126 
127       // Process the events.
128       while (!events.empty()) {
129         EventPtr event = events.front();
130         events.pop();
131         processEvent(event.get());
132       }
133     }
134     m_thread_exists = false;
135   }
136 
processEvent(Event * event)137   void AbstractDevice::processEvent(Event* event) {
138     for (size_t i = 0; i < m_callbacks.size(); ++i) {
139       if (event->getType() == m_callbacks[i]->getType()) {
140         m_callbacks[i]->call(event);
141       }
142     }
143   }
144 
145 
AdrGetSupportedAudioDevices()146   ADR_EXPORT(const char*) AdrGetSupportedAudioDevices() {
147     return
148 #ifdef _MSC_VER
149       "directsound:DirectSound (high-performance)"  ";"
150       "winmm:Windows Multimedia (compatible)"  ";"
151 #else
152 #ifdef HAVE_OSS
153       "oss:Open Sound System"  ";"
154 #endif
155 #ifdef HAVE_DSOUND
156       "directsound:DirectSound (high-performance)"  ";"
157 #endif
158 #ifdef HAVE_WINMM
159       "winmm:Windows Multimedia (compatible)"  ";"
160 #endif
161 #ifdef HAVE_AL
162       "al:SGI AL"  ";"
163 #endif
164 #endif
165       "null:Null output (no sound)"  ;
166   }
167 
168 
169   #define NEED_SEMICOLON do ; while (false)
170 
171   #define TRY_GROUP(group_name) {                               \
172     AudioDevice* device = DoOpenDevice(group_name, parameters); \
173     if (device) {                                               \
174       return device;                                            \
175     }                                                           \
176   } NEED_SEMICOLON
177 
178   #define TRY_DEVICE(DeviceType) {                         \
179     DeviceType* device = DeviceType::create(parameters);   \
180     if (device) {                                          \
181       return device;                                       \
182     }                                                      \
183   } NEED_SEMICOLON
184 
185 
DoOpenDevice(const std::string & name,const ParameterList & parameters)186   AudioDevice* DoOpenDevice(
187     const std::string& name,
188     const ParameterList& parameters)
189   {
190     ADR_GUARD("DoOpenDevice");
191 
192     #ifdef _MSC_VER
193 
194       if (name == "" || name == "autodetect") {
195         TRY_GROUP("directsound");
196         TRY_GROUP("winmm");
197         return 0;
198       }
199 
200       if (name == "directsound") {
201         TRY_DEVICE(DSAudioDevice);
202         return 0;
203       }
204 
205       if (name == "winmm") {
206         TRY_DEVICE(MMAudioDevice);
207         return 0;
208       }
209 
210       if (name == "null") {
211         TRY_DEVICE(NullAudioDevice);
212         return 0;
213       }
214 
215     #else  // not Win32 - assume autoconf UNIX
216 
217       if (name == "" || name == "autodetect") {
218         // in decreasing order of sound API quality
219         TRY_GROUP("al");
220         TRY_GROUP("directsound");
221         TRY_GROUP("winmm");
222         TRY_GROUP("oss");
223         return 0;
224       }
225 
226       #ifdef HAVE_OSS
227         if (name == "oss") {
228           TRY_DEVICE(OSSAudioDevice);
229           return 0;
230         }
231       #endif
232 
233       #ifdef HAVE_DSOUND
234         if (name == "directsound") {
235           TRY_DEVICE(DSAudioDevice);
236           return 0;
237         }
238       #endif
239 
240       #ifdef HAVE_WINMM
241         if (name == "winmm") {
242           TRY_DEVICE(MMAudioDevice);
243           return 0;
244         }
245       #endif
246 
247       #ifdef HAVE_AL
248         if (name == "al") {
249           TRY_DEVICE(ALAudioDevice);
250           return 0;
251         }
252       #endif
253 
254       if (name == "null") {
255         TRY_DEVICE(NullAudioDevice);
256         return 0;
257       }
258 
259     #endif
260 
261     // no devices
262     return 0;
263   }
264 
265 
266   class ThreadedDevice : public RefImplementation<AudioDevice> {
267   public:
ThreadedDevice(AudioDevice * device)268     ThreadedDevice(AudioDevice* device) {
269       ADR_GUARD("ThreadedDevice::ThreadedDevice");
270       if (device) {
271         ADR_LOG("Device is valid");
272       } else {
273         ADR_LOG("Device is not valid");
274       }
275 
276       m_device = device;
277       m_thread_exists = false;
278       m_thread_should_die = false;
279 
280       /// @todo  what if thread creation fails?
281       bool result = AI_CreateThread(threadRoutine, this, 2);
282       if (!result) {
283         ADR_LOG("THREAD CREATION FAILED");
284       }
285     }
286 
~ThreadedDevice()287     ~ThreadedDevice() {
288       m_thread_should_die = true;
289       while (m_thread_exists) {
290         AI_Sleep(50);
291       }
292     }
293 
294     // don't need to update the device...  the thread does it for us
update()295     void ADR_CALL update() {
296     }
297 
openStream(SampleSource * source)298     OutputStream* ADR_CALL openStream(SampleSource* source) {
299       return m_device->openStream(source);
300     }
301 
openBuffer(void * samples,int frame_count,int channel_count,int sample_rate,SampleFormat sample_format)302     OutputStream* ADR_CALL openBuffer(
303       void* samples, int frame_count,
304       int channel_count, int sample_rate, SampleFormat sample_format)
305     {
306       return m_device->openBuffer(
307         samples, frame_count,
308         channel_count, sample_rate, sample_format);
309     }
310 
getName()311     const char* ADR_CALL getName() {
312       return m_device->getName();
313     }
314 
registerCallback(Callback * callback)315     void ADR_CALL registerCallback(Callback* callback) {
316       m_device->registerCallback(callback);
317     }
318 
unregisterCallback(Callback * callback)319     void ADR_CALL unregisterCallback(Callback* callback) {
320       m_device->unregisterCallback(callback);
321     }
322 
clearCallbacks()323     void ADR_CALL clearCallbacks() {
324       m_device->clearCallbacks();
325     }
326 
327   private:
run()328     void run() {
329       ADR_GUARD("ThreadedDevice::run");
330       m_thread_exists = true;
331       while (!m_thread_should_die) {
332         m_device->update();
333       }
334       m_thread_exists = false;
335     }
336 
threadRoutine(void * arg)337     static void threadRoutine(void* arg) {
338       ADR_GUARD("ThreadedDevice::threadRoutine");
339       if (arg) {
340         ADR_LOG("arg is valid");
341       } else {
342         ADR_LOG("arg is not valid");
343       }
344 
345       ThreadedDevice* This = (ThreadedDevice*)arg;
346       This->run();
347     }
348 
349   private:
350     RefPtr<AudioDevice> m_device;
351     volatile bool m_thread_should_die;
352     volatile bool m_thread_exists;
353   };
354 
355 
AdrOpenDevice(const char * name,const char * parameters)356   ADR_EXPORT(AudioDevice*) AdrOpenDevice(
357     const char* name,
358     const char* parameters)
359   {
360     ADR_GUARD("AdrOpenDevice");
361 
362     if (!name) {
363       name = "";
364     }
365     if (!parameters) {
366       parameters = "";
367     }
368 
369     // first, we need an unthreaded audio device
370     AudioDevice* device = DoOpenDevice(
371       std::string(name),
372       ParameterList(parameters));
373     if (!device) {
374       ADR_LOG("Could not open device");
375       return 0;
376     }
377 
378     ADR_LOG("creating threaded device");
379     return new ThreadedDevice(device);
380   }
381 
382 }
383