widget/windows/nsSound.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:aca661559409
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nscore.h"
8 #include "plstr.h"
9 #include <stdio.h>
10 #include "nsString.h"
11 #include <windows.h>
12
13 // mmsystem.h is needed to build with WIN32_LEAN_AND_MEAN
14 #include <mmsystem.h>
15
16 #include "nsSound.h"
17 #include "nsIURL.h"
18 #include "nsNetUtil.h"
19 #include "nsCRT.h"
20
21 #include "prlog.h"
22 #include "prtime.h"
23 #include "prprf.h"
24 #include "prmem.h"
25
26 #include "nsNativeCharsetUtils.h"
27 #include "nsThreadUtils.h"
28
29 #ifdef PR_LOGGING
30 PRLogModuleInfo* gWin32SoundLog = nullptr;
31 #endif
32
33 class nsSoundPlayer: public nsRunnable {
34 public:
35 nsSoundPlayer(nsSound *aSound, const wchar_t* aSoundName) :
36 mSoundName(aSoundName), mSound(aSound)
37 {
38 Init();
39 }
40
41 nsSoundPlayer(nsSound *aSound, const nsAString& aSoundName) :
42 mSoundName(aSoundName), mSound(aSound)
43 {
44 Init();
45 }
46
47 NS_DECL_NSIRUNNABLE
48
49 protected:
50 nsString mSoundName;
51 nsSound *mSound; // Strong, but this will be released from SoundReleaser.
52 nsCOMPtr<nsIThread> mThread;
53
54 void Init()
55 {
56 NS_GetCurrentThread(getter_AddRefs(mThread));
57 NS_ASSERTION(mThread, "failed to get current thread");
58 NS_IF_ADDREF(mSound);
59 }
60
61 class SoundReleaser: public nsRunnable {
62 public:
63 SoundReleaser(nsSound* aSound) :
64 mSound(aSound)
65 {
66 }
67
68 NS_DECL_NSIRUNNABLE
69
70 protected:
71 nsSound *mSound;
72 };
73 };
74
75 NS_IMETHODIMP
76 nsSoundPlayer::Run()
77 {
78 PR_SetCurrentThreadName("Play Sound");
79
80 NS_PRECONDITION(!mSoundName.IsEmpty(), "Sound name should not be empty");
81 ::PlaySoundW(mSoundName.get(), nullptr,
82 SND_NODEFAULT | SND_ALIAS | SND_ASYNC);
83 nsCOMPtr<nsIRunnable> releaser = new SoundReleaser(mSound);
84 // Don't release nsSound from here, because here is not an owning thread of
85 // the nsSound. nsSound must be released in its owning thread.
86 mThread->Dispatch(releaser, NS_DISPATCH_NORMAL);
87 return NS_OK;
88 }
89
90 NS_IMETHODIMP
91 nsSoundPlayer::SoundReleaser::Run()
92 {
93 mSound->ShutdownOldPlayerThread();
94 NS_IF_RELEASE(mSound);
95 return NS_OK;
96 }
97
98
99 #ifndef SND_PURGE
100 // Not available on Windows CE, and according to MSDN
101 // doesn't do anything on recent windows either.
102 #define SND_PURGE 0
103 #endif
104
105 NS_IMPL_ISUPPORTS(nsSound, nsISound, nsIStreamLoaderObserver)
106
107
108 nsSound::nsSound()
109 {
110 #ifdef PR_LOGGING
111 if (!gWin32SoundLog) {
112 gWin32SoundLog = PR_NewLogModule("nsSound");
113 }
114 #endif
115
116 mLastSound = nullptr;
117 }
118
119 nsSound::~nsSound()
120 {
121 NS_ASSERTION(!mPlayerThread, "player thread is not null but should be");
122 PurgeLastSound();
123 }
124
125 void nsSound::ShutdownOldPlayerThread()
126 {
127 if (mPlayerThread) {
128 mPlayerThread->Shutdown();
129 mPlayerThread = nullptr;
130 }
131 }
132
133 void nsSound::PurgeLastSound()
134 {
135 if (mLastSound) {
136 // Halt any currently playing sound.
137 ::PlaySound(nullptr, nullptr, SND_PURGE);
138
139 // Now delete the buffer.
140 free(mLastSound);
141 mLastSound = nullptr;
142 }
143 }
144
145 NS_IMETHODIMP nsSound::Beep()
146 {
147 ::MessageBeep(0);
148
149 return NS_OK;
150 }
151
152 NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
153 nsISupports *context,
154 nsresult aStatus,
155 uint32_t dataLen,
156 const uint8_t *data)
157 {
158 // print a load error on bad status
159 if (NS_FAILED(aStatus)) {
160 #ifdef DEBUG
161 if (aLoader) {
162 nsCOMPtr<nsIRequest> request;
163 nsCOMPtr<nsIChannel> channel;
164 aLoader->GetRequest(getter_AddRefs(request));
165 if (request)
166 channel = do_QueryInterface(request);
167 if (channel) {
168 nsCOMPtr<nsIURI> uri;
169 channel->GetURI(getter_AddRefs(uri));
170 if (uri) {
171 nsAutoCString uriSpec;
172 uri->GetSpec(uriSpec);
173 PR_LOG(gWin32SoundLog, PR_LOG_ALWAYS,
174 ("Failed to load %s\n", uriSpec.get()));
175 }
176 }
177 }
178 #endif
179 return aStatus;
180 }
181
182 ShutdownOldPlayerThread();
183 PurgeLastSound();
184
185 if (data && dataLen > 0) {
186 DWORD flags = SND_MEMORY | SND_NODEFAULT;
187 // We try to make a copy so we can play it async.
188 mLastSound = (uint8_t *) malloc(dataLen);
189 if (mLastSound) {
190 memcpy(mLastSound, data, dataLen);
191 data = mLastSound;
192 flags |= SND_ASYNC;
193 }
194 ::PlaySoundW(reinterpret_cast<LPCWSTR>(data), 0, flags);
195 }
196
197 return NS_OK;
198 }
199
200 NS_IMETHODIMP nsSound::Play(nsIURL *aURL)
201 {
202 nsresult rv;
203
204 #ifdef DEBUG_SOUND
205 char *url;
206 aURL->GetSpec(&url);
207 PR_LOG(gWin32SoundLog, PR_LOG_ALWAYS,
208 ("%s\n", url));
209 #endif
210
211 nsCOMPtr<nsIStreamLoader> loader;
212 rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
213
214 return rv;
215 }
216
217
218 NS_IMETHODIMP nsSound::Init()
219 {
220 // This call halts a sound if it was still playing.
221 // We have to use the sound library for something to make sure
222 // it is initialized.
223 // If we wait until the first sound is played, there will
224 // be a time lag as the library gets loaded.
225 ::PlaySound(nullptr, nullptr, SND_PURGE);
226
227 return NS_OK;
228 }
229
230
231 NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
232 {
233 ShutdownOldPlayerThread();
234 PurgeLastSound();
235
236 if (!NS_IsMozAliasSound(aSoundAlias)) {
237 if (aSoundAlias.IsEmpty())
238 return NS_OK;
239 nsCOMPtr<nsIRunnable> player = new nsSoundPlayer(this, aSoundAlias);
240 NS_ENSURE_TRUE(player, NS_ERROR_OUT_OF_MEMORY);
241 nsresult rv = NS_NewThread(getter_AddRefs(mPlayerThread), player);
242 NS_ENSURE_SUCCESS(rv, rv);
243 return NS_OK;
244 }
245
246 NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
247
248 uint32_t eventId;
249 if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
250 eventId = EVENT_NEW_MAIL_RECEIVED;
251 else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG))
252 eventId = EVENT_CONFIRM_DIALOG_OPEN;
253 else if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG))
254 eventId = EVENT_ALERT_DIALOG_OPEN;
255 else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE))
256 eventId = EVENT_MENU_EXECUTE;
257 else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP))
258 eventId = EVENT_MENU_POPUP;
259 else
260 return NS_OK;
261
262 return PlayEventSound(eventId);
263 }
264
265 NS_IMETHODIMP nsSound::PlayEventSound(uint32_t aEventId)
266 {
267 ShutdownOldPlayerThread();
268 PurgeLastSound();
269
270 const wchar_t *sound = nullptr;
271 switch (aEventId) {
272 case EVENT_NEW_MAIL_RECEIVED:
273 sound = L"MailBeep";
274 break;
275 case EVENT_ALERT_DIALOG_OPEN:
276 sound = L"SystemExclamation";
277 break;
278 case EVENT_CONFIRM_DIALOG_OPEN:
279 sound = L"SystemQuestion";
280 break;
281 case EVENT_MENU_EXECUTE:
282 sound = L"MenuCommand";
283 break;
284 case EVENT_MENU_POPUP:
285 sound = L"MenuPopup";
286 break;
287 case EVENT_EDITOR_MAX_LEN:
288 sound = L".Default";
289 break;
290 default:
291 // Win32 plays no sounds at NS_SYSSOUND_PROMPT_DIALOG and
292 // NS_SYSSOUND_SELECT_DIALOG.
293 return NS_OK;
294 }
295 NS_ASSERTION(sound, "sound is null");
296
297 nsCOMPtr<nsIRunnable> player = new nsSoundPlayer(this, sound);
298 NS_ENSURE_TRUE(player, NS_ERROR_OUT_OF_MEMORY);
299 nsresult rv = NS_NewThread(getter_AddRefs(mPlayerThread), player);
300 NS_ENSURE_SUCCESS(rv, rv);
301 return NS_OK;
302 }

mercurial