1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/nsSound.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,302 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nscore.h" 1.11 +#include "plstr.h" 1.12 +#include <stdio.h> 1.13 +#include "nsString.h" 1.14 +#include <windows.h> 1.15 + 1.16 +// mmsystem.h is needed to build with WIN32_LEAN_AND_MEAN 1.17 +#include <mmsystem.h> 1.18 + 1.19 +#include "nsSound.h" 1.20 +#include "nsIURL.h" 1.21 +#include "nsNetUtil.h" 1.22 +#include "nsCRT.h" 1.23 + 1.24 +#include "prlog.h" 1.25 +#include "prtime.h" 1.26 +#include "prprf.h" 1.27 +#include "prmem.h" 1.28 + 1.29 +#include "nsNativeCharsetUtils.h" 1.30 +#include "nsThreadUtils.h" 1.31 + 1.32 +#ifdef PR_LOGGING 1.33 +PRLogModuleInfo* gWin32SoundLog = nullptr; 1.34 +#endif 1.35 + 1.36 +class nsSoundPlayer: public nsRunnable { 1.37 +public: 1.38 + nsSoundPlayer(nsSound *aSound, const wchar_t* aSoundName) : 1.39 + mSoundName(aSoundName), mSound(aSound) 1.40 + { 1.41 + Init(); 1.42 + } 1.43 + 1.44 + nsSoundPlayer(nsSound *aSound, const nsAString& aSoundName) : 1.45 + mSoundName(aSoundName), mSound(aSound) 1.46 + { 1.47 + Init(); 1.48 + } 1.49 + 1.50 + NS_DECL_NSIRUNNABLE 1.51 + 1.52 +protected: 1.53 + nsString mSoundName; 1.54 + nsSound *mSound; // Strong, but this will be released from SoundReleaser. 1.55 + nsCOMPtr<nsIThread> mThread; 1.56 + 1.57 + void Init() 1.58 + { 1.59 + NS_GetCurrentThread(getter_AddRefs(mThread)); 1.60 + NS_ASSERTION(mThread, "failed to get current thread"); 1.61 + NS_IF_ADDREF(mSound); 1.62 + } 1.63 + 1.64 + class SoundReleaser: public nsRunnable { 1.65 + public: 1.66 + SoundReleaser(nsSound* aSound) : 1.67 + mSound(aSound) 1.68 + { 1.69 + } 1.70 + 1.71 + NS_DECL_NSIRUNNABLE 1.72 + 1.73 + protected: 1.74 + nsSound *mSound; 1.75 + }; 1.76 +}; 1.77 + 1.78 +NS_IMETHODIMP 1.79 +nsSoundPlayer::Run() 1.80 +{ 1.81 + PR_SetCurrentThreadName("Play Sound"); 1.82 + 1.83 + NS_PRECONDITION(!mSoundName.IsEmpty(), "Sound name should not be empty"); 1.84 + ::PlaySoundW(mSoundName.get(), nullptr, 1.85 + SND_NODEFAULT | SND_ALIAS | SND_ASYNC); 1.86 + nsCOMPtr<nsIRunnable> releaser = new SoundReleaser(mSound); 1.87 + // Don't release nsSound from here, because here is not an owning thread of 1.88 + // the nsSound. nsSound must be released in its owning thread. 1.89 + mThread->Dispatch(releaser, NS_DISPATCH_NORMAL); 1.90 + return NS_OK; 1.91 +} 1.92 + 1.93 +NS_IMETHODIMP 1.94 +nsSoundPlayer::SoundReleaser::Run() 1.95 +{ 1.96 + mSound->ShutdownOldPlayerThread(); 1.97 + NS_IF_RELEASE(mSound); 1.98 + return NS_OK; 1.99 +} 1.100 + 1.101 + 1.102 +#ifndef SND_PURGE 1.103 +// Not available on Windows CE, and according to MSDN 1.104 +// doesn't do anything on recent windows either. 1.105 +#define SND_PURGE 0 1.106 +#endif 1.107 + 1.108 +NS_IMPL_ISUPPORTS(nsSound, nsISound, nsIStreamLoaderObserver) 1.109 + 1.110 + 1.111 +nsSound::nsSound() 1.112 +{ 1.113 +#ifdef PR_LOGGING 1.114 + if (!gWin32SoundLog) { 1.115 + gWin32SoundLog = PR_NewLogModule("nsSound"); 1.116 + } 1.117 +#endif 1.118 + 1.119 + mLastSound = nullptr; 1.120 +} 1.121 + 1.122 +nsSound::~nsSound() 1.123 +{ 1.124 + NS_ASSERTION(!mPlayerThread, "player thread is not null but should be"); 1.125 + PurgeLastSound(); 1.126 +} 1.127 + 1.128 +void nsSound::ShutdownOldPlayerThread() 1.129 +{ 1.130 + if (mPlayerThread) { 1.131 + mPlayerThread->Shutdown(); 1.132 + mPlayerThread = nullptr; 1.133 + } 1.134 +} 1.135 + 1.136 +void nsSound::PurgeLastSound() 1.137 +{ 1.138 + if (mLastSound) { 1.139 + // Halt any currently playing sound. 1.140 + ::PlaySound(nullptr, nullptr, SND_PURGE); 1.141 + 1.142 + // Now delete the buffer. 1.143 + free(mLastSound); 1.144 + mLastSound = nullptr; 1.145 + } 1.146 +} 1.147 + 1.148 +NS_IMETHODIMP nsSound::Beep() 1.149 +{ 1.150 + ::MessageBeep(0); 1.151 + 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader, 1.156 + nsISupports *context, 1.157 + nsresult aStatus, 1.158 + uint32_t dataLen, 1.159 + const uint8_t *data) 1.160 +{ 1.161 + // print a load error on bad status 1.162 + if (NS_FAILED(aStatus)) { 1.163 +#ifdef DEBUG 1.164 + if (aLoader) { 1.165 + nsCOMPtr<nsIRequest> request; 1.166 + nsCOMPtr<nsIChannel> channel; 1.167 + aLoader->GetRequest(getter_AddRefs(request)); 1.168 + if (request) 1.169 + channel = do_QueryInterface(request); 1.170 + if (channel) { 1.171 + nsCOMPtr<nsIURI> uri; 1.172 + channel->GetURI(getter_AddRefs(uri)); 1.173 + if (uri) { 1.174 + nsAutoCString uriSpec; 1.175 + uri->GetSpec(uriSpec); 1.176 + PR_LOG(gWin32SoundLog, PR_LOG_ALWAYS, 1.177 + ("Failed to load %s\n", uriSpec.get())); 1.178 + } 1.179 + } 1.180 + } 1.181 +#endif 1.182 + return aStatus; 1.183 + } 1.184 + 1.185 + ShutdownOldPlayerThread(); 1.186 + PurgeLastSound(); 1.187 + 1.188 + if (data && dataLen > 0) { 1.189 + DWORD flags = SND_MEMORY | SND_NODEFAULT; 1.190 + // We try to make a copy so we can play it async. 1.191 + mLastSound = (uint8_t *) malloc(dataLen); 1.192 + if (mLastSound) { 1.193 + memcpy(mLastSound, data, dataLen); 1.194 + data = mLastSound; 1.195 + flags |= SND_ASYNC; 1.196 + } 1.197 + ::PlaySoundW(reinterpret_cast<LPCWSTR>(data), 0, flags); 1.198 + } 1.199 + 1.200 + return NS_OK; 1.201 +} 1.202 + 1.203 +NS_IMETHODIMP nsSound::Play(nsIURL *aURL) 1.204 +{ 1.205 + nsresult rv; 1.206 + 1.207 +#ifdef DEBUG_SOUND 1.208 + char *url; 1.209 + aURL->GetSpec(&url); 1.210 + PR_LOG(gWin32SoundLog, PR_LOG_ALWAYS, 1.211 + ("%s\n", url)); 1.212 +#endif 1.213 + 1.214 + nsCOMPtr<nsIStreamLoader> loader; 1.215 + rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this); 1.216 + 1.217 + return rv; 1.218 +} 1.219 + 1.220 + 1.221 +NS_IMETHODIMP nsSound::Init() 1.222 +{ 1.223 + // This call halts a sound if it was still playing. 1.224 + // We have to use the sound library for something to make sure 1.225 + // it is initialized. 1.226 + // If we wait until the first sound is played, there will 1.227 + // be a time lag as the library gets loaded. 1.228 + ::PlaySound(nullptr, nullptr, SND_PURGE); 1.229 + 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 + 1.234 +NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias) 1.235 +{ 1.236 + ShutdownOldPlayerThread(); 1.237 + PurgeLastSound(); 1.238 + 1.239 + if (!NS_IsMozAliasSound(aSoundAlias)) { 1.240 + if (aSoundAlias.IsEmpty()) 1.241 + return NS_OK; 1.242 + nsCOMPtr<nsIRunnable> player = new nsSoundPlayer(this, aSoundAlias); 1.243 + NS_ENSURE_TRUE(player, NS_ERROR_OUT_OF_MEMORY); 1.244 + nsresult rv = NS_NewThread(getter_AddRefs(mPlayerThread), player); 1.245 + NS_ENSURE_SUCCESS(rv, rv); 1.246 + return NS_OK; 1.247 + } 1.248 + 1.249 + NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead"); 1.250 + 1.251 + uint32_t eventId; 1.252 + if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP)) 1.253 + eventId = EVENT_NEW_MAIL_RECEIVED; 1.254 + else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG)) 1.255 + eventId = EVENT_CONFIRM_DIALOG_OPEN; 1.256 + else if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG)) 1.257 + eventId = EVENT_ALERT_DIALOG_OPEN; 1.258 + else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE)) 1.259 + eventId = EVENT_MENU_EXECUTE; 1.260 + else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP)) 1.261 + eventId = EVENT_MENU_POPUP; 1.262 + else 1.263 + return NS_OK; 1.264 + 1.265 + return PlayEventSound(eventId); 1.266 +} 1.267 + 1.268 +NS_IMETHODIMP nsSound::PlayEventSound(uint32_t aEventId) 1.269 +{ 1.270 + ShutdownOldPlayerThread(); 1.271 + PurgeLastSound(); 1.272 + 1.273 + const wchar_t *sound = nullptr; 1.274 + switch (aEventId) { 1.275 + case EVENT_NEW_MAIL_RECEIVED: 1.276 + sound = L"MailBeep"; 1.277 + break; 1.278 + case EVENT_ALERT_DIALOG_OPEN: 1.279 + sound = L"SystemExclamation"; 1.280 + break; 1.281 + case EVENT_CONFIRM_DIALOG_OPEN: 1.282 + sound = L"SystemQuestion"; 1.283 + break; 1.284 + case EVENT_MENU_EXECUTE: 1.285 + sound = L"MenuCommand"; 1.286 + break; 1.287 + case EVENT_MENU_POPUP: 1.288 + sound = L"MenuPopup"; 1.289 + break; 1.290 + case EVENT_EDITOR_MAX_LEN: 1.291 + sound = L".Default"; 1.292 + break; 1.293 + default: 1.294 + // Win32 plays no sounds at NS_SYSSOUND_PROMPT_DIALOG and 1.295 + // NS_SYSSOUND_SELECT_DIALOG. 1.296 + return NS_OK; 1.297 + } 1.298 + NS_ASSERTION(sound, "sound is null"); 1.299 + 1.300 + nsCOMPtr<nsIRunnable> player = new nsSoundPlayer(this, sound); 1.301 + NS_ENSURE_TRUE(player, NS_ERROR_OUT_OF_MEMORY); 1.302 + nsresult rv = NS_NewThread(getter_AddRefs(mPlayerThread), player); 1.303 + NS_ENSURE_SUCCESS(rv, rv); 1.304 + return NS_OK; 1.305 +}