tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WaveShaperNode.cpp (12319B)


      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 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "WaveShaperNode.h"
      8 
      9 #include "AlignmentUtils.h"
     10 #include "AudioNode.h"
     11 #include "AudioNodeEngine.h"
     12 #include "AudioNodeTrack.h"
     13 #include "Tracing.h"
     14 #include "mozilla/PodOperations.h"
     15 #include "mozilla/dom/WaveShaperNodeBinding.h"
     16 
     17 namespace mozilla::dom {
     18 
     19 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
     20 
     21 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
     22  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     23 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     24 
     25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode)
     26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     27 
     28 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode)
     29  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     30 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     31 
     32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WaveShaperNode)
     33 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
     34 
     35 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
     36 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
     37 
     38 static uint32_t ValueOf(OverSampleType aType) {
     39  switch (aType) {
     40    case OverSampleType::None:
     41      return 1;
     42    case OverSampleType::_2x:
     43      return 2;
     44    case OverSampleType::_4x:
     45      return 4;
     46    default:
     47      MOZ_ASSERT_UNREACHABLE("We should never reach here");
     48      return 1;
     49  }
     50 }
     51 
     52 class Resampler final {
     53 public:
     54  Resampler()
     55      : mType(OverSampleType::None),
     56        mUpSampler(nullptr),
     57        mDownSampler(nullptr),
     58        mChannels(0),
     59        mSampleRate(0) {}
     60 
     61  ~Resampler() { Destroy(); }
     62 
     63  void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType) {
     64    if (aChannels == mChannels && aSampleRate == mSampleRate &&
     65        aType == mType) {
     66      return;
     67    }
     68 
     69    mChannels = aChannels;
     70    mSampleRate = aSampleRate;
     71    mType = aType;
     72 
     73    Destroy();
     74 
     75    if (aType == OverSampleType::None) {
     76      mBuffer.Clear();
     77      return;
     78    }
     79 
     80    mUpSampler = speex_resampler_init(aChannels, aSampleRate,
     81                                      aSampleRate * ValueOf(aType),
     82                                      SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
     83    mDownSampler =
     84        speex_resampler_init(aChannels, aSampleRate * ValueOf(aType),
     85                             aSampleRate, SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
     86    mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE * ValueOf(aType));
     87  }
     88 
     89  float* UpSample(uint32_t aChannel, const float* aInputData,
     90                  uint32_t aBlocks) {
     91    uint32_t inSamples = WEBAUDIO_BLOCK_SIZE;
     92    uint32_t outSamples = WEBAUDIO_BLOCK_SIZE * aBlocks;
     93    float* outputData = mBuffer.Elements();
     94 
     95    MOZ_ASSERT(mBuffer.Length() == outSamples);
     96 
     97    WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel, aInputData,
     98                                         &inSamples, outputData, &outSamples);
     99 
    100    MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE &&
    101               outSamples == WEBAUDIO_BLOCK_SIZE * aBlocks);
    102 
    103    return outputData;
    104  }
    105 
    106  void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks) {
    107    uint32_t inSamples = WEBAUDIO_BLOCK_SIZE * aBlocks;
    108    uint32_t outSamples = WEBAUDIO_BLOCK_SIZE;
    109    const float* inputData = mBuffer.Elements();
    110 
    111    MOZ_ASSERT(mBuffer.Length() == inSamples);
    112 
    113    WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel, inputData,
    114                                         &inSamples, aOutputData, &outSamples);
    115 
    116    MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE * aBlocks &&
    117               outSamples == WEBAUDIO_BLOCK_SIZE);
    118  }
    119 
    120  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
    121    size_t amount = 0;
    122    // Future: properly measure speex memory
    123    amount += aMallocSizeOf(mUpSampler);
    124    amount += aMallocSizeOf(mDownSampler);
    125    amount += mBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
    126    return amount;
    127  }
    128 
    129 private:
    130  void Destroy() {
    131    if (mUpSampler) {
    132      speex_resampler_destroy(mUpSampler);
    133      mUpSampler = nullptr;
    134    }
    135    if (mDownSampler) {
    136      speex_resampler_destroy(mDownSampler);
    137      mDownSampler = nullptr;
    138    }
    139  }
    140 
    141 private:
    142  OverSampleType mType;
    143  SpeexResamplerState* mUpSampler;
    144  SpeexResamplerState* mDownSampler;
    145  uint32_t mChannels;
    146  TrackRate mSampleRate;
    147  nsTArray<float> mBuffer;
    148 };
    149 
    150 class WaveShaperNodeEngine final : public AudioNodeEngine {
    151 public:
    152  explicit WaveShaperNodeEngine(AudioNode* aNode)
    153      : AudioNodeEngine(aNode), mType(OverSampleType::None) {}
    154 
    155  enum Parameters { TYPE };
    156 
    157  void SetRawArrayData(nsTArray<float>&& aCurve) override {
    158    mCurve = std::move(aCurve);
    159  }
    160 
    161  void SetInt32Parameter(uint32_t aIndex, int32_t aValue) override {
    162    switch (aIndex) {
    163      case TYPE:
    164        mType = static_cast<OverSampleType>(aValue);
    165        break;
    166      default:
    167        NS_ERROR("Bad WaveShaperNode Int32Parameter");
    168    }
    169  }
    170 
    171  template <uint32_t blocks>
    172  void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer) {
    173    for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE * blocks; ++j) {
    174      // Index into the curve array based on the amplitude of the
    175      // incoming signal by using an amplitude range of [-1, 1] and
    176      // performing a linear interpolation of the neighbor values.
    177      float index = (mCurve.Length() - 1) * (aInputBuffer[j] + 1.0f) / 2.0f;
    178      if (index < 0.0f) {
    179        aOutputBuffer[j] = mCurve[0];
    180      } else {
    181        int32_t indexLower = index;
    182        if (static_cast<uint32_t>(indexLower) >= mCurve.Length() - 1) {
    183          aOutputBuffer[j] = mCurve[mCurve.Length() - 1];
    184        } else {
    185          uint32_t indexHigher = indexLower + 1;
    186          float interpolationFactor = index - indexLower;
    187          aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
    188                             interpolationFactor * mCurve[indexHigher];
    189        }
    190      }
    191    }
    192  }
    193 
    194  void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
    195                    const AudioBlock& aInput, AudioBlock* aOutput,
    196                    bool* aFinished) override {
    197    TRACE("WaveShaperNodeEngine::ProcessBlock");
    198 
    199    uint32_t channelCount = aInput.ChannelCount();
    200    if (!mCurve.Length()) {
    201      // Optimize the case where we don't have a curve buffer
    202      *aOutput = aInput;
    203      return;
    204    }
    205 
    206    // If the input is null, check to see if non-null output will be produced
    207    bool nullInput = false;
    208    if (channelCount == 0) {
    209      float index = (mCurve.Length() - 1) * 0.5;
    210      uint32_t indexLower = index;
    211      uint32_t indexHigher = indexLower + 1;
    212      float interpolationFactor = index - indexLower;
    213      if ((1.0f - interpolationFactor) * mCurve[indexLower] +
    214              interpolationFactor * mCurve[indexHigher] ==
    215          0.0) {
    216        *aOutput = aInput;
    217        return;
    218      }
    219      nullInput = true;
    220      channelCount = 1;
    221    }
    222 
    223    aOutput->AllocateChannels(channelCount);
    224    for (uint32_t i = 0; i < channelCount; ++i) {
    225      const float* inputSamples;
    226      float scaledInput[WEBAUDIO_BLOCK_SIZE + 4];
    227      float* alignedScaledInput = ALIGNED16(scaledInput);
    228      ASSERT_ALIGNED16(alignedScaledInput);
    229      if (!nullInput) {
    230        if (aInput.mVolume != 1.0f) {
    231          AudioBlockCopyChannelWithScale(
    232              static_cast<const float*>(aInput.mChannelData[i]), aInput.mVolume,
    233              alignedScaledInput);
    234          inputSamples = alignedScaledInput;
    235        } else {
    236          inputSamples = static_cast<const float*>(aInput.mChannelData[i]);
    237        }
    238      } else {
    239        PodZero(alignedScaledInput, WEBAUDIO_BLOCK_SIZE);
    240        inputSamples = alignedScaledInput;
    241      }
    242      float* outputBuffer = aOutput->ChannelFloatsForWrite(i);
    243      float* sampleBuffer;
    244 
    245      switch (mType) {
    246        case OverSampleType::None:
    247          mResampler.Reset(channelCount, aTrack->mSampleRate,
    248                           OverSampleType::None);
    249          ProcessCurve<1>(inputSamples, outputBuffer);
    250          break;
    251        case OverSampleType::_2x:
    252          mResampler.Reset(channelCount, aTrack->mSampleRate,
    253                           OverSampleType::_2x);
    254          sampleBuffer = mResampler.UpSample(i, inputSamples, 2);
    255          ProcessCurve<2>(sampleBuffer, sampleBuffer);
    256          mResampler.DownSample(i, outputBuffer, 2);
    257          break;
    258        case OverSampleType::_4x:
    259          mResampler.Reset(channelCount, aTrack->mSampleRate,
    260                           OverSampleType::_4x);
    261          sampleBuffer = mResampler.UpSample(i, inputSamples, 4);
    262          ProcessCurve<4>(sampleBuffer, sampleBuffer);
    263          mResampler.DownSample(i, outputBuffer, 4);
    264          break;
    265        default:
    266          MOZ_ASSERT_UNREACHABLE("We should never reach here");
    267      }
    268    }
    269  }
    270 
    271  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
    272    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
    273    amount += mCurve.ShallowSizeOfExcludingThis(aMallocSizeOf);
    274    amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
    275    return amount;
    276  }
    277 
    278  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
    279    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    280  }
    281 
    282 private:
    283  nsTArray<float> mCurve;
    284  OverSampleType mType;
    285  Resampler mResampler;
    286 };
    287 
    288 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
    289    : AudioNode(aContext, 2, ChannelCountMode::Max,
    290                ChannelInterpretation::Speakers),
    291      mType(OverSampleType::None) {
    292  WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
    293  mTrack = AudioNodeTrack::Create(
    294      aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
    295 }
    296 
    297 /* static */
    298 already_AddRefed<WaveShaperNode> WaveShaperNode::Create(
    299    AudioContext& aAudioContext, const WaveShaperOptions& aOptions,
    300    ErrorResult& aRv) {
    301  RefPtr<WaveShaperNode> audioNode = new WaveShaperNode(&aAudioContext);
    302 
    303  audioNode->Initialize(aOptions, aRv);
    304  if (NS_WARN_IF(aRv.Failed())) {
    305    return nullptr;
    306  }
    307 
    308  if (aOptions.mCurve.WasPassed()) {
    309    audioNode->SetCurveInternal(aOptions.mCurve.Value(), aRv);
    310    if (NS_WARN_IF(aRv.Failed())) {
    311      return nullptr;
    312    }
    313  }
    314 
    315  audioNode->SetOversample(aOptions.mOversample);
    316  return audioNode.forget();
    317 }
    318 
    319 JSObject* WaveShaperNode::WrapObject(JSContext* aCx,
    320                                     JS::Handle<JSObject*> aGivenProto) {
    321  return WaveShaperNode_Binding::Wrap(aCx, this, aGivenProto);
    322 }
    323 
    324 void WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve,
    325                              ErrorResult& aRv) {
    326  // Let's purge the cached value for the curve attribute.
    327  WaveShaperNode_Binding::ClearCachedCurveValue(this);
    328 
    329  if (aCurve.IsNull()) {
    330    CleanCurveInternal();
    331    return;
    332  }
    333 
    334  nsTArray<float> curve;
    335  if (!aCurve.Value().AppendDataTo(curve)) {
    336    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    337    return;
    338  }
    339 
    340  SetCurveInternal(curve, aRv);
    341 }
    342 
    343 void WaveShaperNode::SetCurveInternal(const nsTArray<float>& aCurve,
    344                                      ErrorResult& aRv) {
    345  if (aCurve.Length() < 2) {
    346    aRv.ThrowInvalidStateError("Must have at least two entries");
    347    return;
    348  }
    349 
    350  mCurve = aCurve.Clone();
    351  SendCurveToTrack();
    352 }
    353 
    354 void WaveShaperNode::CleanCurveInternal() {
    355  mCurve.Clear();
    356  SendCurveToTrack();
    357 }
    358 
    359 void WaveShaperNode::SendCurveToTrack() {
    360  AudioNodeTrack* ns = mTrack;
    361  MOZ_ASSERT(ns, "Why don't we have a track here?");
    362 
    363  nsTArray<float> copyCurve(mCurve.Clone());
    364  ns->SetRawArrayData(std::move(copyCurve));
    365 }
    366 
    367 void WaveShaperNode::GetCurve(JSContext* aCx,
    368                              JS::MutableHandle<JSObject*> aRetval,
    369                              ErrorResult& aError) {
    370  // Let's return a null value if the list is empty.
    371  if (mCurve.IsEmpty()) {
    372    aRetval.set(nullptr);
    373    return;
    374  }
    375 
    376  MOZ_ASSERT(mCurve.Length() >= 2);
    377  JSObject* curve = Float32Array::Create(aCx, this, mCurve, aError);
    378  if (aError.Failed()) {
    379    return;
    380  }
    381  aRetval.set(curve);
    382 }
    383 
    384 void WaveShaperNode::SetOversample(OverSampleType aType) {
    385  mType = aType;
    386  SendInt32ParameterToTrack(WaveShaperNodeEngine::TYPE,
    387                            static_cast<int32_t>(aType));
    388 }
    389 
    390 }  // namespace mozilla::dom