summaryrefslogtreecommitdiffstats
path: root/media/libsoundtouch/src/RLBoxSoundTouch.cpp
blob: 2c5fca2bad64ccda3f0c65d7a58fcb58be8e6ce5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/Assertions.h"
#include "RLBoxSoundTouch.h"

using namespace rlbox;
using namespace mozilla;
using namespace soundtouch;

RLBoxSoundTouch::RLBoxSoundTouch() {
#ifdef MOZ_WASM_SANDBOXING_SOUNDTOUCH
  mSandbox.create_sandbox(true /* infallible */);
#else
  mSandbox.create_sandbox();
#endif
  mTimeStretcher = mSandbox.invoke_sandbox_function(createSoundTouchObj);

  // Allocate buffer in sandbox to receive samples.
  mSampleBuffer = mSandbox.malloc_in_sandbox<AudioDataValue>(mSampleBufferSize);
  MOZ_RELEASE_ASSERT(mSampleBuffer);
}

RLBoxSoundTouch::~RLBoxSoundTouch() {
  mSandbox.free_in_sandbox(mSampleBuffer);
  mSandbox.invoke_sandbox_function(destroySoundTouchObj, mTimeStretcher);
  mTimeStretcher = nullptr;
  mSandbox.destroy_sandbox();
}

void RLBoxSoundTouch::setSampleRate(uint aRate) {
  mSandbox.invoke_sandbox_function(SetSampleRate, mTimeStretcher, aRate);
}

void RLBoxSoundTouch::setChannels(uint aChannels) {
  mChannels = aChannels;
  mSandbox.invoke_sandbox_function(SetChannels, mTimeStretcher, aChannels);
}

void RLBoxSoundTouch::setPitch(double aPitch) {
  mSandbox.invoke_sandbox_function(SetPitch, mTimeStretcher, aPitch);
}

void RLBoxSoundTouch::setSetting(int aSettingId, int aValue) {
  mSandbox.invoke_sandbox_function(SetSetting, mTimeStretcher, aSettingId,
                                   aValue);
}

void RLBoxSoundTouch::setTempo(double aTempo) {
  mSandbox.invoke_sandbox_function(SetTempo, mTimeStretcher, aTempo);
}

void RLBoxSoundTouch::setRate(double aRate) {
  mSandbox.invoke_sandbox_function(SetRate, mTimeStretcher, aRate);
}

uint RLBoxSoundTouch::numChannels() {
  auto numChannels = mChannels;
  mSandbox.invoke_sandbox_function(NumChannels, mTimeStretcher)
      .copy_and_verify([numChannels](auto ch) {
        MOZ_RELEASE_ASSERT(ch == numChannels, "Number of channels changed");
      });
  return mChannels;
}

tainted_soundtouch<uint> RLBoxSoundTouch::numSamples() {
  return mSandbox.invoke_sandbox_function(NumSamples, mTimeStretcher);
}

tainted_soundtouch<uint> RLBoxSoundTouch::numUnprocessedSamples() {
  return mSandbox.invoke_sandbox_function(NumUnprocessedSamples,
                                          mTimeStretcher);
}

void RLBoxSoundTouch::putSamples(const AudioDataValue* aSamples,
                                 uint aNumSamples) {
  const CheckedInt<size_t> nrTotalSamples = numChannels() * aNumSamples;
  MOZ_RELEASE_ASSERT(nrTotalSamples.isValid(), "Input buffer size overflow");

  bool copied = false;
  auto t_aSamples = copy_memory_or_grant_access(
      mSandbox, aSamples, nrTotalSamples.value(), false, copied);

  mSandbox.invoke_sandbox_function(PutSamples, mTimeStretcher, t_aSamples,
                                   aNumSamples);

  if (copied) {
    mSandbox.free_in_sandbox(t_aSamples);
  }
}

uint RLBoxSoundTouch::receiveSamples(AudioDataValue* aOutput,
                                     uint aMaxSamples) {
  // Check number of channels.
  const CheckedInt<uint> channels = numChannels();
  // Sanity check max samples.
  const CheckedInt<uint> maxElements = channels * aMaxSamples;
  MOZ_RELEASE_ASSERT(maxElements.isValid(), "Max number of elements overflow");

  // Check mSampleBuffer size, and resize if required.
  if (mSampleBufferSize < maxElements.value()) {
    resizeSampleBuffer(maxElements.value());
  }

  auto numWrittenSamples =
      mSandbox
          .invoke_sandbox_function(ReceiveSamples, mTimeStretcher,
                                   mSampleBuffer, aMaxSamples)
          .copy_and_verify([aMaxSamples](uint written) {
            MOZ_RELEASE_ASSERT(written <= aMaxSamples,
                               "Number of samples exceeds max samples");
            return written;
          });

  // Copy received elements from sandbox.
  if (numWrittenSamples > 0) {
    const CheckedInt<uint> numCopyElements = channels * numWrittenSamples;

    MOZ_RELEASE_ASSERT(numCopyElements.isValid() &&
                           numCopyElements.value() <= maxElements.value(),
                       "Bad number of written elements");

    // Get pointer to mSampleBuffer. RLBox ensures that we have at least
    // numCopyElements elements.  We are just copying data values out. These
    // values are untrusted but should only be interpreted as sound samples --
    // so should, at worst, just sound weird.
    auto* mSampleBuffer_ptr = mSampleBuffer.unverified_safe_pointer_because(
        numCopyElements.value(),
        "Pointer to buffer is within sandbox and has at least numCopyElements "
        "elements.");
    PodCopy(aOutput, mSampleBuffer_ptr, numCopyElements.value());
  }

  return numWrittenSamples;
}

void RLBoxSoundTouch::flush() {
  return mSandbox.invoke_sandbox_function(Flush, mTimeStretcher);
}

void RLBoxSoundTouch::resizeSampleBuffer(uint aNewSize) {
  mSandbox.free_in_sandbox(mSampleBuffer);
  mSampleBufferSize = aNewSize;
  mSampleBuffer = mSandbox.malloc_in_sandbox<AudioDataValue>(aNewSize);
  MOZ_RELEASE_ASSERT(mSampleBuffer);
}