RLBoxSoundTouch.cpp (5574B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "mozilla/Assertions.h" 7 #include "RLBoxSoundTouch.h" 8 9 using namespace rlbox; 10 using namespace mozilla; 11 using namespace soundtouch; 12 13 RLBoxSoundTouch::RLBoxSoundTouch() {} 14 15 bool RLBoxSoundTouch::Init() { 16 #ifdef MOZ_WASM_SANDBOXING_SOUNDTOUCH 17 const bool success = mSandbox.create_sandbox( 18 /* shouldAbortOnFailure = */ false, 19 /* custom capacity = */ nullptr, 20 "rlbox_wasm2c_soundtouch"); 21 #else 22 const bool success = true; 23 mSandbox.create_sandbox(); 24 #endif 25 26 if (!success){ 27 return false; 28 } 29 30 mTimeStretcher = mSandbox.invoke_sandbox_function(createSoundTouchObj); 31 32 // Allocate buffer in sandbox to receive samples. 33 mSampleBuffer = mSandbox.malloc_in_sandbox<AudioDataValue>(mSampleBufferSize); 34 MOZ_RELEASE_ASSERT(mSampleBuffer); 35 mCreated = true; 36 return true; 37 } 38 39 RLBoxSoundTouch::~RLBoxSoundTouch() { 40 if (mCreated) { 41 mSandbox.free_in_sandbox(mSampleBuffer); 42 mSandbox.invoke_sandbox_function(destroySoundTouchObj, mTimeStretcher); 43 mTimeStretcher = nullptr; 44 mSandbox.destroy_sandbox(); 45 } 46 } 47 48 void RLBoxSoundTouch::setSampleRate(uint aRate) { 49 mSandbox.invoke_sandbox_function(SetSampleRate, mTimeStretcher, aRate); 50 } 51 52 void RLBoxSoundTouch::setChannels(uint aChannels) { 53 mChannels = aChannels; 54 mSandbox.invoke_sandbox_function(SetChannels, mTimeStretcher, aChannels); 55 } 56 57 void RLBoxSoundTouch::setPitch(double aPitch) { 58 mSandbox.invoke_sandbox_function(SetPitch, mTimeStretcher, aPitch); 59 } 60 61 void RLBoxSoundTouch::setSetting(int aSettingId, int aValue) { 62 mSandbox.invoke_sandbox_function(SetSetting, mTimeStretcher, aSettingId, 63 aValue); 64 } 65 66 void RLBoxSoundTouch::setTempo(double aTempo) { 67 mSandbox.invoke_sandbox_function(SetTempo, mTimeStretcher, aTempo); 68 } 69 70 void RLBoxSoundTouch::setRate(double aRate) { 71 mSandbox.invoke_sandbox_function(SetRate, mTimeStretcher, aRate); 72 } 73 74 uint RLBoxSoundTouch::numChannels() { 75 auto numChannels = mChannels; 76 mSandbox.invoke_sandbox_function(NumChannels, mTimeStretcher) 77 .copy_and_verify([numChannels](auto ch) { 78 MOZ_RELEASE_ASSERT(ch == numChannels, "Number of channels changed"); 79 }); 80 return mChannels; 81 } 82 83 tainted_soundtouch<uint> RLBoxSoundTouch::numSamples() { 84 return mSandbox.invoke_sandbox_function(NumSamples, mTimeStretcher); 85 } 86 87 tainted_soundtouch<uint> RLBoxSoundTouch::numUnprocessedSamples() { 88 return mSandbox.invoke_sandbox_function(NumUnprocessedSamples, 89 mTimeStretcher); 90 } 91 92 void RLBoxSoundTouch::putSamples(const AudioDataValue* aSamples, 93 uint aNumSamples) { 94 const CheckedInt<size_t> nrTotalSamples = numChannels() * aNumSamples; 95 MOZ_RELEASE_ASSERT(nrTotalSamples.isValid(), "Input buffer size overflow"); 96 97 bool copied = false; 98 auto t_aSamples = copy_memory_or_grant_access( 99 mSandbox, aSamples, nrTotalSamples.value(), false, copied); 100 101 mSandbox.invoke_sandbox_function(PutSamples, mTimeStretcher, t_aSamples, 102 aNumSamples); 103 104 if (copied) { 105 mSandbox.free_in_sandbox(t_aSamples); 106 } 107 } 108 109 uint RLBoxSoundTouch::receiveSamples(AudioDataValue* aOutput, 110 uint aMaxSamples) { 111 // Check number of channels. 112 const CheckedInt<uint> channels = numChannels(); 113 // Sanity check max samples. 114 const CheckedInt<uint> maxElements = channels * aMaxSamples; 115 MOZ_RELEASE_ASSERT(maxElements.isValid(), "Max number of elements overflow"); 116 117 // Check mSampleBuffer size, and resize if required. 118 if (mSampleBufferSize < maxElements.value()) { 119 resizeSampleBuffer(maxElements.value()); 120 } 121 122 auto numWrittenSamples = 123 mSandbox 124 .invoke_sandbox_function(ReceiveSamples, mTimeStretcher, 125 mSampleBuffer, aMaxSamples) 126 .copy_and_verify([aMaxSamples](uint written) { 127 MOZ_RELEASE_ASSERT(written <= aMaxSamples, 128 "Number of samples exceeds max samples"); 129 return written; 130 }); 131 132 // Copy received elements from sandbox. 133 if (numWrittenSamples > 0) { 134 const CheckedInt<uint> numCopyElements = channels * numWrittenSamples; 135 136 MOZ_RELEASE_ASSERT(numCopyElements.isValid() && 137 numCopyElements.value() <= maxElements.value(), 138 "Bad number of written elements"); 139 140 // Get pointer to mSampleBuffer. RLBox ensures that we have at least 141 // numCopyElements elements. We are just copying data values out. These 142 // values are untrusted but should only be interpreted as sound samples -- 143 // so should, at worst, just sound weird. 144 auto* mSampleBuffer_ptr = mSampleBuffer.unverified_safe_pointer_because( 145 numCopyElements.value(), 146 "Pointer to buffer is within sandbox and has at least numCopyElements " 147 "elements."); 148 PodCopy(aOutput, mSampleBuffer_ptr, numCopyElements.value()); 149 } 150 151 return numWrittenSamples; 152 } 153 154 void RLBoxSoundTouch::flush() { 155 return mSandbox.invoke_sandbox_function(Flush, mTimeStretcher); 156 } 157 158 void RLBoxSoundTouch::resizeSampleBuffer(uint aNewSize) { 159 mSandbox.free_in_sandbox(mSampleBuffer); 160 mSampleBufferSize = aNewSize; 161 mSampleBuffer = mSandbox.malloc_in_sandbox<AudioDataValue>(aNewSize); 162 MOZ_RELEASE_ASSERT(mSampleBuffer); 163 }