Source code for src.FMOD.Sounds.EVSoundEngine

# Copyright (c) 2026 Kai Braun, Ozan Miguel Gündogdu, Yeri Jikong, Sven Winkelmann
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License. 
# See LICENSE file in the project root for full license information.
# Also consult our README to comply with Third-Party Licenses.

import os
import time
import keyboard
import math as m

os.environ["PYFMODEX_DLL_PATH"] = r"C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\core\lib\x64\fmod.dll"
os.environ["PYFMODEX_STUDIO_DLL_PATH"] = r"C:\Program Files (x86)\FMOD SoundSystem\FMOD Studio API Windows\api\studio\lib\x64\fmodstudio.dll"

import pyfmodex
from pyfmodex.enums import DSP_TYPE

[docs] class EVSoundEngine: """ Procedural Audio Engine for Electric Vehicle (EV) sound synthesis. This engine utilizes the FMOD Core API to synthesize vehicle sounds in real-time using oscillators and noise generators. It simulates three primary acoustic components: 1. **Inverter/Motor**: Sine-wave synthesis with harmonic overtones. 2. **Road/Tire Noise**: Filtered white noise modulated by speed. 3. **Aerodynamic Hiss**: High-frequency wind noise appearing at higher velocities. Attributes: system (pyfmodex.System): The FMOD Core system instance. is_running (bool): Tracks the active state of the audio emitters. engine_group (ChannelGroup): Audio bus for motor-related DSPs. road_group (ChannelGroup): Audio bus for tire and wind-related DSPs. """
[docs] def __init__(self): """ Initializes the FMOD Core system and constructs the DSP signal chain. Sets up sine oscillators for the motor, noise generators for the road, and a filtering pipeline (low-pass and resonance) to simulate cabin insulation and tire resonance. """ self.system = pyfmodex.System() self.system.init(maxchannels=32) # Channel Groups self.master_group = self.system.create_channel_group("Master") self.engine_group = self.system.create_channel_group("Engine") self.road_group = self.system.create_channel_group("Road") # 1. MOTOR (Inverter) self.engine_dsp = self.system.create_dsp_by_type(DSP_TYPE.OSCILLATOR) # Fundamental frequency self.engine_dsp.set_parameter_int(0, 0) # Sine self.engine_dsp_harmonic = self.system.create_dsp_by_type(DSP_TYPE.OSCILLATOR) # First harmonic self.engine_dsp_harmonic.set_parameter_int(0,0) self.engine_dsp_harmonic2 = self.system.create_dsp_by_type(DSP_TYPE.OSCILLATOR) # Second harmonic self.engine_dsp_harmonic2.set_parameter_int(0,0) # 2. ROAD BASE (Das rohe Rauschen) self.road_base_dsp = self.system.create_dsp_by_type(DSP_TYPE.OSCILLATOR) self.road_base_dsp.set_parameter_int(0, 4) # White Noise # 3. WIND / HISS self.hiss_dsp = self.system.create_dsp_by_type(DSP_TYPE.OSCILLATOR) self.hiss_dsp.set_parameter_int(0, 4) # White Noise # FILTER # Reifen-Resonanz (Peak/Wummern) self.tire_resonance = self.system.create_dsp_by_type(DSP_TYPE.LOWPASS) self.tire_resonance.set_parameter_float(0, 200.0) self.tire_resonance.set_parameter_float(1, 4.0) # Resonance/Q-Faktor # Schalldämmung (Cabin) self.cabin_filter = self.system.create_dsp_by_type(DSP_TYPE.LOWPASS) self.cabin_filter.set_parameter_float(0, 1800.0) self.is_running = False
[docs] def start(self): """ Activates the DSP chain and begins audio playback. Assigns DSPs to their respective channel groups and establishes the hierarchical routing from road and engine groups to the master group. """ # 1. Grundwelle starten self.engine_ch = self.system.play_dsp(self.engine_dsp) self.engine_ch.channel_group = self.engine_group # 1. OBERWELLE in einem eigenen Channel starten # Wir weisen sie ebenfalls der motor_group zu self.engine_harmonic_ch = self.system.play_dsp(self.engine_dsp_harmonic) # 2. Oberwelle starten self.engine_harmonic2_ch = self.system.play_dsp(self.engine_dsp_harmonic2) # Hier nutzen wir road_base_dsp für den Reifen-Sound self.road_ch = self.system.play_dsp(self.road_base_dsp) self.road_ch.channel_group = self.road_group self.hiss_ch = self.system.play_dsp(self.hiss_dsp) self.hiss_ch.channel_group = self.road_group # Hierarchien setzen self.master_group.add_group(self.engine_group) self.master_group.add_group(self.road_group) # DSP Kette: Road Noise -> Tire Resonance Filter self.road_group.add_dsp(0, self.tire_resonance) # Alles zusammen -> Cabin Filter self.master_group.add_dsp(0, self.cabin_filter) self.is_running = True
[docs] def update_params(self, speed_kmh, torque, road_roughness=0.01): """ Modulates the synthesized sound based on real-time vehicle dynamics. Args: speed_kmh (float): Current vehicle speed. Affects pitch of the motor and volume/frequency of road and wind noise. torque (float): Engine load. Directly modulates the volume of the motor oscillators to simulate power delivery. road_roughness (float, optional): Multiplier for tire noise volume. Defaults to 0.01. """ if not self.is_running: return # MOTOR FREQUENZ motor_freq = 200 + (speed_kmh * 3) if speed_kmh == 0: motor_freq = 0 self.engine_dsp.set_parameter_float(1, motor_freq) self.engine_dsp_harmonic.set_parameter_float(1, motor_freq + 15) self.engine_dsp_harmonic2.set_parameter_float(1, motor_freq + 60) self.engine_ch.volume = abs(torque) * 0.05 self.engine_harmonic_ch.volume = abs(torque) * 0.04 self.engine_harmonic2_ch.volume = abs(torque) * 0.005 # ROAD TEXTURE (Reifenrollen) # Lautstärke basierend auf Speed & Straßenzustand road_vol = min(speed_kmh / 140.0, 0.5) * road_roughness self.road_ch.volume = road_vol # Das Wummern wird bei Speed schneller/heller dynamic_road_freq = 120 + (speed_kmh * 1.5) self.tire_resonance.set_parameter_float(0, min(dynamic_road_freq, 600)) # WIND / HISS (Zischen ab 60 km/h) hiss_freq = 1000 self.hiss_dsp.set_parameter_float(1, hiss_freq) hiss_vol = max(0, (speed_kmh - 60) / 200.0) self.hiss_ch.volume = hiss_vol * 0.2
[docs] def stop(self): """ Gracefully releases the FMOD Core system resources. """ if self.system: self.system.release()