content/media/webaudio/AudioBuffer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webaudio/AudioBuffer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,266 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     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 "AudioBuffer.h"
    1.11 +#include "mozilla/dom/AudioBufferBinding.h"
    1.12 +#include "jsfriendapi.h"
    1.13 +#include "mozilla/ErrorResult.h"
    1.14 +#include "AudioSegment.h"
    1.15 +#include "AudioChannelFormat.h"
    1.16 +#include "mozilla/PodOperations.h"
    1.17 +#include "mozilla/CheckedInt.h"
    1.18 +#include "AudioNodeEngine.h"
    1.19 +
    1.20 +namespace mozilla {
    1.21 +namespace dom {
    1.22 +
    1.23 +NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
    1.24 +
    1.25 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
    1.26 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
    1.27 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSChannels)
    1.28 +  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    1.29 +  tmp->ClearJSChannels();
    1.30 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1.31 +
    1.32 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioBuffer)
    1.33 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
    1.34 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    1.35 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1.36 +
    1.37 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AudioBuffer)
    1.38 +  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    1.39 +  for (uint32_t i = 0; i < tmp->mJSChannels.Length(); ++i) {
    1.40 +    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSChannels[i])
    1.41 +  }
    1.42 +NS_IMPL_CYCLE_COLLECTION_TRACE_END
    1.43 +
    1.44 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
    1.45 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
    1.46 +
    1.47 +AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
    1.48 +                         uint32_t aLength, float aSampleRate)
    1.49 +  : mContext(aContext),
    1.50 +    mLength(aLength),
    1.51 +    mSampleRate(aSampleRate)
    1.52 +{
    1.53 +  mJSChannels.SetCapacity(aNumberOfChannels);
    1.54 +  SetIsDOMBinding();
    1.55 +  mozilla::HoldJSObjects(this);
    1.56 +}
    1.57 +
    1.58 +AudioBuffer::~AudioBuffer()
    1.59 +{
    1.60 +  ClearJSChannels();
    1.61 +}
    1.62 +
    1.63 +void
    1.64 +AudioBuffer::ClearJSChannels()
    1.65 +{
    1.66 +  mJSChannels.Clear();
    1.67 +  mozilla::DropJSObjects(this);
    1.68 +}
    1.69 +
    1.70 +/* static */ already_AddRefed<AudioBuffer>
    1.71 +AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
    1.72 +                    uint32_t aLength, float aSampleRate,
    1.73 +                    JSContext* aJSContext, ErrorResult& aRv)
    1.74 +{
    1.75 +  // Note that a buffer with zero channels is permitted here for the sake of
    1.76 +  // AudioProcessingEvent, where channel counts must match parameters passed
    1.77 +  // to createScriptProcessor(), one of which may be zero.
    1.78 +  if (aSampleRate < WebAudioUtils::MinSampleRate ||
    1.79 +      aSampleRate > WebAudioUtils::MaxSampleRate ||
    1.80 +      aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
    1.81 +      !aLength || aLength > INT32_MAX) {
    1.82 +    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    1.83 +    return nullptr;
    1.84 +  }
    1.85 +
    1.86 +  nsRefPtr<AudioBuffer> buffer =
    1.87 +    new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate);
    1.88 +
    1.89 +  for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
    1.90 +    JS::Rooted<JSObject*> array(aJSContext,
    1.91 +                                JS_NewFloat32Array(aJSContext, aLength));
    1.92 +    if (!array) {
    1.93 +      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    1.94 +      return nullptr;
    1.95 +    }
    1.96 +    buffer->mJSChannels.AppendElement(array.get());
    1.97 +  }
    1.98 +
    1.99 +  return buffer.forget();
   1.100 +}
   1.101 +
   1.102 +JSObject*
   1.103 +AudioBuffer::WrapObject(JSContext* aCx)
   1.104 +{
   1.105 +  return AudioBufferBinding::Wrap(aCx, this);
   1.106 +}
   1.107 +
   1.108 +bool
   1.109 +AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
   1.110 +{
   1.111 +  if (mSharedChannels) {
   1.112 +    for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
   1.113 +      const float* data = mSharedChannels->GetData(i);
   1.114 +      // The following code first zeroes the array and then copies our data
   1.115 +      // into it. We could avoid this with additional JS APIs to construct
   1.116 +      // an array (or ArrayBuffer) containing initial data.
   1.117 +      JS::Rooted<JSObject*> array(aJSContext,
   1.118 +                                  JS_NewFloat32Array(aJSContext, mLength));
   1.119 +      if (!array) {
   1.120 +        return false;
   1.121 +      }
   1.122 +      memcpy(JS_GetFloat32ArrayData(array), data, sizeof(float)*mLength);
   1.123 +      mJSChannels[i] = array;
   1.124 +    }
   1.125 +
   1.126 +    mSharedChannels = nullptr;
   1.127 +  }
   1.128 +
   1.129 +  return true;
   1.130 +}
   1.131 +
   1.132 +void
   1.133 +AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
   1.134 +                             uint32_t aStartInChannel, ErrorResult& aRv)
   1.135 +{
   1.136 +  aDestination.ComputeLengthAndData();
   1.137 +
   1.138 +  uint32_t length = aDestination.Length();
   1.139 +  CheckedInt<uint32_t> end = aStartInChannel;
   1.140 +  end += length;
   1.141 +  if (aChannelNumber >= NumberOfChannels() ||
   1.142 +      !end.isValid() || end.value() > mLength) {
   1.143 +    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1.144 +    return;
   1.145 +  }
   1.146 +
   1.147 +  if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
   1.148 +    // The array was probably neutered
   1.149 +    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1.150 +    return;
   1.151 +  }
   1.152 +
   1.153 +  const float* sourceData = mSharedChannels ?
   1.154 +    mSharedChannels->GetData(aChannelNumber) :
   1.155 +    JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]);
   1.156 +  PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
   1.157 +}
   1.158 +
   1.159 +void
   1.160 +AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
   1.161 +                           uint32_t aChannelNumber, uint32_t aStartInChannel,
   1.162 +                           ErrorResult& aRv)
   1.163 +{
   1.164 +  aSource.ComputeLengthAndData();
   1.165 +
   1.166 +  uint32_t length = aSource.Length();
   1.167 +  CheckedInt<uint32_t> end = aStartInChannel;
   1.168 +  end += length;
   1.169 +  if (aChannelNumber >= NumberOfChannels() ||
   1.170 +      !end.isValid() || end.value() > mLength) {
   1.171 +    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1.172 +    return;
   1.173 +  }
   1.174 +
   1.175 +  if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
   1.176 +    // The array was probably neutered
   1.177 +    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   1.178 +    return;
   1.179 +  }
   1.180 +
   1.181 +  if (!RestoreJSChannelData(aJSContext)) {
   1.182 +    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1.183 +    return;
   1.184 +  }
   1.185 +
   1.186 +  PodMove(JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]) + aStartInChannel,
   1.187 +          aSource.Data(), length);
   1.188 +}
   1.189 +
   1.190 +void
   1.191 +AudioBuffer::SetRawChannelContents(JSContext* aJSContext, uint32_t aChannel,
   1.192 +                                   float* aContents)
   1.193 +{
   1.194 +  PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel]), aContents, mLength);
   1.195 +}
   1.196 +
   1.197 +void
   1.198 +AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
   1.199 +                            JS::MutableHandle<JSObject*> aRetval,
   1.200 +                            ErrorResult& aRv)
   1.201 +{
   1.202 +  if (aChannel >= NumberOfChannels()) {
   1.203 +    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.204 +    return;
   1.205 +  }
   1.206 +
   1.207 +  if (!RestoreJSChannelData(aJSContext)) {
   1.208 +    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1.209 +    return;
   1.210 +  }
   1.211 +
   1.212 +  if (mJSChannels[aChannel]) {
   1.213 +    JS::ExposeObjectToActiveJS(mJSChannels[aChannel]);
   1.214 +  }
   1.215 +  aRetval.set(mJSChannels[aChannel]);
   1.216 +}
   1.217 +
   1.218 +static already_AddRefed<ThreadSharedFloatArrayBufferList>
   1.219 +StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
   1.220 +                                                     const nsTArray<JSObject*>& aJSArrays)
   1.221 +{
   1.222 +  nsRefPtr<ThreadSharedFloatArrayBufferList> result =
   1.223 +    new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
   1.224 +  for (uint32_t i = 0; i < aJSArrays.Length(); ++i) {
   1.225 +    JS::Rooted<JSObject*> arrayBuffer(aJSContext,
   1.226 +                                      JS_GetArrayBufferViewBuffer(aJSContext, aJSArrays[i]));
   1.227 +    uint8_t* stolenData = arrayBuffer
   1.228 +                          ? (uint8_t*) JS_StealArrayBufferContents(aJSContext, arrayBuffer)
   1.229 +                          : nullptr;
   1.230 +    if (stolenData) {
   1.231 +      result->SetData(i, stolenData, reinterpret_cast<float*>(stolenData));
   1.232 +    } else {
   1.233 +      return nullptr;
   1.234 +    }
   1.235 +  }
   1.236 +  return result.forget();
   1.237 +}
   1.238 +
   1.239 +ThreadSharedFloatArrayBufferList*
   1.240 +AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
   1.241 +{
   1.242 +  if (!mSharedChannels) {
   1.243 +    for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
   1.244 +      if (mLength != JS_GetTypedArrayLength(mJSChannels[i])) {
   1.245 +        // Probably one of the arrays was neutered
   1.246 +        return nullptr;
   1.247 +      }
   1.248 +    }
   1.249 +
   1.250 +    mSharedChannels =
   1.251 +      StealJSArrayDataIntoThreadSharedFloatArrayBufferList(aJSContext, mJSChannels);
   1.252 +  }
   1.253 +
   1.254 +  return mSharedChannels;
   1.255 +}
   1.256 +
   1.257 +size_t
   1.258 +AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   1.259 +{
   1.260 +  size_t amount = aMallocSizeOf(this);
   1.261 +  amount += mJSChannels.SizeOfExcludingThis(aMallocSizeOf);
   1.262 +  if (mSharedChannels) {
   1.263 +    amount += mSharedChannels->SizeOfExcludingThis(aMallocSizeOf);
   1.264 +  }
   1.265 +  return amount;
   1.266 +}
   1.267 +
   1.268 +}
   1.269 +}

mercurial