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