LCOV - code coverage report
Current view: directory - content/media - nsAudioStream.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 161 19 11.8 %
Date: 2012-06-02 Functions: 30 3 10.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * the Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Chris Double <chris.double@double.co.nz>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/dom/ContentChild.h"
      41                 : #include "mozilla/dom/PAudioChild.h"
      42                 : #include "mozilla/dom/AudioChild.h"
      43                 : #include "nsXULAppAPI.h"
      44                 : using namespace mozilla::dom;
      45                 : 
      46                 : #include <stdio.h>
      47                 : #include <math.h>
      48                 : #include "prlog.h"
      49                 : #include "prmem.h"
      50                 : #include "prdtoa.h"
      51                 : #include "nsAutoPtr.h"
      52                 : #include "nsAudioStream.h"
      53                 : #include "nsAlgorithm.h"
      54                 : #include "VideoUtils.h"
      55                 : #include "mozilla/Mutex.h"
      56                 : extern "C" {
      57                 : #include "sydneyaudio/sydney_audio.h"
      58                 : }
      59                 : #include "mozilla/TimeStamp.h"
      60                 : #include "nsThreadUtils.h"
      61                 : #include "mozilla/Preferences.h"
      62                 : 
      63                 : #if defined(MOZ_CUBEB)
      64                 : #include "nsAutoRef.h"
      65                 : #include "cubeb/cubeb.h"
      66                 : #endif
      67                 : 
      68                 : using namespace mozilla;
      69                 : 
      70                 : #if defined(XP_MACOSX)
      71                 : #define SA_PER_STREAM_VOLUME 1
      72                 : #endif
      73                 : 
      74                 : // Android's audio backend is not available in content processes, so audio must
      75                 : // be remoted to the parent chrome process.
      76                 : #if defined(ANDROID)
      77                 : #define REMOTE_AUDIO 1
      78                 : #endif
      79                 : 
      80                 : using mozilla::TimeStamp;
      81                 : 
      82                 : #ifdef PR_LOGGING
      83                 : PRLogModuleInfo* gAudioStreamLog = nsnull;
      84                 : #endif
      85                 : 
      86                 : #if defined(MOZ_CUBEB)
      87                 : static cubeb* gCubebContext;
      88                 : #endif
      89                 : 
      90                 : static const PRUint32 FAKE_BUFFER_SIZE = 176400;
      91                 : 
      92                 : // Number of milliseconds per second.
      93                 : static const PRInt64 MS_PER_S = 1000;
      94                 : 
      95                 : class nsNativeAudioStream : public nsAudioStream
      96                 : {
      97                 :  public:
      98                 :   NS_DECL_ISUPPORTS
      99                 : 
     100                 :   ~nsNativeAudioStream();
     101                 :   nsNativeAudioStream();
     102                 : 
     103                 :   nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
     104                 :   void Shutdown();
     105                 :   nsresult Write(const void* aBuf, PRUint32 aFrames);
     106                 :   PRUint32 Available();
     107                 :   void SetVolume(double aVolume);
     108                 :   void Drain();
     109                 :   void Pause();
     110                 :   void Resume();
     111                 :   PRInt64 GetPosition();
     112                 :   PRInt64 GetPositionInFrames();
     113                 :   bool IsPaused();
     114                 :   PRInt32 GetMinWriteSize();
     115                 : 
     116                 :  private:
     117                 : 
     118                 :   double mVolume;
     119                 :   void* mAudioHandle;
     120                 : 
     121                 :   // True if this audio stream is paused.
     122                 :   bool mPaused;
     123                 : 
     124                 :   // True if this stream has encountered an error.
     125                 :   bool mInError;
     126                 : 
     127                 : };
     128                 : 
     129                 : #if defined(REMOTE_AUDIO)
     130                 : class nsRemotedAudioStream : public nsAudioStream
     131                 : {
     132                 :  public:
     133                 :   NS_DECL_ISUPPORTS
     134                 : 
     135                 :   nsRemotedAudioStream();
     136                 :   ~nsRemotedAudioStream();
     137                 : 
     138                 :   nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
     139                 :   void Shutdown();
     140                 :   nsresult Write(const void* aBuf, PRUint32 aFrames);
     141                 :   PRUint32 Available();
     142                 :   void SetVolume(double aVolume);
     143                 :   void Drain();
     144                 :   void Pause();
     145                 :   void Resume();
     146                 :   PRInt64 GetPosition();
     147                 :   PRInt64 GetPositionInFrames();
     148                 :   bool IsPaused();
     149                 :   PRInt32 GetMinWriteSize();
     150                 : 
     151                 : private:
     152                 :   nsRefPtr<AudioChild> mAudioChild;
     153                 : 
     154                 :   PRInt32 mBytesPerFrame;
     155                 : 
     156                 :   // True if this audio stream is paused.
     157                 :   bool mPaused;
     158                 : 
     159                 :   friend class AudioInitEvent;
     160                 : };
     161                 : 
     162                 : class AudioInitEvent : public nsRunnable
     163                 : {
     164                 :  public:
     165                 :   AudioInitEvent(nsRemotedAudioStream* owner)
     166                 :   {
     167                 :     mOwner = owner;
     168                 :   }
     169                 : 
     170                 :   NS_IMETHOD Run()
     171                 :   {
     172                 :     ContentChild * cpc = ContentChild::GetSingleton();
     173                 :     NS_ASSERTION(cpc, "Content Protocol is NULL!");
     174                 :     mOwner->mAudioChild =  static_cast<AudioChild*>(cpc->SendPAudioConstructor(mOwner->mChannels,
     175                 :                                                                                mOwner->mRate,
     176                 :                                                                                mOwner->mFormat));
     177                 :     return NS_OK;
     178                 :   }
     179                 : 
     180                 :   nsRefPtr<nsRemotedAudioStream> mOwner;
     181                 : };
     182                 : 
     183                 : class AudioWriteEvent : public nsRunnable
     184                 : {
     185                 :  public:
     186                 :   AudioWriteEvent(AudioChild* aChild,
     187                 :                   const void* aBuf,
     188                 :                   PRUint32 aNumberOfFrames,
     189                 :                   PRUint32 aBytesPerFrame)
     190                 :   {
     191                 :     mAudioChild = aChild;
     192                 :     mBytesPerFrame = aBytesPerFrame;
     193                 :     mBuffer.Assign((const char*)aBuf, aNumberOfFrames * aBytesPerFrame);
     194                 :   }
     195                 : 
     196                 :   NS_IMETHOD Run()
     197                 :   {
     198                 :     if (!mAudioChild->IsIPCOpen())
     199                 :       return NS_OK;
     200                 : 
     201                 :     mAudioChild->SendWrite(mBuffer, mBuffer.Length() / mBytesPerFrame);
     202                 :     return NS_OK;
     203                 :   }
     204                 : 
     205                 :   nsRefPtr<AudioChild> mAudioChild;
     206                 :   nsCString mBuffer;
     207                 :   PRUint32 mBytesPerFrame;
     208                 : };
     209                 : 
     210                 : class AudioSetVolumeEvent : public nsRunnable
     211                 : {
     212                 :  public:
     213                 :   AudioSetVolumeEvent(AudioChild* aChild, double aVolume)
     214                 :   {
     215                 :     mAudioChild = aChild;
     216                 :     mVolume = aVolume;
     217                 :   }
     218                 : 
     219                 :   NS_IMETHOD Run()
     220                 :   {
     221                 :     if (!mAudioChild->IsIPCOpen())
     222                 :       return NS_OK;
     223                 : 
     224                 :     mAudioChild->SendSetVolume(mVolume);
     225                 :     return NS_OK;
     226                 :   }
     227                 : 
     228                 :   nsRefPtr<AudioChild> mAudioChild;
     229                 :   double mVolume;
     230                 : };
     231                 : 
     232                 : 
     233                 : class AudioMinWriteSizeEvent : public nsRunnable
     234                 : {
     235                 :  public:
     236                 :   AudioMinWriteSizeEvent(AudioChild* aChild)
     237                 :   {
     238                 :     mAudioChild = aChild;
     239                 :   }
     240                 : 
     241                 :   NS_IMETHOD Run()
     242                 :   {
     243                 :     if (!mAudioChild->IsIPCOpen())
     244                 :       return NS_OK;
     245                 : 
     246                 :     mAudioChild->SendMinWriteSize();
     247                 :     return NS_OK;
     248                 :   }
     249                 : 
     250                 :   nsRefPtr<AudioChild> mAudioChild;
     251                 : };
     252                 : 
     253                 : class AudioDrainEvent : public nsRunnable
     254                 : {
     255                 :  public:
     256                 :   AudioDrainEvent(AudioChild* aChild)
     257                 :   {
     258                 :     mAudioChild = aChild;
     259                 :   }
     260                 : 
     261                 :   NS_IMETHOD Run()
     262                 :   {
     263                 :     if (!mAudioChild->IsIPCOpen())
     264                 :       return NS_OK;
     265                 : 
     266                 :     mAudioChild->SendDrain();
     267                 :     return NS_OK;
     268                 :   }
     269                 : 
     270                 :   nsRefPtr<AudioChild> mAudioChild;
     271                 : };
     272                 : 
     273                 : 
     274                 : class AudioPauseEvent : public nsRunnable
     275                 : {
     276                 :  public:
     277                 :   AudioPauseEvent(AudioChild* aChild, bool pause)
     278                 :   {
     279                 :     mAudioChild = aChild;
     280                 :     mPause = pause;
     281                 :   }
     282                 : 
     283                 :   NS_IMETHOD Run()
     284                 :   {
     285                 :     if (!mAudioChild->IsIPCOpen())
     286                 :       return NS_OK;
     287                 : 
     288                 :     if (mPause)
     289                 :       mAudioChild->SendPause();
     290                 :     else
     291                 :       mAudioChild->SendResume();
     292                 : 
     293                 :     return NS_OK;
     294                 :   }
     295                 : 
     296                 :   nsRefPtr<AudioChild> mAudioChild;
     297                 :   bool mPause;
     298                 : };
     299                 : 
     300                 : 
     301                 : class AudioShutdownEvent : public nsRunnable
     302                 : {
     303                 :  public:
     304                 :   AudioShutdownEvent(AudioChild* aChild)
     305                 :   {
     306                 :     mAudioChild = aChild;
     307                 :   }
     308                 : 
     309                 :   NS_IMETHOD Run()
     310                 :   {
     311                 :     if (mAudioChild->IsIPCOpen())
     312                 :       mAudioChild->SendShutdown();
     313                 :     return NS_OK;
     314                 :   }
     315                 : 
     316                 :   nsRefPtr<AudioChild> mAudioChild;
     317                 : };
     318                 : #endif
     319                 : 
     320                 : #define PREF_VOLUME_SCALE "media.volume_scale"
     321                 : #define PREF_USE_CUBEB "media.use_cubeb"
     322                 : 
     323                 : static mozilla::Mutex* gAudioPrefsLock = nsnull;
     324                 : static double gVolumeScale = 1.0;
     325                 : static bool gUseCubeb = false;
     326                 : 
     327            1405 : static int PrefChanged(const char* aPref, void* aClosure)
     328                 : {
     329            1405 :   if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
     330            2810 :     nsAdoptingString value = Preferences::GetString(aPref);
     331            2810 :     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     332            1405 :     if (value.IsEmpty()) {
     333               0 :       gVolumeScale = 1.0;
     334                 :     } else {
     335            2810 :       NS_ConvertUTF16toUTF8 utf8(value);
     336            1405 :       gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nsnull));
     337                 :     }
     338               0 :   } else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
     339               0 :     bool value = Preferences::GetBool(aPref, false);
     340               0 :     mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     341               0 :     gUseCubeb = value;
     342                 :   }
     343            1405 :   return 0;
     344                 : }
     345                 : 
     346               0 : static double GetVolumeScale()
     347                 : {
     348               0 :   mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     349               0 :   return gVolumeScale;
     350                 : }
     351                 : 
     352                 : #if defined(MOZ_CUBEB)
     353                 : static bool GetUseCubeb()
     354                 : {
     355                 :   mozilla::MutexAutoLock lock(*gAudioPrefsLock);
     356                 :   return gUseCubeb;
     357                 : }
     358                 : #endif
     359                 : 
     360            1404 : void nsAudioStream::InitLibrary()
     361                 : {
     362                 : #ifdef PR_LOGGING
     363            1404 :   gAudioStreamLog = PR_NewLogModule("nsAudioStream");
     364                 : #endif
     365            1404 :   gAudioPrefsLock = new mozilla::Mutex("nsAudioStream::gAudioPrefsLock");
     366            1404 :   PrefChanged(PREF_VOLUME_SCALE, nsnull);
     367            1404 :   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
     368                 : #if defined(MOZ_CUBEB)
     369                 :   PrefChanged(PREF_USE_CUBEB, nsnull);
     370                 :   Preferences::RegisterCallback(PrefChanged, PREF_USE_CUBEB);
     371                 :   if (cubeb_init(&gCubebContext, "nsAudioStream") != 0) {
     372                 :     NS_WARNING("cubeb_init failed");
     373                 :   }
     374                 : #endif
     375            1404 : }
     376                 : 
     377            1403 : void nsAudioStream::ShutdownLibrary()
     378                 : {
     379            1403 :   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
     380                 : #if defined(MOZ_CUBEB)
     381                 :   Preferences::UnregisterCallback(PrefChanged, PREF_USE_CUBEB);
     382                 : #endif
     383            1403 :   delete gAudioPrefsLock;
     384            1403 :   gAudioPrefsLock = nsnull;
     385                 : 
     386                 : #if defined(MOZ_CUBEB)
     387                 :   if (gCubebContext) {
     388                 :     cubeb_destroy(gCubebContext);
     389                 :     gCubebContext = nsnull;
     390                 :   }
     391                 : #endif
     392            1403 : }
     393                 : 
     394                 : nsIThread *
     395               0 : nsAudioStream::GetThread()
     396                 : {
     397               0 :   if (!mAudioPlaybackThread) {
     398               0 :     NS_NewThread(getter_AddRefs(mAudioPlaybackThread),
     399                 :                  nsnull,
     400               0 :                  MEDIA_THREAD_STACK_SIZE);
     401                 :   }
     402               0 :   return mAudioPlaybackThread;
     403                 : }
     404                 : 
     405                 : class AsyncShutdownPlaybackThread : public nsRunnable
     406               0 : {
     407                 : public:
     408               0 :   AsyncShutdownPlaybackThread(nsIThread* aThread) : mThread(aThread) {}
     409               0 :   NS_IMETHODIMP Run() { return mThread->Shutdown(); }
     410                 : private:
     411                 :   nsCOMPtr<nsIThread> mThread;
     412                 : };
     413                 : 
     414               0 : nsAudioStream::~nsAudioStream()
     415                 : {
     416               0 :   if (mAudioPlaybackThread) {
     417               0 :     nsCOMPtr<nsIRunnable> event = new AsyncShutdownPlaybackThread(mAudioPlaybackThread);
     418               0 :     NS_DispatchToMainThread(event);
     419                 :   }
     420               0 : }
     421                 : 
     422               0 : nsNativeAudioStream::nsNativeAudioStream() :
     423                 :   mVolume(1.0),
     424                 :   mAudioHandle(0),
     425                 :   mPaused(false),
     426               0 :   mInError(false)
     427                 : {
     428               0 : }
     429                 : 
     430               0 : nsNativeAudioStream::~nsNativeAudioStream()
     431                 : {
     432               0 :   Shutdown();
     433               0 : }
     434                 : 
     435               0 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsNativeAudioStream)
     436                 : 
     437               0 : nsresult nsNativeAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
     438                 : {
     439               0 :   mRate = aRate;
     440               0 :   mChannels = aNumChannels;
     441               0 :   mFormat = aFormat;
     442                 : 
     443               0 :   if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t**>(&mAudioHandle),
     444                 :                            NULL,
     445                 :                            SA_MODE_WRONLY,
     446                 :                            SA_PCM_FORMAT_S16_NE,
     447                 :                            aRate,
     448               0 :                            aNumChannels) != SA_SUCCESS) {
     449               0 :     mAudioHandle = nsnull;
     450               0 :     mInError = true;
     451               0 :     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_create_pcm error"));
     452               0 :     return NS_ERROR_FAILURE;
     453                 :   }
     454                 : 
     455               0 :   if (sa_stream_open(static_cast<sa_stream_t*>(mAudioHandle)) != SA_SUCCESS) {
     456               0 :     sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
     457               0 :     mAudioHandle = nsnull;
     458               0 :     mInError = true;
     459               0 :     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_open error"));
     460               0 :     return NS_ERROR_FAILURE;
     461                 :   }
     462               0 :   mInError = false;
     463                 : 
     464               0 :   return NS_OK;
     465                 : }
     466                 : 
     467               0 : void nsNativeAudioStream::Shutdown()
     468                 : {
     469               0 :   if (!mAudioHandle)
     470               0 :     return;
     471                 : 
     472               0 :   sa_stream_destroy(static_cast<sa_stream_t*>(mAudioHandle));
     473               0 :   mAudioHandle = nsnull;
     474               0 :   mInError = true;
     475                 : }
     476                 : 
     477               0 : nsresult nsNativeAudioStream::Write(const void* aBuf, PRUint32 aFrames)
     478                 : {
     479               0 :   NS_ASSERTION(!mPaused, "Don't write audio when paused, you'll block");
     480                 : 
     481               0 :   if (mInError)
     482               0 :     return NS_ERROR_FAILURE;
     483                 : 
     484               0 :   PRUint32 samples = aFrames * mChannels;
     485               0 :   nsAutoArrayPtr<short> s_data(new short[samples]);
     486                 : 
     487               0 :   if (s_data) {
     488               0 :     double scaled_volume = GetVolumeScale() * mVolume;
     489               0 :     switch (mFormat) {
     490                 :       case FORMAT_U8: {
     491               0 :         const PRUint8* buf = static_cast<const PRUint8*>(aBuf);
     492               0 :         PRInt32 volume = PRInt32((1 << 16) * scaled_volume);
     493               0 :         for (PRUint32 i = 0; i < samples; ++i) {
     494               0 :           s_data[i] = short(((PRInt32(buf[i]) - 128) * volume) >> 8);
     495                 :         }
     496               0 :         break;
     497                 :       }
     498                 :       case FORMAT_S16_LE: {
     499               0 :         const short* buf = static_cast<const short*>(aBuf);
     500               0 :         PRInt32 volume = PRInt32((1 << 16) * scaled_volume);
     501               0 :         for (PRUint32 i = 0; i < samples; ++i) {
     502               0 :           short s = buf[i];
     503                 : #if defined(IS_BIG_ENDIAN)
     504                 :           s = ((s & 0x00ff) << 8) | ((s & 0xff00) >> 8);
     505                 : #endif
     506               0 :           s_data[i] = short((PRInt32(s) * volume) >> 16);
     507                 :         }
     508               0 :         break;
     509                 :       }
     510                 :       case FORMAT_FLOAT32: {
     511               0 :         const float* buf = static_cast<const float*>(aBuf);
     512               0 :         for (PRUint32 i = 0; i <  samples; ++i) {
     513               0 :           float scaled_value = floorf(0.5 + 32768 * buf[i] * scaled_volume);
     514               0 :           if (buf[i] < 0.0) {
     515               0 :             s_data[i] = (scaled_value < -32768.0) ?
     516                 :               -32768 :
     517               0 :               short(scaled_value);
     518                 :           } else {
     519               0 :             s_data[i] = (scaled_value > 32767.0) ?
     520                 :               32767 :
     521               0 :               short(scaled_value);
     522                 :           }
     523                 :         }
     524               0 :         break;
     525                 :       }
     526                 :     }
     527                 : 
     528               0 :     if (sa_stream_write(static_cast<sa_stream_t*>(mAudioHandle),
     529               0 :                         s_data.get(),
     530               0 :                         samples * sizeof(short)) != SA_SUCCESS)
     531                 :     {
     532               0 :       PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_write error"));
     533               0 :       mInError = true;
     534               0 :       return NS_ERROR_FAILURE;
     535                 :     }
     536                 :   }
     537               0 :   return NS_OK;
     538                 : }
     539                 : 
     540               0 : PRUint32 nsNativeAudioStream::Available()
     541                 : {
     542                 :   // If the audio backend failed to open, lie and say we'll accept some
     543                 :   // data.
     544               0 :   if (mInError)
     545               0 :     return FAKE_BUFFER_SIZE;
     546                 : 
     547               0 :   size_t s = 0;
     548               0 :   if (sa_stream_get_write_size(static_cast<sa_stream_t*>(mAudioHandle), &s) != SA_SUCCESS)
     549               0 :     return 0;
     550                 : 
     551               0 :   return s / mChannels / sizeof(short);
     552                 : }
     553                 : 
     554               0 : void nsNativeAudioStream::SetVolume(double aVolume)
     555                 : {
     556               0 :   NS_ASSERTION(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
     557                 : #if defined(SA_PER_STREAM_VOLUME)
     558                 :   if (sa_stream_set_volume_abs(static_cast<sa_stream_t*>(mAudioHandle), aVolume) != SA_SUCCESS) {
     559                 :     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_set_volume_abs error"));
     560                 :     mInError = true;
     561                 :   }
     562                 : #else
     563               0 :   mVolume = aVolume;
     564                 : #endif
     565               0 : }
     566                 : 
     567               0 : void nsNativeAudioStream::Drain()
     568                 : {
     569               0 :   NS_ASSERTION(!mPaused, "Don't drain audio when paused, it won't finish!");
     570                 : 
     571               0 :   if (mInError)
     572               0 :     return;
     573                 : 
     574               0 :   int r = sa_stream_drain(static_cast<sa_stream_t*>(mAudioHandle));
     575               0 :   if (r != SA_SUCCESS && r != SA_ERROR_INVALID) {
     576               0 :     PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsNativeAudioStream: sa_stream_drain error"));
     577               0 :     mInError = true;
     578                 :   }
     579                 : }
     580                 : 
     581               0 : void nsNativeAudioStream::Pause()
     582                 : {
     583               0 :   if (mInError)
     584               0 :     return;
     585               0 :   mPaused = true;
     586               0 :   sa_stream_pause(static_cast<sa_stream_t*>(mAudioHandle));
     587                 : }
     588                 : 
     589               0 : void nsNativeAudioStream::Resume()
     590                 : {
     591               0 :   if (mInError)
     592               0 :     return;
     593               0 :   mPaused = false;
     594               0 :   sa_stream_resume(static_cast<sa_stream_t*>(mAudioHandle));
     595                 : }
     596                 : 
     597               0 : PRInt64 nsNativeAudioStream::GetPosition()
     598                 : {
     599               0 :   PRInt64 position = GetPositionInFrames();
     600               0 :   if (position >= 0) {
     601               0 :     return ((USECS_PER_S * position) / mRate);
     602                 :   }
     603               0 :   return -1;
     604                 : }
     605                 : 
     606               0 : PRInt64 nsNativeAudioStream::GetPositionInFrames()
     607                 : {
     608               0 :   if (mInError) {
     609               0 :     return -1;
     610                 :   }
     611                 : 
     612               0 :   sa_position_t positionType = SA_POSITION_WRITE_SOFTWARE;
     613                 : #if defined(XP_WIN)
     614                 :   positionType = SA_POSITION_WRITE_HARDWARE;
     615                 : #endif
     616               0 :   int64_t position = 0;
     617               0 :   if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
     618               0 :                              positionType, &position) == SA_SUCCESS) {
     619               0 :     return position / mChannels / sizeof(short);
     620                 :   }
     621                 : 
     622               0 :   return -1;
     623                 : }
     624                 : 
     625               0 : bool nsNativeAudioStream::IsPaused()
     626                 : {
     627               0 :   return mPaused;
     628                 : }
     629                 : 
     630               0 : PRInt32 nsNativeAudioStream::GetMinWriteSize()
     631                 : {
     632                 :   size_t size;
     633                 :   int r = sa_stream_get_min_write(static_cast<sa_stream_t*>(mAudioHandle),
     634               0 :                                   &size);
     635               0 :   if (r == SA_ERROR_NOT_SUPPORTED)
     636               0 :     return 1;
     637               0 :   else if (r != SA_SUCCESS || size > PR_INT32_MAX)
     638               0 :     return -1;
     639                 : 
     640               0 :   return static_cast<PRInt32>(size / mChannels / sizeof(short));
     641                 : }
     642                 : 
     643                 : #if defined(REMOTE_AUDIO)
     644                 : nsRemotedAudioStream::nsRemotedAudioStream()
     645                 :  : mAudioChild(nsnull),
     646                 :    mBytesPerFrame(0),
     647                 :    mPaused(false)
     648                 : {}
     649                 : 
     650                 : nsRemotedAudioStream::~nsRemotedAudioStream()
     651                 : {
     652                 :   Shutdown();
     653                 : }
     654                 : 
     655                 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsRemotedAudioStream)
     656                 : 
     657                 : nsresult
     658                 : nsRemotedAudioStream::Init(PRInt32 aNumChannels,
     659                 :                            PRInt32 aRate,
     660                 :                            SampleFormat aFormat)
     661                 : {
     662                 :   mRate = aRate;
     663                 :   mChannels = aNumChannels;
     664                 :   mFormat = aFormat;
     665                 : 
     666                 :   switch (mFormat) {
     667                 :     case FORMAT_U8: {
     668                 :       mBytesPerFrame = sizeof(PRUint8) * mChannels;
     669                 :       break;
     670                 :     }
     671                 :     case FORMAT_S16_LE: {
     672                 :       mBytesPerFrame = sizeof(short) * mChannels;
     673                 :       break;
     674                 :     }
     675                 :     case FORMAT_FLOAT32: {
     676                 :       mBytesPerFrame = sizeof(float) * mChannels;
     677                 :     }
     678                 :   }
     679                 : 
     680                 :   nsCOMPtr<nsIRunnable> event = new AudioInitEvent(this);
     681                 :   NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
     682                 :   return NS_OK;
     683                 : }
     684                 : 
     685                 : void
     686                 : nsRemotedAudioStream::Shutdown()
     687                 : {
     688                 :   if (!mAudioChild)
     689                 :     return;
     690                 :   nsCOMPtr<nsIRunnable> event = new AudioShutdownEvent(mAudioChild);
     691                 :   NS_DispatchToMainThread(event);
     692                 :   mAudioChild = nsnull;
     693                 : }
     694                 : 
     695                 : nsresult
     696                 : nsRemotedAudioStream::Write(const void* aBuf, PRUint32 aFrames)
     697                 : {
     698                 :   if (!mAudioChild)
     699                 :     return NS_ERROR_FAILURE;
     700                 :   nsCOMPtr<nsIRunnable> event = new AudioWriteEvent(mAudioChild,
     701                 :                                                     aBuf,
     702                 :                                                     aFrames,
     703                 :                                                     mBytesPerFrame);
     704                 :   NS_DispatchToMainThread(event);
     705                 :   return NS_OK;
     706                 : }
     707                 : 
     708                 : PRUint32
     709                 : nsRemotedAudioStream::Available()
     710                 : {
     711                 :   return FAKE_BUFFER_SIZE;
     712                 : }
     713                 : 
     714                 : PRInt32 nsRemotedAudioStream::GetMinWriteSize()
     715                 : {
     716                 :   if (!mAudioChild)
     717                 :     return -1;
     718                 :   nsCOMPtr<nsIRunnable> event = new AudioMinWriteSizeEvent(mAudioChild);
     719                 :   NS_DispatchToMainThread(event);
     720                 :   return mAudioChild->WaitForMinWriteSize();
     721                 : }
     722                 : 
     723                 : void
     724                 : nsRemotedAudioStream::SetVolume(double aVolume)
     725                 : {
     726                 :   if (!mAudioChild)
     727                 :     return;
     728                 :   nsCOMPtr<nsIRunnable> event = new AudioSetVolumeEvent(mAudioChild, aVolume);
     729                 :   NS_DispatchToMainThread(event);
     730                 : }
     731                 : 
     732                 : void
     733                 : nsRemotedAudioStream::Drain()
     734                 : {
     735                 :   if (!mAudioChild)
     736                 :     return;
     737                 :   nsCOMPtr<nsIRunnable> event = new AudioDrainEvent(mAudioChild);
     738                 :   NS_DispatchToMainThread(event);
     739                 :   mAudioChild->WaitForDrain();
     740                 : }
     741                 : 
     742                 : void
     743                 : nsRemotedAudioStream::Pause()
     744                 : {
     745                 :   mPaused = true;
     746                 :   if (!mAudioChild)
     747                 :     return;
     748                 :   nsCOMPtr<nsIRunnable> event = new AudioPauseEvent(mAudioChild, true);
     749                 :   NS_DispatchToMainThread(event);
     750                 : }
     751                 : 
     752                 : void
     753                 : nsRemotedAudioStream::Resume()
     754                 : {
     755                 :   mPaused = false;
     756                 :   if (!mAudioChild)
     757                 :     return;
     758                 :   nsCOMPtr<nsIRunnable> event = new AudioPauseEvent(mAudioChild, false);
     759                 :   NS_DispatchToMainThread(event);
     760                 : }
     761                 : 
     762                 : PRInt64 nsRemotedAudioStream::GetPosition()
     763                 : {
     764                 :   PRInt64 position = GetPositionInFrames();
     765                 :   if (position >= 0) {
     766                 :     return ((USECS_PER_S * position) / mRate);
     767                 :   }
     768                 :   return 0;
     769                 : }
     770                 : 
     771                 : PRInt64
     772                 : nsRemotedAudioStream::GetPositionInFrames()
     773                 : {
     774                 :   if(!mAudioChild)
     775                 :     return 0;
     776                 : 
     777                 :   PRInt64 position = mAudioChild->GetLastKnownPosition();
     778                 :   if (position == -1)
     779                 :     return 0;
     780                 : 
     781                 :   PRInt64 time = mAudioChild->GetLastKnownPositionTimestamp();
     782                 :   PRInt64 dt = PR_IntervalToMilliseconds(PR_IntervalNow() - time);
     783                 : 
     784                 :   return position + (mRate * dt / MS_PER_S);
     785                 : }
     786                 : 
     787                 : bool
     788                 : nsRemotedAudioStream::IsPaused()
     789                 : {
     790                 :   return mPaused;
     791                 : }
     792                 : #endif
     793                 : 
     794                 : #if defined(MOZ_CUBEB)
     795                 : template <>
     796                 : class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
     797                 : {
     798                 : public:
     799                 :   static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
     800                 : };
     801                 : 
     802                 : class nsBufferedAudioStream : public nsAudioStream
     803                 : {
     804                 :  public:
     805                 :   NS_DECL_ISUPPORTS
     806                 : 
     807                 :   nsBufferedAudioStream();
     808                 :   ~nsBufferedAudioStream();
     809                 : 
     810                 :   nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
     811                 :   void Shutdown();
     812                 :   nsresult Write(const void* aBuf, PRUint32 aFrames);
     813                 :   PRUint32 Available();
     814                 :   void SetVolume(double aVolume);
     815                 :   void Drain();
     816                 :   void Pause();
     817                 :   void Resume();
     818                 :   PRInt64 GetPosition();
     819                 :   PRInt64 GetPositionInFrames();
     820                 :   bool IsPaused();
     821                 :   PRInt32 GetMinWriteSize();
     822                 : 
     823                 : private:
     824                 :   static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
     825                 :   {
     826                 :     return static_cast<nsBufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
     827                 :   }
     828                 : 
     829                 :   static int StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
     830                 :   {
     831                 :     return static_cast<nsBufferedAudioStream*>(aThis)->StateCallback(aState);
     832                 :   }
     833                 : 
     834                 :   long DataCallback(void* aBuffer, long aFrames);
     835                 :   int StateCallback(cubeb_state aState);
     836                 : 
     837                 :   // Shared implementation of underflow adjusted position calculation.
     838                 :   // Caller must own the monitor.
     839                 :   PRInt64 GetPositionInFramesUnlocked();
     840                 : 
     841                 :   // The monitor is held to protect all access to member variables.  Write()
     842                 :   // waits while mBuffer is full; DataCallback() notifies as it consumes
     843                 :   // data from mBuffer.  Drain() waits while mState is DRAINING;
     844                 :   // StateCallback() notifies when mState is DRAINED.
     845                 :   Monitor mMonitor;
     846                 : 
     847                 :   // Sum of silent frames written when DataCallback requests more frames
     848                 :   // than are available in mBuffer.
     849                 :   PRUint64 mLostFrames;
     850                 : 
     851                 :   // Temporary audio buffer.  Filled by Write() and consumed by
     852                 :   // DataCallback().  Once mBufferLimit is reached, Write() blocks until
     853                 :   // sufficient space becomes available in mBuffer.  The buffer and buffer
     854                 :   // limit deal in bytes, not frames.
     855                 :   nsTArray<PRUint8> mBuffer;
     856                 :   PRUint32 mBufferLimit;
     857                 : 
     858                 :   // Software volume level.  Applied during the servicing of DataCallback().
     859                 :   double mVolume;
     860                 : 
     861                 :   // Owning reference to a cubeb_stream.  cubeb_stream_destroy is called by
     862                 :   // nsAutoRef's destructor.
     863                 :   nsAutoRef<cubeb_stream> mCubebStream;
     864                 : 
     865                 :   PRUint32 mBytesPerFrame;
     866                 : 
     867                 :   enum StreamState {
     868                 :     INITIALIZED, // Initialized, playback has not begun.
     869                 :     STARTED,     // Started by a call to Write() (iff INITIALIZED) or Resume().
     870                 :     STOPPED,     // Stopped by a call to Pause().
     871                 :     DRAINING,    // Drain requested.  DataCallback will indicate end of stream
     872                 :                  // once the remaining contents of mBuffer are requested by
     873                 :                  // cubeb, after which StateCallback will indicate drain
     874                 :                  // completion.
     875                 :     DRAINED      // StateCallback has indicated that the drain is complete.
     876                 :   };
     877                 : 
     878                 :   StreamState mState;
     879                 : 
     880                 :   // Arbitrary default stream latency.  The higher this value, the longer stream
     881                 :   // volume changes will take to become audible.
     882                 :   static const unsigned int DEFAULT_LATENCY_MS = 100;
     883                 : };
     884                 : #endif
     885                 : 
     886               0 : nsAudioStream* nsAudioStream::AllocateStream()
     887                 : {
     888                 : #if defined(REMOTE_AUDIO)
     889                 :   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     890                 :     return new nsRemotedAudioStream();
     891                 :   }
     892                 : #endif
     893                 : #if defined(MOZ_CUBEB)
     894                 :   if (GetUseCubeb()) {
     895                 :     return new nsBufferedAudioStream();
     896                 :   }
     897                 : #endif
     898               0 :   return new nsNativeAudioStream();
     899                 : }
     900                 : 
     901                 : #if defined(MOZ_CUBEB)
     902                 : nsBufferedAudioStream::nsBufferedAudioStream()
     903                 :   : mMonitor("nsBufferedAudioStream"), mLostFrames(0), mVolume(1.0),
     904                 :     mBytesPerFrame(0), mState(INITIALIZED)
     905                 : {
     906                 : }
     907                 : 
     908                 : nsBufferedAudioStream::~nsBufferedAudioStream()
     909                 : {
     910                 :   Shutdown();
     911                 : }
     912                 : 
     913                 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsBufferedAudioStream)
     914                 : 
     915                 : nsresult
     916                 : nsBufferedAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
     917                 : {
     918                 :   if (!gCubebContext || aNumChannels < 0 || aRate < 0) {
     919                 :     return NS_ERROR_FAILURE;
     920                 :   }
     921                 : 
     922                 :   mRate = aRate;
     923                 :   mChannels = aNumChannels;
     924                 :   mFormat = aFormat;
     925                 : 
     926                 :   cubeb_stream_params params;
     927                 :   params.rate = aRate;
     928                 :   params.channels = aNumChannels;
     929                 :   switch (aFormat) {
     930                 :   case FORMAT_S16_LE:
     931                 :     params.format = CUBEB_SAMPLE_S16LE;
     932                 :     mBytesPerFrame = sizeof(short) * aNumChannels;
     933                 :     break;
     934                 :   case FORMAT_FLOAT32:
     935                 :     params.format = CUBEB_SAMPLE_FLOAT32LE;
     936                 :     mBytesPerFrame = sizeof(float) * aNumChannels;
     937                 :     break;
     938                 :   default:
     939                 :     return NS_ERROR_FAILURE;
     940                 :   }
     941                 : 
     942                 :   {
     943                 :     cubeb_stream* stream;
     944                 :     if (cubeb_stream_init(gCubebContext, &stream, "nsBufferedAudioStream", params,
     945                 :                           DEFAULT_LATENCY_MS, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
     946                 :       mCubebStream.own(stream);
     947                 :     }
     948                 :   }
     949                 : 
     950                 :   if (!mCubebStream) {
     951                 :     return NS_ERROR_FAILURE;
     952                 :   }
     953                 : 
     954                 :   // Limit mBuffer to one second of audio.  This value is arbitrary, and was
     955                 :   // selected based on the observed behaviour of the existing nsAudioStream
     956                 :   // implementations.
     957                 :   mBufferLimit = aRate * mBytesPerFrame;
     958                 :   NS_ABORT_IF_FALSE(mBufferLimit % mBytesPerFrame == 0, "Must buffer complete frames");
     959                 : 
     960                 :   // Pre-allocate the buffer.  nsTArray::RemoveElementsAt shrinks the buffer
     961                 :   // only if its length reaches zero, so allocator thrashing should be
     962                 :   // minimal.
     963                 :   mBuffer.SetCapacity(mBufferLimit);
     964                 : 
     965                 :   return NS_OK;
     966                 : }
     967                 : 
     968                 : void
     969                 : nsBufferedAudioStream::Shutdown()
     970                 : {
     971                 :   if (mCubebStream) {
     972                 :     cubeb_stream_stop(mCubebStream);
     973                 :     mCubebStream.reset();
     974                 :   }
     975                 : }
     976                 : 
     977                 : nsresult
     978                 : nsBufferedAudioStream::Write(const void* aBuf, PRUint32 aFrames)
     979                 : {
     980                 :   MonitorAutoLock mon(mMonitor);
     981                 :   if (!mCubebStream) {
     982                 :     return NS_ERROR_FAILURE;
     983                 :   }
     984                 :   NS_ASSERTION(mState == INITIALIZED || mState == STARTED, "Stream write in unexpected state.");
     985                 : 
     986                 :   const PRUint8* src = static_cast<const PRUint8*>(aBuf);
     987                 :   PRUint32 bytesToCopy = aFrames * mBytesPerFrame;
     988                 : 
     989                 :   while (bytesToCopy > 0) {
     990                 :     NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated.");
     991                 : 
     992                 :     PRUint32 available = NS_MIN(bytesToCopy, mBufferLimit - mBuffer.Length());
     993                 :     NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames.");
     994                 : 
     995                 :     mBuffer.AppendElements(src, available);
     996                 :     src += available;
     997                 :     bytesToCopy -= available;
     998                 : 
     999                 :     if (mState != STARTED && cubeb_stream_start(mCubebStream) == CUBEB_OK) {
    1000                 :       mState = STARTED;
    1001                 :     }
    1002                 : 
    1003                 :     if (bytesToCopy > 0) {
    1004                 :       mon.Wait();
    1005                 :     }
    1006                 :   }
    1007                 : 
    1008                 :   return NS_OK;
    1009                 : }
    1010                 : 
    1011                 : PRUint32
    1012                 : nsBufferedAudioStream::Available()
    1013                 : {
    1014                 :   MonitorAutoLock mon(mMonitor);
    1015                 :   NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated.");
    1016                 :   NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
    1017                 :   return (mBufferLimit - mBuffer.Length()) / mBytesPerFrame;
    1018                 : }
    1019                 : 
    1020                 : PRInt32 nsBufferedAudioStream::GetMinWriteSize()
    1021                 : {
    1022                 :   return 1;
    1023                 : }
    1024                 : 
    1025                 : void
    1026                 : nsBufferedAudioStream::SetVolume(double aVolume)
    1027                 : {
    1028                 :   MonitorAutoLock mon(mMonitor);
    1029                 :   NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
    1030                 :   mVolume = aVolume;
    1031                 : }
    1032                 : 
    1033                 : void
    1034                 : nsBufferedAudioStream::Drain()
    1035                 : {
    1036                 :   MonitorAutoLock mon(mMonitor);
    1037                 :   if (mState != STARTED) {
    1038                 :     return;
    1039                 :   }
    1040                 :   mState = DRAINING;
    1041                 :   while (mState != DRAINED) {
    1042                 :     mon.Wait();
    1043                 :   }
    1044                 : }
    1045                 : 
    1046                 : void
    1047                 : nsBufferedAudioStream::Pause()
    1048                 : {
    1049                 :   MonitorAutoLock mon(mMonitor);
    1050                 :   if (!mCubebStream || mState != STARTED) {
    1051                 :     return;
    1052                 :   }
    1053                 : 
    1054                 :   if (cubeb_stream_stop(mCubebStream) == CUBEB_OK) {
    1055                 :     mState = STOPPED;
    1056                 :   }
    1057                 : }
    1058                 : 
    1059                 : void
    1060                 : nsBufferedAudioStream::Resume()
    1061                 : {
    1062                 :   MonitorAutoLock mon(mMonitor);
    1063                 :   if (!mCubebStream || mState != STOPPED) {
    1064                 :     return;
    1065                 :   }
    1066                 : 
    1067                 :   if (cubeb_stream_start(mCubebStream) == CUBEB_OK) {
    1068                 :     mState = STARTED;
    1069                 :   }
    1070                 : }
    1071                 : 
    1072                 : PRInt64 nsBufferedAudioStream::GetPosition()
    1073                 : {
    1074                 :   MonitorAutoLock mon(mMonitor);
    1075                 :   PRInt64 frames = GetPositionInFramesUnlocked();
    1076                 :   if (frames >= 0) {
    1077                 :     return USECS_PER_S * frames / mRate;
    1078                 :   }
    1079                 :   return -1;
    1080                 : }
    1081                 : 
    1082                 : PRInt64
    1083                 : nsBufferedAudioStream::GetPositionInFrames()
    1084                 : {
    1085                 :   MonitorAutoLock mon(mMonitor);
    1086                 :   return GetPositionInFramesUnlocked();
    1087                 : }
    1088                 : 
    1089                 : PRInt64
    1090                 : nsBufferedAudioStream::GetPositionInFramesUnlocked()
    1091                 : {
    1092                 :   mMonitor.AssertCurrentThreadOwns();
    1093                 : 
    1094                 :   if (!mCubebStream) {
    1095                 :     return -1;
    1096                 :   }
    1097                 : 
    1098                 :   uint64_t position = 0;
    1099                 :   if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) {
    1100                 :     return -1;
    1101                 :   }
    1102                 : 
    1103                 :   // Adjust the reported position by the number of silent frames written
    1104                 :   // during stream underruns.
    1105                 :   PRInt64 adjustedPosition = 0;
    1106                 :   if (position >= mLostFrames) {
    1107                 :     adjustedPosition = position - mLostFrames;
    1108                 :   }
    1109                 :   return adjustedPosition;
    1110                 : }
    1111                 : 
    1112                 : bool
    1113                 : nsBufferedAudioStream::IsPaused()
    1114                 : {
    1115                 :   MonitorAutoLock mon(mMonitor);
    1116                 :   return mState == STOPPED;
    1117                 : }
    1118                 : 
    1119                 : template<typename T>
    1120                 : void
    1121                 : SampleCopy(void* aDst, const PRUint8* aSrc, PRUint32 aSamples, double aVolume)
    1122                 : {
    1123                 :   const T* src = reinterpret_cast<const T*>(aSrc);
    1124                 :   double scaled_volume = GetVolumeScale() * aVolume;
    1125                 :   T* dst = static_cast<T*>(aDst);
    1126                 :   for (PRUint32 i = 0; i < aSamples; ++i) {
    1127                 :     dst[i] = T(src[i] * scaled_volume);
    1128                 :   }
    1129                 : }
    1130                 : 
    1131                 : long
    1132                 : nsBufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
    1133                 : {
    1134                 :   MonitorAutoLock mon(mMonitor);
    1135                 :   PRUint32 bytesWanted = aFrames * mBytesPerFrame;
    1136                 : 
    1137                 :   // Adjust bytesWanted to fit what is available in mBuffer.
    1138                 :   PRUint32 available = NS_MIN(bytesWanted, mBuffer.Length());
    1139                 :   NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
    1140                 : 
    1141                 :   // Copy each sample from mBuffer to aBuffer, adjusting the volume during the copy.
    1142                 :   PRUint32 samplesToCopy = available / mBytesPerFrame * mChannels;
    1143                 :   switch (mFormat) {
    1144                 :   case FORMAT_S16_LE:
    1145                 :     SampleCopy<PRInt16>(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume);
    1146                 :     break;
    1147                 :   case FORMAT_FLOAT32:
    1148                 :     SampleCopy<float>(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume);
    1149                 :     break;
    1150                 :   default:
    1151                 :     return -1;
    1152                 :   }
    1153                 : 
    1154                 :   // Remove copied data from the temporary audio buffer.
    1155                 :   mBuffer.RemoveElementsAt(0, available);
    1156                 :   NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
    1157                 : 
    1158                 :   // Notify any blocked Write() call that more space is available in mBuffer.
    1159                 :   mon.NotifyAll();
    1160                 : 
    1161                 :   // Calculate remaining bytes requested by caller.  If the stream is not
    1162                 :   // draining an underrun has occurred, so fill the remaining buffer with
    1163                 :   // silence.
    1164                 :   bytesWanted -= available;
    1165                 :   if (mState != DRAINING) {
    1166                 :     memset(static_cast<PRUint8*>(aBuffer) + available, 0, bytesWanted);
    1167                 :     mLostFrames += bytesWanted / mBytesPerFrame;
    1168                 :     bytesWanted = 0;
    1169                 :   }
    1170                 : 
    1171                 :   return aFrames - (bytesWanted / mBytesPerFrame);
    1172                 : }
    1173                 : 
    1174                 : int
    1175                 : nsBufferedAudioStream::StateCallback(cubeb_state aState)
    1176                 : {
    1177                 :   if (aState == CUBEB_STATE_DRAINED) {
    1178                 :     MonitorAutoLock mon(mMonitor);
    1179                 :     mState = DRAINED;
    1180                 :     mon.NotifyAll();
    1181                 :   }
    1182                 :   return CUBEB_OK;
    1183                 : }
    1184                 : #endif
    1185                 : 

Generated by: LCOV version 1.7