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 "PeriodicWave.h"
8 #include "AudioContext.h"
9 #include "mozilla/dom/PeriodicWaveBinding.h"
10 
11 namespace mozilla::dom {
12 
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PeriodicWave,mContext)13 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PeriodicWave, mContext)
14 
15 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PeriodicWave, AddRef)
16 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PeriodicWave, Release)
17 
18 PeriodicWave::PeriodicWave(AudioContext* aContext, const float* aRealData,
19                            const uint32_t aRealSize, const float* aImagData,
20                            const uint32_t aImagSize,
21                            const bool aDisableNormalization, ErrorResult& aRv)
22     : mContext(aContext), mDisableNormalization(aDisableNormalization) {
23   if (aRealData && aImagData && aRealSize != aImagSize) {
24     aRv.ThrowIndexSizeError("\"real\" and \"imag\" are different in length");
25     return;
26   }
27 
28   uint32_t length = 0;
29   if (aRealData) {
30     length = aRealSize;
31   } else if (aImagData) {
32     length = aImagSize;
33   } else {
34     // If nothing has been passed, this PeriodicWave will be a sine wave: 2
35     // elements for each array, the second imaginary component set to 1.0.
36     length = 2;
37   }
38 
39   if (length < 2) {
40     aRv.ThrowIndexSizeError(
41         "\"real\" and \"imag\" must have a length of "
42         "at least 2");
43     return;
44   }
45 
46   MOZ_ASSERT(aContext);
47   MOZ_ASSERT((aRealData || aImagData) || length == 2);
48 
49   // Caller should have checked this and thrown.
50   MOZ_ASSERT(length >= 2);
51   mCoefficients.mDuration = length;
52 
53   // Copy coefficient data.
54   // The SharedBuffer and two arrays share a single allocation.
55   CheckedInt<size_t> bufferSize(sizeof(float));
56   bufferSize *= length;
57   bufferSize *= 2;
58   RefPtr<SharedBuffer> buffer = SharedBuffer::Create(bufferSize, fallible);
59   if (!buffer) {
60     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
61     return;
62   }
63 
64   auto data = static_cast<float*>(buffer->Data());
65   mCoefficients.mBuffer = std::move(buffer);
66 
67   if (!aRealData && !aImagData) {
68     PodZero(data, length);
69     mCoefficients.mChannelData.AppendElement(data);
70     data += length;
71     data[0] = 0.0f;
72     data[1] = 1.0f;
73     mCoefficients.mChannelData.AppendElement(data);
74   } else {
75     if (aRealData) {
76       PodCopy(data, aRealData, length);
77     } else {
78       PodZero(data, length);
79     }
80     mCoefficients.mChannelData.AppendElement(data);
81 
82     data += length;
83     if (aImagData) {
84       PodCopy(data, aImagData, length);
85     } else {
86       PodZero(data, length);
87     }
88     mCoefficients.mChannelData.AppendElement(data);
89   }
90   mCoefficients.mVolume = 1.0f;
91   mCoefficients.mBufferFormat = AUDIO_FORMAT_FLOAT32;
92 }
93 
94 /* static */
Constructor(const GlobalObject & aGlobal,AudioContext & aAudioContext,const PeriodicWaveOptions & aOptions,ErrorResult & aRv)95 already_AddRefed<PeriodicWave> PeriodicWave::Constructor(
96     const GlobalObject& aGlobal, AudioContext& aAudioContext,
97     const PeriodicWaveOptions& aOptions, ErrorResult& aRv) {
98   const float* realData;
99   const float* imagData;
100   uint32_t realSize;
101   uint32_t imagSize;
102 
103   if (aOptions.mReal.WasPassed()) {
104     realData = aOptions.mReal.Value().Elements();
105     realSize = aOptions.mReal.Value().Length();
106   } else {
107     realData = nullptr;
108     realSize = 0;
109   }
110 
111   if (aOptions.mImag.WasPassed()) {
112     imagData = aOptions.mImag.Value().Elements();
113     imagSize = aOptions.mImag.Value().Length();
114   } else {
115     imagData = nullptr;
116     imagSize = 0;
117   }
118 
119   RefPtr<PeriodicWave> wave =
120       new PeriodicWave(&aAudioContext, realData, realSize, imagData, imagSize,
121                        aOptions.mDisableNormalization, aRv);
122   if (aRv.Failed()) {
123     return nullptr;
124   }
125 
126   return wave.forget();
127 }
128 
SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const129 size_t PeriodicWave::SizeOfExcludingThisIfNotShared(
130     MallocSizeOf aMallocSizeOf) const {
131   // Not owned:
132   // - mContext
133   size_t amount = 0;
134   amount += mCoefficients.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
135 
136   return amount;
137 }
138 
SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const139 size_t PeriodicWave::SizeOfIncludingThisIfNotShared(
140     MallocSizeOf aMallocSizeOf) const {
141   return aMallocSizeOf(this) + SizeOfExcludingThisIfNotShared(aMallocSizeOf);
142 }
143 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)144 JSObject* PeriodicWave::WrapObject(JSContext* aCx,
145                                    JS::Handle<JSObject*> aGivenProto) {
146   return PeriodicWave_Binding::Wrap(aCx, this, aGivenProto);
147 }
148 
149 }  // namespace mozilla::dom
150