diff --git a/src/app/pages/algorithms/sorting/service/sorting-audio.service.ts b/src/app/pages/algorithms/sorting/service/sorting-audio.service.ts
new file mode 100644
index 0000000..3ac8f3f
--- /dev/null
+++ b/src/app/pages/algorithms/sorting/service/sorting-audio.service.ts
@@ -0,0 +1,80 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SortingAudioService {
+ private audioContext: AudioContext | null = null;
+
+ private ensureContext(): AudioContext {
+ this.audioContext ??= new AudioContext();
+ if (this.audioContext.state === 'suspended') {
+ this.audioContext.resume();
+ }
+ return this.audioContext;
+ }
+
+ // Call this on the user gesture (button click) so the AudioContext can be created/resumed.
+ initOnUserGesture(): void {
+ this.ensureContext();
+ }
+
+ playTone(value: number, maxValue: number, animationSpeedMs: number): void {
+ const ctx = this.ensureContext();
+ const frequency = this.valueToFrequency(value, maxValue);
+
+ // Keep tone duration slightly shorter than the animation frame to avoid overlap
+ const duration = Math.min(0.1, (animationSpeedMs * 0.75) / 1000);
+
+ const oscillator = ctx.createOscillator();
+ const gainNode = ctx.createGain();
+
+ oscillator.connect(gainNode);
+ gainNode.connect(ctx.destination);
+
+ oscillator.type = 'sawtooth';
+ oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);
+
+ gainNode.gain.setValueAtTime(0.15, ctx.currentTime);
+ gainNode.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + duration);
+
+ oscillator.start(ctx.currentTime);
+ oscillator.stop(ctx.currentTime + duration);
+ }
+
+ playSortedSweep(sortedValues: number[], maxValue: number): void {
+ const ctx = this.ensureContext();
+ // Play a quick ascending sweep through all sorted bar values
+ const step = Math.ceil(sortedValues.length / 40);
+ sortedValues.forEach((value, i) => {
+ if (i % step !== 0) {
+ return;
+ }
+ const delayMs = (i / step) * 18;
+ setTimeout(() => {
+ const frequency = this.valueToFrequency(value, maxValue);
+ const oscillator = ctx.createOscillator();
+ const gainNode = ctx.createGain();
+
+ oscillator.connect(gainNode);
+ gainNode.connect(ctx.destination);
+
+ oscillator.type = 'sine';
+ oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);
+
+ gainNode.gain.setValueAtTime(0.15, ctx.currentTime);
+ gainNode.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.06);
+
+ oscillator.start(ctx.currentTime);
+ oscillator.stop(ctx.currentTime + 0.06);
+ }, delayMs);
+ });
+ }
+
+ // Maps a bar value linearly to the frequency range 180–1100 Hz (roughly 3 octaves)
+ private valueToFrequency(value: number, maxValue: number): number {
+ const minFreq = 400;
+ const maxFreq = 800;
+ return minFreq + (value / maxValue) * (maxFreq - minFreq);
+ }
+}
diff --git a/src/app/pages/algorithms/sorting/sorting.component.html b/src/app/pages/algorithms/sorting/sorting.component.html
index 7f97529..77ade5b 100644
--- a/src/app/pages/algorithms/sorting/sorting.component.html
+++ b/src/app/pages/algorithms/sorting/sorting.component.html
@@ -37,6 +37,10 @@
+
diff --git a/src/app/pages/algorithms/sorting/sorting.component.ts b/src/app/pages/algorithms/sorting/sorting.component.ts
index f25bfd2..9cdc792 100644
--- a/src/app/pages/algorithms/sorting/sorting.component.ts
+++ b/src/app/pages/algorithms/sorting/sorting.component.ts
@@ -7,6 +7,7 @@ import {MatButtonModule} from "@angular/material/button";
import {MatIconModule} from "@angular/material/icon";
import {TranslateModule} from "@ngx-translate/core";
import { SortingService } from './service/sorting.service';
+import { SortingAudioService } from './service/sorting-audio.service';
import {SortData, SortSnapshot} from './sorting.models';
import { FormsModule } from '@angular/forms';
import {MatInput} from '@angular/material/input';
@@ -22,6 +23,7 @@ import {Information} from '../information/information';
export class SortingComponent implements OnInit {
private readonly sortingService: SortingService = inject(SortingService);
+ private readonly audioService: SortingAudioService = inject(SortingAudioService);
private readonly cdr: ChangeDetectorRef = inject(ChangeDetectorRef);
readonly MAX_ARRAY_SIZE: number = 200;
@@ -70,9 +72,10 @@ export class SortingComponent implements OnInit {
unsortedArrayCopy: SortData[] = [];
arraySize = 50;
maxArrayValue = 100;
- animationSpeed = 50; // Milliseconds per step
+ animationSpeed = 100; // Milliseconds per step
selectedAlgorithm: string = this.algoInformation.entries[0].name;
executionTime = 0;
+ isSoundEnabled = false;
ngOnInit(): void {
this.generateNewArray();
@@ -117,8 +120,14 @@ export class SortingComponent implements OnInit {
}
}
+ toggleSound(): void {
+ this.isSoundEnabled = !this.isSoundEnabled;
+ }
+
async startSorting(): Promise {
this.resetSorting();
+ // Init the AudioContext on this user gesture so the browser allows sound
+ this.audioService.initOnUserGesture();
const startTime = performance.now();
let snapshots: SortSnapshot[] = [];
@@ -156,11 +165,22 @@ export class SortingComponent implements OnInit {
}
}
+ if (this.isSoundEnabled) {
+ // Play a tone for each comparing bar (max 2 at once to avoid noise)
+ snapshot.array
+ .filter(item => item.state === 'comparing')
+ .slice(0, 2)
+ .forEach(item => this.audioService.playTone(item.value, this.maxArrayValue, this.animationSpeed));
+ }
+
this.cdr.detectChanges();
if (index === snapshots.length - 1) {
this.sortArray.forEach(item => item.state = 'sorted');
this.cdr.detectChanges();
+ if (this.isSoundEnabled) {
+ this.audioService.playSortedSweep(this.sortArray.map(item => item.value), this.maxArrayValue);
+ }
}
}, index * this.animationSpeed);
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index f93298f..e45e3cf 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -364,6 +364,8 @@
"START": "Sortierung starten",
"RESET": "Zurücksetzen",
"GENERATE_NEW_ARRAY": "Neues Array generieren",
+ "SOUND_ON": "Ton an",
+ "SOUND_OFF": "Ton aus",
"EXECUTION_TIME": "Ausführungszeit",
"ARRAY_SIZE": "Anzahl der Balken",
"EXPLANATION": {
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index b2f0d97..effb9f9 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -364,6 +364,8 @@
"START": "Start Sorting",
"RESET": "Reset",
"GENERATE_NEW_ARRAY": "Generate New Array",
+ "SOUND_ON": "Sound On",
+ "SOUND_OFF": "Sound Off",
"EXECUTION_TIME": "Execution Time",
"ARRAY_SIZE": "Number of Bars",
"EXPLANATION": {