1: using System;
2: using System.Collections.Generic;
3: using System.IO;
4: using System.Net;
5: using System.Windows;
6: using System.Windows.Controls;
7: using System.Windows.Media;
8:
9: namespace Wintellect.SilverlightToolbox
10: {
11: public static class SoundEffects
12: {
13: static Panel Root;
14: static MediaElement BackgroundLoop = new MediaElement();
15: static WebClient EffectDownloader = new WebClient();
16:
17: static Queue<MediaElement> AvailableSoundEffectGenerators = new Queue<MediaElement>();
18: static Dictionary<string, Stream> DownloadedEffects = new Dictionary<string, Stream>();
19: static Queue<string> PendingDownloads = new Queue<string>();
20: static Queue<QueuedEffect> PendingEffects = new Queue<QueuedEffect>();
21: static Dictionary<MediaElement, Action> PendingStartupCallbacks = new Dictionary<MediaElement, Action>();
22: static Dictionary<MediaElement, Action> PendingCompletionCallbacks = new Dictionary<MediaElement, Action>();
23:
24: enum TargetType
25: {
26: BackgroundMusic,
27: SoundEffect,
28: }
29:
30: class QueuedEffect
31: {
32: public string MediaName { get; set; }
33: public TargetType Target { get; set; }
34: public Action StartedCallback { get; set; }
35: public Action CompletedCallback { get; set; }
36: }
37:
38: public static void Initialize(Panel root)
39: {
40: Root = root;
41: InitializeTarget(root, BackgroundLoop);
42: EffectDownloader.OpenReadCompleted += (s, e) =>
43: {
44: DownloadedEffects[(string)e.UserState] = e.Result;
45: DownloadEffects();
46: PlayEffect();
47: };
48: }
49:
50: static void DownloadEffects()
51: {
52: if (PendingDownloads.Count == 0)
53: return;
54: string MediaName = PendingDownloads.Dequeue();
55: EffectDownloader.OpenReadAsync(new Uri(MediaName, UriKind.Relative), MediaName);
56: }
57:
58: static void InitializeTarget(Panel root, MediaElement target)
59: {
60: target.Width = 0;
61: target.Height = 0;
62: target.Visibility = Visibility.Collapsed;
63: root.Children.Add(target);
64: target.AutoPlay = false;
65: target.MediaOpened += (s, e) =>
66: {
67: MediaElement Target = s as MediaElement;
68: Target.Volume = 0.35;
69: Target.Play();
70: if (PendingStartupCallbacks.ContainsKey(Target))
71: {
72: Target.Dispatcher.BeginInvoke(PendingStartupCallbacks[Target]);
73: PendingStartupCallbacks.Remove(Target);
74: }
75: };
76: target.MediaEnded += (s, e) =>
77: {
78: MediaElement Target = s as MediaElement;
79: Target.Stop();
80: if (s == BackgroundLoop)
81: Target.Play();
82: else
83: AvailableSoundEffectGenerators.Enqueue(Target);
84:
85: if (PendingCompletionCallbacks.ContainsKey(Target))
86: {
87: Target.Dispatcher.BeginInvoke(PendingCompletionCallbacks[Target]);
88: PendingCompletionCallbacks.Remove(Target);
89: }
90: };
91: }
92:
93: static MediaElement GetUnusedEffectGenerator()
94: {
95: if (AvailableSoundEffectGenerators.Count > 0)
96: return AvailableSoundEffectGenerators.Dequeue();
97: else
98: {
99: MediaElement Result = new MediaElement();
100: InitializeTarget(Root, Result);
101: return Result;
102: }
103: }
104:
105: static void PlayEffect()
106: {
107: lock (PendingEffects)
108: {
109: if (PendingEffects.Count == 0)
110: return;
111: QueuedEffect Effect = PendingEffects.Dequeue();
112: if (DownloadedEffects.ContainsKey(Effect.MediaName))
113: {
114: MediaElement TargetElement = null;
115: switch (Effect.Target)
116: {
117: case TargetType.BackgroundMusic:
118: { TargetElement = BackgroundLoop; break; }
119: case TargetType.SoundEffect:
120: { TargetElement = GetUnusedEffectGenerator(); break; }
121: }
122: if (Effect.StartedCallback != null)
123: PendingStartupCallbacks.Add(TargetElement, Effect.StartedCallback);
124: if (Effect.CompletedCallback != null)
125: PendingCompletionCallbacks.Add(TargetElement, Effect.CompletedCallback);
126: TargetElement.SetSource(DownloadedEffects[Effect.MediaName]);
127: }
128: else
129: {
130: PendingEffects.Enqueue(Effect);
131: }
132: }
133: }
134:
135: static void PlaySound(TargetType target, string mediaName, Action startedCallback, Action completedCallback)
136: {
137: if (target == TargetType.BackgroundMusic)
138: if (BackgroundLoop.CurrentState != MediaElementState.Stopped)
139: BackgroundLoop.Stop();
140:
141: if (mediaName != String.Empty)
142: {
143: lock (PendingEffects)
144: {
145: if (!DownloadedEffects.ContainsKey(mediaName))
146: {
147: PendingDownloads.Enqueue(mediaName);
148: }
149: PendingEffects.Enqueue(new QueuedEffect
150: {
151: MediaName = mediaName,
152: Target = target,
153: StartedCallback = startedCallback,
154: CompletedCallback = completedCallback
155: });
156: }
157: DownloadEffects();
158: PlayEffect();
159: }
160: }
161:
162: public static void SetBackgroundLoop(string mediaName)
163: {
164: PlaySound(TargetType.BackgroundMusic, mediaName, null, null);
165: }
166:
167: public static void PlaySoundEffect(string effectName)
168: {
169: PlaySound(TargetType.SoundEffect, effectName, null, null);
170: }
171:
172: public static void PlaySoundEffect(string effectName, Action startedCallback, Action completedCallback)
173: {
174: PlaySound(TargetType.SoundEffect, effectName, startedCallback, completedCallback);
175: }
176:
177: public static void PreloadMedia(string mediaName)
178: {
179: if (!DownloadedEffects.ContainsKey(mediaName))
180: {
181: PendingDownloads.Enqueue(mediaName);
182: DownloadEffects();
183: }
184: }
185: }
186: }