00001 #region License
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #endregion
00027
00028
00029 using System;
00030 using System.Collections.Generic;
00031 using System.Text;
00032 using System.Diagnostics;
00033
00034 using OpenTK.Audio.OpenAL;
00035
00036 namespace OpenTK.Audio
00037 {
00042 public sealed class AudioContext : IDisposable
00043 {
00044 #region --- Fields ---
00045
00046 bool disposed;
00047 bool is_processing, is_synchronized;
00048 IntPtr device_handle;
00049 ContextHandle context_handle;
00050 bool context_exists;
00051
00052 string device_name;
00053 static object audio_context_lock = new object();
00054 static Dictionary<ContextHandle, AudioContext> available_contexts = new Dictionary<ContextHandle, AudioContext>();
00055
00056 #endregion
00057
00058 #region --- Constructors ---
00059
00060 #region static AudioContext()
00061
00066 static AudioContext()
00067 {
00068 if (AudioDeviceEnumerator.IsOpenALSupported)
00069 { }
00070 }
00071
00072 #endregion static AudioContext()
00073
00074 #region public AudioContext()
00075
00077 public AudioContext()
00078 : this(null, 0, 0, false, true, MaxAuxiliarySends.UseDriverDefault) { }
00079
00080 #endregion
00081
00082 #region public AudioContext(string device)
00083
00088 public AudioContext(string device) : this(device, 0, 0, false, true, MaxAuxiliarySends.UseDriverDefault) { }
00089
00090 #endregion
00091
00092 #region public AudioContext(string device, int freq)
00093
00101 public AudioContext(string device, int freq) : this(device, freq, 0, false, true, MaxAuxiliarySends.UseDriverDefault) { }
00102
00103 #endregion
00104
00105 #region public AudioContext(string device, int freq, int refresh)
00106
00115 public AudioContext(string device, int freq, int refresh)
00116 : this(device, freq, refresh, false, true, MaxAuxiliarySends.UseDriverDefault) { }
00117
00118 #endregion
00119
00120 #region public AudioContext(string device, int freq, int refresh, bool sync)
00121
00131 public AudioContext(string device, int freq, int refresh, bool sync)
00132 : this(AudioDeviceEnumerator.AvailablePlaybackDevices[0], freq, refresh, sync, true) { }
00133
00134 #endregion
00135
00136 #region public AudioContext(string device, int freq, int refresh, bool sync, bool enableEfx)
00137
00163 public AudioContext(string device, int freq, int refresh, bool sync, bool enableEfx)
00164 {
00165 CreateContext(device, freq, refresh, sync, enableEfx, MaxAuxiliarySends.UseDriverDefault);
00166 }
00167
00168 #endregion
00169
00170 #region public AudioContext(string device, int freq, int refresh, bool sync, bool enableEfx, MaxAuxiliarySends efxMaxAuxSends)
00171
00198 public AudioContext(string device, int freq, int refresh, bool sync, bool enableEfx, MaxAuxiliarySends efxMaxAuxSends)
00199 {
00200 CreateContext(device, freq, refresh, sync, enableEfx, efxMaxAuxSends);
00201 }
00202
00203 #endregion
00204
00205 #endregion --- Constructors ---
00206
00207 #region --- Private Methods ---
00208
00209 #region CreateContext
00210
00212 public enum MaxAuxiliarySends:int
00213 {
00215 UseDriverDefault = 0,
00217 One = 1,
00219 Two = 2,
00221 Three = 3,
00223 Four = 4,
00224 }
00225
00252 void CreateContext(string device, int freq, int refresh, bool sync, bool enableEfx, MaxAuxiliarySends efxAuxiliarySends)
00253 {
00254 if (!AudioDeviceEnumerator.IsOpenALSupported)
00255 throw new DllNotFoundException("openal32.dll");
00256
00257 if (AudioDeviceEnumerator.Version == AudioDeviceEnumerator.AlcVersion.Alc1_1 && AudioDeviceEnumerator.AvailablePlaybackDevices.Count == 0)
00258 throw new NotSupportedException("No audio hardware is available.");
00259 if (context_exists) throw new NotSupportedException("Multiple AudioContexts are not supported.");
00260 if (freq < 0) throw new ArgumentOutOfRangeException("freq", freq, "Should be greater than zero.");
00261 if (refresh < 0) throw new ArgumentOutOfRangeException("refresh", refresh, "Should be greater than zero.");
00262
00263
00264 if (!String.IsNullOrEmpty(device))
00265 {
00266 device_name = device;
00267 device_handle = Alc.OpenDevice(device);
00268 }
00269 if (device_handle == IntPtr.Zero)
00270 {
00271 device_name = "IntPtr.Zero (null string)";
00272 device_handle = Alc.OpenDevice(null);
00273 }
00274 if (device_handle == IntPtr.Zero)
00275 {
00276 device_name = AudioContext.DefaultDevice;
00277 device_handle = Alc.OpenDevice(AudioContext.DefaultDevice);
00278 }
00279 if (device_handle == IntPtr.Zero)
00280 {
00281 device_name = "None";
00282 throw new AudioDeviceException(String.Format("Audio device '{0}' does not exist or is tied up by another application.",
00283 String.IsNullOrEmpty(device) ? "default" : device));
00284 }
00285
00286 CheckErrors();
00287
00288
00289 List<int> attributes = new List<int>();
00290
00291 if (freq != 0)
00292 {
00293 attributes.Add((int)AlcContextAttributes.Frequency);
00294 attributes.Add(freq);
00295 }
00296
00297 if (refresh != 0)
00298 {
00299 attributes.Add((int)AlcContextAttributes.Refresh);
00300 attributes.Add(refresh);
00301 }
00302
00303 attributes.Add((int)AlcContextAttributes.Sync);
00304 attributes.Add(sync ? 1 : 0);
00305
00306 if (enableEfx && Alc.IsExtensionPresent(device_handle, "ALC_EXT_EFX"))
00307 {
00308 int num_slots;
00309 switch (efxAuxiliarySends)
00310 {
00311 case MaxAuxiliarySends.One:
00312 case MaxAuxiliarySends.Two:
00313 case MaxAuxiliarySends.Three:
00314 case MaxAuxiliarySends.Four:
00315 num_slots = (int)efxAuxiliarySends;
00316 break;
00317 default:
00318 case MaxAuxiliarySends.UseDriverDefault:
00319 Alc.GetInteger(device_handle, AlcGetInteger.EfxMaxAuxiliarySends, 1, out num_slots);
00320 break;
00321 }
00322
00323 attributes.Add((int)AlcContextAttributes.EfxMaxAuxiliarySends);
00324 attributes.Add(num_slots);
00325 }
00326 attributes.Add(0);
00327
00328 context_handle = Alc.CreateContext(device_handle, attributes.ToArray());
00329
00330 if (context_handle == ContextHandle.Zero)
00331 {
00332 Alc.CloseDevice(device_handle);
00333 throw new AudioContextException("The audio context could not be created with the specified parameters.");
00334 }
00335
00336 CheckErrors();
00337
00338
00339
00340
00341 if (AudioDeviceEnumerator.AvailablePlaybackDevices.Count > 0)
00342 MakeCurrent();
00343
00344 CheckErrors();
00345
00346 device_name = Alc.GetString(device_handle, AlcGetString.DeviceSpecifier);
00347
00348
00349 lock (audio_context_lock)
00350 {
00351 available_contexts.Add(this.context_handle, this);
00352 context_exists = true;
00353 }
00354 }
00355
00356 #endregion --- Private Methods ---
00357
00358 #region static void MakeCurrent(AudioContext context)
00359
00369 static void MakeCurrent(AudioContext context)
00370 {
00371 lock (audio_context_lock)
00372 {
00373 if (!Alc.MakeContextCurrent(context != null ? context.context_handle : ContextHandle.Zero))
00374 throw new AudioContextException(String.Format("ALC {0} error detected at {1}.",
00375 Alc.GetError(context != null ? (IntPtr)context.context_handle : IntPtr.Zero).ToString(),
00376 context != null ? context.ToString() : "null"));
00377 }
00378 }
00379
00380 #endregion
00381
00382 #region internal bool IsCurrent
00383
00392 internal bool IsCurrent
00393 {
00394 get
00395 {
00396 lock (audio_context_lock)
00397 {
00398 if (available_contexts.Count == 0)
00399 return false;
00400 else
00401 {
00402 return AudioContext.CurrentContext == this;
00403 }
00404 }
00405 }
00406 set
00407 {
00408 if (value) AudioContext.MakeCurrent(this);
00409 else AudioContext.MakeCurrent(null);
00410 }
00411 }
00412
00413 #endregion
00414
00415 #region IntPtr Device
00416
00417 IntPtr Device { get { return device_handle; } }
00418
00419 #endregion
00420
00421 #endregion
00422
00423 #region --- Public Members ---
00424
00425 #region CheckErrors
00426
00434 public void CheckErrors()
00435 {
00436 if (disposed)
00437 throw new ObjectDisposedException(this.GetType().FullName);
00438
00439 new AudioDeviceErrorChecker(device_handle).Dispose();
00440 }
00441
00442 #endregion
00443
00444 #region CurrentError
00445
00449 public AlcError CurrentError
00450 {
00451 get
00452 {
00453 if (disposed)
00454 throw new ObjectDisposedException(this.GetType().FullName);
00455
00456 return Alc.GetError(device_handle);
00457 }
00458 }
00459
00460 #endregion
00461
00462 #region MakeCurrent
00463
00475 public void MakeCurrent()
00476 {
00477 if (disposed)
00478 throw new ObjectDisposedException(this.GetType().FullName);
00479
00480 AudioContext.MakeCurrent(this);
00481 }
00482
00483 #endregion
00484
00485 #region IsProcessing
00486
00493 public bool IsProcessing
00494 {
00495 get
00496 {
00497 if (disposed)
00498 throw new ObjectDisposedException(this.GetType().FullName);
00499
00500 return is_processing;
00501 }
00502 private set { is_processing = value; }
00503 }
00504
00505 #endregion
00506
00507 #region IsSynchronized
00508
00514 public bool IsSynchronized
00515 {
00516 get
00517 {
00518 if (disposed)
00519 throw new ObjectDisposedException(this.GetType().FullName);
00520
00521 return is_synchronized;
00522 }
00523 private set { is_synchronized = value; }
00524 }
00525
00526 #endregion
00527
00528 #region public void Process
00529
00548 public void Process()
00549 {
00550 if (disposed)
00551 throw new ObjectDisposedException(this.GetType().FullName);
00552
00553 Alc.ProcessContext(this.context_handle);
00554 IsProcessing = true;
00555 }
00556
00557 #endregion
00558
00559 #region public void Suspend
00560
00581 public void Suspend()
00582 {
00583 if (disposed)
00584 throw new ObjectDisposedException(this.GetType().FullName);
00585
00586 Alc.SuspendContext(this.context_handle);
00587 IsProcessing = false;
00588 }
00589
00590 #endregion
00591
00592 #region public bool SupportsExtension(string extension)
00593
00599 public bool SupportsExtension(string extension)
00600 {
00601 if (disposed)
00602 throw new ObjectDisposedException(this.GetType().FullName);
00603
00604 return Alc.IsExtensionPresent(this.Device, extension);
00605 }
00606
00607 #endregion
00608
00609 #region CurrentDevice
00610
00614 public string CurrentDevice
00615 {
00616 get
00617 {
00618 if (disposed)
00619 throw new ObjectDisposedException(this.GetType().FullName);
00620
00621 return device_name;
00622 }
00623 }
00624
00625 #endregion
00626
00627 #endregion --- Public Members ---
00628
00629 #region --- Static Members ---
00630
00631 #region public static AudioContext CurrentContext
00632
00640 public static AudioContext CurrentContext
00641 {
00642 get
00643 {
00644 lock (audio_context_lock)
00645 {
00646 if (available_contexts.Count == 0)
00647 return null;
00648 else
00649 {
00650 AudioContext context;
00651 AudioContext.available_contexts.TryGetValue(
00652 (ContextHandle)Alc.GetCurrentContext(),
00653 out context);
00654 return context;
00655 }
00656 }
00657 }
00658 }
00659
00660 #endregion
00661
00662 #region AvailableDevices
00663
00667 public static IList<string> AvailableDevices
00668 {
00669 get
00670 {
00671 return AudioDeviceEnumerator.AvailablePlaybackDevices;
00672 }
00673 }
00674 #endregion public static IList<string> AvailablePlaybackDevices
00675
00676 #region DefaultDevice
00677
00681 public static string DefaultDevice
00682 {
00683 get
00684 {
00685 return AudioDeviceEnumerator.DefaultPlaybackDevice;
00686 }
00687 }
00688
00689 #endregion
00690
00691 #endregion
00692
00693 #region --- IDisposable Members ---
00694
00698 public void Dispose()
00699 {
00700 this.Dispose(true);
00701 GC.SuppressFinalize(this);
00702 }
00703
00704 void Dispose(bool manual)
00705 {
00706 if (!disposed)
00707 {
00708 if (this.IsCurrent)
00709 this.IsCurrent = false;
00710
00711 if (context_handle != ContextHandle.Zero)
00712 {
00713 available_contexts.Remove(context_handle);
00714 Alc.DestroyContext(context_handle);
00715 }
00716
00717 if (device_handle != IntPtr.Zero)
00718 Alc.CloseDevice(device_handle);
00719
00720 if (manual)
00721 {
00722 }
00723 disposed = true;
00724 }
00725 }
00726
00730 ~AudioContext()
00731 {
00732 this.Dispose(false);
00733 }
00734
00735 #endregion
00736
00737 #region --- Overrides ---
00738
00743 public override int GetHashCode()
00744 {
00745 return base.GetHashCode();
00746 }
00747
00753 public override bool Equals(object obj)
00754 {
00755 return base.Equals(obj);
00756 }
00757
00762 public override string ToString()
00763 {
00764 return String.Format("{0} (handle: {1}, device: {2})",
00765 this.device_name, this.context_handle, this.device_handle);
00766 }
00767
00768 #endregion
00769 }
00770 }