Skip to content

Commit 856855a

Browse files
committed
Update stereo_sound and sound bundle functions to include better type-checking
1 parent bdb0a8f commit 856855a

File tree

3 files changed

+295
-160
lines changed

3 files changed

+295
-160
lines changed

src/bundles/sound/src/functions.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,23 @@ function save(audioBuffer: AudioBuffer) {
133133
}
134134

135135
/**
136-
* Throws an error if any of the provided parameters are outside of their
137-
* acceptable values.
136+
* Throws an exception if duration is not a number or if
137+
* number is negative
138138
*/
139-
function validateSound(func_name: string, wave: Wave, duration: number) {
139+
function validateDuration(func_name: string, duration: unknown): asserts duration is number {
140140
if (typeof duration !== 'number') {
141141
throw new Error(`${func_name} expects a number for duration, got ${duration}`);
142142
}
143143

144144
if (duration < 0) {
145145
throw new Error(`${func_name}: Sound duration must be greater than or equal to 0`);
146146
}
147+
}
147148

149+
/**
150+
* Throws an exception if wave is not a function
151+
*/
152+
function validateWave(func_name: string, wave: unknown): asserts wave is Wave {
148153
if (typeof wave !== 'function') {
149154
throw new Error(`${func_name} expects a wave, got ${wave}`);
150155
}
@@ -266,7 +271,9 @@ export function record_for(duration: number, buffer: number): () => Sound {
266271
* @example const s = make_sound(t => Math_sin(2 * Math_PI * 440 * t), 5);
267272
*/
268273
export function make_sound(wave: Wave, duration: number): Sound {
269-
validateSound(make_sound.name, wave, duration);
274+
validateDuration(make_sound.name, duration);
275+
validateWave(make_sound.name, wave);
276+
270277
return pair((t: number) => (t >= duration ? 0 : wave(t)), duration);
271278
}
272279

@@ -316,7 +323,9 @@ export function is_sound(x: unknown): x is Sound {
316323
* @example play_wave(t => math_sin(t * 3000), 5);
317324
*/
318325
export function play_wave(wave: Wave, duration: number): Sound {
319-
validateSound(play_wave.name, wave, duration);
326+
validateDuration(play_wave.name, duration);
327+
validateWave(play_wave.name, wave);
328+
320329
return play(make_sound(wave, duration));
321330
}
322331

@@ -486,6 +495,7 @@ export function stop(): void {
486495
* @category Primitive
487496
*/
488497
export function noise_sound(duration: number): Sound {
498+
validateDuration(noise_sound.name, duration);
489499
return make_sound((_t) => Math.random() * 2 - 1, duration);
490500
}
491501

@@ -498,6 +508,7 @@ export function noise_sound(duration: number): Sound {
498508
* @category Primitive
499509
*/
500510
export function silence_sound(duration: number): Sound {
511+
validateDuration(silence_sound.name, duration);
501512
return make_sound((_t) => 0, duration);
502513
}
503514

@@ -511,6 +522,7 @@ export function silence_sound(duration: number): Sound {
511522
* @category Primitive
512523
*/
513524
export function sine_sound(freq: number, duration: number): Sound {
525+
validateDuration(sine_sound.name, duration);
514526
return make_sound((t) => Math.sin(2 * Math.PI * t * freq), duration);
515527
}
516528

@@ -524,6 +536,7 @@ export function sine_sound(freq: number, duration: number): Sound {
524536
* @category Primitive
525537
*/
526538
export function square_sound(f: number, duration: number): Sound {
539+
validateDuration(square_sound.name, duration);
527540
function fourier_expansion_square(t: number) {
528541
let answer = 0;
529542
for (let i = 1; i <= fourier_expansion_level; i += 1) {
@@ -547,6 +560,7 @@ export function square_sound(f: number, duration: number): Sound {
547560
* @category Primitive
548561
*/
549562
export function triangle_sound(freq: number, duration: number): Sound {
563+
validateDuration(triangle_sound.name, duration);
550564
function fourier_expansion_triangle(t: number) {
551565
let answer = 0;
552566
for (let i = 0; i < fourier_expansion_level; i += 1) {
@@ -572,6 +586,7 @@ export function triangle_sound(freq: number, duration: number): Sound {
572586
* @category Primitive
573587
*/
574588
export function sawtooth_sound(freq: number, duration: number): Sound {
589+
validateDuration(sawtooth_sound.name, duration);
575590
function fourier_expansion_sawtooth(t: number) {
576591
let answer = 0;
577592
for (let i = 1; i <= fourier_expansion_level; i += 1) {
@@ -673,7 +688,7 @@ export function adsr(
673688
sustain_level: number,
674689
release_ratio: number
675690
): SoundTransformer {
676-
return (sound) => {
691+
return sound => {
677692
const wave = get_wave(sound);
678693
const duration = get_duration(sound);
679694
const attack_time = duration * attack_ratio;

src/bundles/stereo_sound/src/__tests__/index.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,70 @@ describe(funcs.consecutively, () => {
125125
expect(points[2]).toEqual([0, 2]);
126126
});
127127
});
128+
129+
describe(funcs.make_sound, () => {
130+
it('Should error gracefully when duration is negative', () => {
131+
expect(() => funcs.make_sound(() => 0, -1))
132+
.toThrow('make_sound: Sound duration must be greater than or equal to 0');
133+
});
134+
135+
it('Should not error when duration is zero', () => {
136+
expect(() => funcs.make_sound(() => 0, 0)).not.toThrow();
137+
});
138+
139+
it('Should error gracefully when wave is not a function', () => {
140+
expect(() => funcs.make_sound(true as any, 1))
141+
.toThrow('make_sound: wave must be a Wave, got true');
142+
});
143+
});
144+
145+
describe(funcs.play, () => {
146+
it('Should error gracefully when duration is negative', () => {
147+
const sound: Sound = [[() => 0, () => 0], -1];
148+
expect(() => funcs.play(sound))
149+
.toThrow('play: duration of sound is negative');
150+
});
151+
152+
it('Should not error when duration is zero', () => {
153+
const sound = funcs.make_sound(() => 0, 0);
154+
expect(() => funcs.play(sound)).not.toThrow();
155+
});
156+
157+
it('Should throw error when given not a sound', () => {
158+
expect(() => funcs.play(0 as any)).toThrow('play is expecting sound, but encountered 0');
159+
});
160+
});
161+
162+
describe(funcs.play_wave, () => {
163+
it('Should error gracefully when duration is negative', () => {
164+
expect(() => funcs.play_wave(() => 0, -1))
165+
.toThrow('play_wave: Sound duration must be greater than or equal to 0');
166+
});
167+
168+
it('Should error gracefully when duration is not a number', () => {
169+
expect(() => funcs.play_wave(() => 0, true as any))
170+
.toThrow('play_wave expects a number for duration, got true');
171+
});
172+
173+
it('Should error gracefully when wave is not a function', () => {
174+
expect(() => funcs.play_wave(true as any, 0))
175+
.toThrow('play_wave: wave must be a Wave, got true');
176+
});
177+
});
178+
179+
describe(funcs.play_in_tab, () => {
180+
it('Should error gracefully when duration is negative', () => {
181+
const sound: Sound = [[() => 0, () => 0], -1];
182+
expect(() => funcs.play_in_tab(sound))
183+
.toThrow('play_in_tab: duration of sound is negative');
184+
});
185+
186+
it('Should not error when duration is zero', () => {
187+
const sound: Sound = [[() => 0, () => 0], 0];
188+
expect(() => funcs.play_in_tab(sound)).not.toThrow();
189+
});
190+
191+
it('Should throw error when given not a sound', () => {
192+
expect(() => funcs.play_in_tab(0 as any)).toThrow('play_in_tab is expecting sound, but encountered 0');
193+
});
194+
});

0 commit comments

Comments
 (0)