widget/windows/nsSound.cpp

changeset 0
6474c204b198
     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 +}

mercurial