Skip to content

Commit 8f9591d

Browse files
incorporate amplitude limits
1 parent 1b84629 commit 8f9591d

File tree

1 file changed

+27
-24
lines changed

1 file changed

+27
-24
lines changed

cedargrove_wavebuilder.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ class WaveBuilder:
5555
a member of the ``WaveShape`` class (type: string). The floating point
5656
oscillator frequency is defined as either a frequency in Hertz or
5757
overtone ratio based on the fundamental (lowest) frequency. The
58-
amplitude is a floating point value between 0.0 and 1.0 (inclusive).
59-
Amplitude values outside the range will produce overflow effects
60-
that may be unwanted. No default.
58+
amplitude is a floating point value between -1.0 and 1.0 (inclusive).
59+
Amplitude values less than zero will flip the phase of the resultant
60+
oscillator waveform 180 degrees. No default.
6161
:param integer table_length: The number of samples contained in the
6262
resultant waveform table. No default.
6363
:param integer sample_max: The maximum positive value of a sample,
@@ -86,7 +86,7 @@ def __init__(
8686
):
8787
self._oscillators = oscillators
8888
self._table_length = table_length
89-
self._sample_max = sample_max
89+
self._sample_max = int(sample_max)
9090
self._lambda_factor = lambda_factor
9191
self._loop_smoothing = loop_smoothing
9292
self._debug = debug
@@ -118,11 +118,11 @@ def table_length(self, new_table_length):
118118
def sample_max(self):
119119
"""The maximum positive value of a sample, limited to a signed
120120
16-bit integer value (0 to 32767)."""
121-
return self._sample_max
121+
return int(self._sample_max)
122122

123123
@sample_max.setter
124124
def sample_max(self, new_sample_max=32767):
125-
self._sample_max = new_sample_max
125+
self._sample_max = int(new_sample_max)
126126
self._update_table()
127127

128128
@property
@@ -176,12 +176,12 @@ def summed_amplitude(self):
176176
# pylint: disable=unused-argument
177177
def _noise_wave(self, ratio, amplitude):
178178
"""Returns a sample array with a noise waveform adjusted to a specified amplitude."""
179+
amp_factor = abs(
180+
min(int(round(self._sample_max * amplitude, 0)), self._sample_max)
181+
)
179182
_temporary = np.array(
180183
[
181-
random.randint(
182-
int(-self._sample_max * amplitude),
183-
int(self._sample_max * amplitude),
184-
)
184+
random.randint(-amp_factor, amp_factor)
185185
for _ in range(self._table_length)
186186
],
187187
dtype=np.int16,
@@ -191,6 +191,7 @@ def _noise_wave(self, ratio, amplitude):
191191
def _saw_wave(self, ratio, amplitude):
192192
"""Returns a waveform array with a saw wave waveform proportional
193193
to the frequency ratio and adjusted to a specified amplitude."""
194+
amp_factor = min(int(round(self._sample_max * amplitude, 0)), self._sample_max)
194195
_temporary = np.array([], dtype=np.int16) # Create a zero-length array
195196

196197
# Calculate the array length and subtract the initial zero element
@@ -203,13 +204,13 @@ def _saw_wave(self, ratio, amplitude):
203204
_temporary,
204205
np.linspace(
205206
0,
206-
int(self._sample_max * amplitude),
207+
int(amp_factor),
207208
half_lambda - 1,
208209
dtype=np.int16,
209210
),
210211
np.array([0], dtype=np.int16),
211212
np.linspace(
212-
int(-self._sample_max * amplitude),
213+
int(-amp_factor),
213214
0,
214215
half_lambda - 1,
215216
dtype=np.int16,
@@ -224,6 +225,7 @@ def _saw_wave(self, ratio, amplitude):
224225
def _sine_wave(self, ratio, amplitude):
225226
"""Returns a waveform array with a sine wave waveform proportional
226227
to the frequency ratio and adjusted to a specified amplitude."""
228+
amp_factor = min(int(round(self._sample_max * amplitude, 0)), self._sample_max)
227229
_temporary = np.array(
228230
np.sin(
229231
np.linspace(
@@ -233,15 +235,15 @@ def _sine_wave(self, ratio, amplitude):
233235
endpoint=False,
234236
)
235237
)
236-
* amplitude
237-
* self._sample_max,
238+
* amp_factor,
238239
dtype=np.int16,
239240
)
240241
return _temporary
241242

242243
def _square_wave(self, ratio, amplitude):
243244
"""Returns a waveform array with a square wave waveform proportional
244245
to the frequency ratio and adjusted to a specified amplitude."""
246+
amp_factor = min(int(round(self._sample_max * amplitude, 0)), self._sample_max)
245247
# Create a zero-length temporary array
246248
_temporary = np.array([], dtype=np.int16)
247249

@@ -254,11 +256,9 @@ def _square_wave(self, ratio, amplitude):
254256
(
255257
_temporary,
256258
np.array([0], dtype=np.int16),
257-
np.ones(half_lambda - 1, dtype=np.int16)
258-
* int(self._sample_max * amplitude),
259+
np.ones(half_lambda - 1, dtype=np.int16) * int(amp_factor),
259260
np.array([0], dtype=np.int16),
260-
np.ones(half_lambda - 1, dtype=np.int16)
261-
* int(-self._sample_max * amplitude),
261+
np.ones(half_lambda - 1, dtype=np.int16) * int(-amp_factor),
262262
)
263263
)
264264

@@ -269,14 +269,15 @@ def _square_wave(self, ratio, amplitude):
269269
def _triangle_wave(self, ratio, amplitude):
270270
"""Returns a waveform array with a triangle wave waveform proportional
271271
to the frequency ratio and adjusted to a specified amplitude."""
272+
amp_factor = min(int(round(self._sample_max * amplitude, 0)), self._sample_max)
272273
# Create a zero-length temporary array
273274
_temporary = np.array([], dtype=np.int16)
274275

275276
# Calculate the sample length of one-quarter lambda
276277
quarter_lambda = int((self._table_length / (self._lambda_factor * 4)) / ratio)
277278

278279
# Calculate a one-step increment for even quarter-lambda segments
279-
increment = int(self._sample_max / quarter_lambda * amplitude)
280+
increment = int(amp_factor / quarter_lambda)
280281

281282
# Build the waveform array from a full-lambda waveform
282283
while len(_temporary) < self._table_length:
@@ -285,24 +286,24 @@ def _triangle_wave(self, ratio, amplitude):
285286
_temporary,
286287
np.linspace(
287288
0,
288-
int(round(self._sample_max * amplitude, 0)),
289+
amp_factor,
289290
quarter_lambda,
290291
dtype=np.int16,
291292
),
292293
np.linspace(
293-
int(round(self._sample_max * amplitude, 0)) - increment,
294+
amp_factor - increment,
294295
0,
295296
quarter_lambda,
296297
dtype=np.int16,
297298
),
298299
np.linspace(
299300
0 - increment,
300-
int(round(-self._sample_max * amplitude, 0)),
301+
-amp_factor,
301302
quarter_lambda,
302303
dtype=np.int16,
303304
),
304305
np.linspace(
305-
int(round(-self._sample_max * amplitude, 0)) + increment,
306+
-amp_factor + increment,
306307
0 - increment,
307308
quarter_lambda,
308309
dtype=np.int16,
@@ -322,7 +323,9 @@ def _update_table(self):
322323
(t, freq / fundamental_frequency, a) for t, freq, a in self._oscillators
323324
]
324325

325-
self._summed_amplitude = sum([osc[2] for osc in self._oscillators])
326+
self._summed_amplitude = sum([abs(osc[2]) for osc in self._oscillators])
327+
if self._summed_amplitude > 1.0:
328+
raise ValueError(f"Summed amplitude of oscillators exceeds 1.0.")
326329

327330
# Test each oscillator ratio to confirm that table_length has sufficient resolution
328331
for overtone in self._oscillators:

0 commit comments

Comments
 (0)