diff --git a/korman/properties/modifiers/region.py b/korman/properties/modifiers/region.py index 5dc903c..fb9ebe3 100644 --- a/korman/properties/modifiers/region.py +++ b/korman/properties/modifiers/region.py @@ -215,6 +215,241 @@ class PlasmaPanicLinkRegion(PlasmaModifierProperties): return True +reverb_flags = [("kFlagDecayTimeScale", "Decay Time Scale", "Reverberation decay time"), + ("kFlagReflectionsScale", "Reflections Scale", "Reflection level"), + ("kFlagReflectionsDelayScale", "Reflections Delay Scale", "Initial reflection delay time"), + ("kFlagReverbScale", "Reverb Scale", "Reverberation level"), + ("kFlagReverbDelayScale", "Reverb Delay Scale", "Late reverberation delay time"), + ("kFlagEchoTimeScale", "Echo Time Scale", "Echo time"), + ("kFlagModulationTimeScale", "Modulation Time Scale", "Modulation time"), + ("kFlagDecayHFLimit", "Decay HF Limit", "Limit unnaturally long decay times of high-frequency sounds by forcing a limit to the decay time to be calculated from the Air Absorption HF value")] + +class PlasmaReverbRegion(PlasmaModifierProperties): + pl_id = "reverb" + pl_depends = {"softvolume"} + + bl_category = "Region" + bl_label = "Sound Reverb Region" + bl_description = "Sound Reverberation (EAX) Region" + bl_icon = "IPO_ELASTIC" + + preset = EnumProperty(name="Environment Preset", + description="The type of audio environment to simulate", + items=[("GENERIC", "Generic", "A generic-sounding environment with light reverberation"), + ("PADDEDCELL", "Padded cell", ""), + ("ROOM", "Room", ""), + ("BATHROOM", "Bathroom", ""), + ("LIVINGROOM", "Living room", ""), + ("STONEROOM", "Stone room", ""), + ("AUDITORIUM", "Auditorium", ""), + ("CONCERTHALL", "Concert Hall", ""), + ("CAVE", "Cave", ""), + ("ARENA", "Arena", ""), + ("HANGAR", "Hangar", ""), + ("CARPETTEDHALLWAY", "Carpetted hallway", ""), + ("HALLWAY", "Hallway", ""), + ("STONECORRIDOR", "Stone corridor", ""), + ("ALLEY", "Alley", ""), + ("FOREST", "Forest", ""), + ("CITY", "City", ""), + ("MOUNTAINS", "Mountains", ""), + ("QUARRY", "Quarry", ""), + ("PLAIN", "Plain", ""), + ("PARKINGLOT", "Parking lot", ""), + ("SEWERPIPE", "Sewer pipe", ""), + ("UNDERWATER", "Underwater", ""), + ("DRUGGED", "Drugged", ""), + ("DIZZY", "Drizzy", ""), + ("PSYCHOTIC", "Psychotic", ""), + ("CASTLE_SMALLROOM", "Castle - Small room", ""), + ("CASTLE_SHORTPASSAGE", "Castle - Short passage", ""), + ("CASTLE_MEDIUMROOM", "Castle - Medium room", ""), + ("CASTLE_LONGPASSAGE", "Castle - Long passage", ""), + ("CASTLE_LARGEROOM", "Castle - Large room", ""), + ("CASTLE_HALL", "Castle - Hall", ""), + ("CASTLE_CUPBOARD", "Castle - Cupboard", ""), + ("CASTLE_COURTYARD", "Castle - Courtyard", ""), + ("CASTLE_ALCOVE", "Castle - Alcove", ""), + ("FACTORY_ALCOVE", "Factory - Alcove", ""), + ("FACTORY_SHORTPASSAGE", "Factory - Short passage", ""), + ("FACTORY_MEDIUMROOM", "Factory - Medium room", ""), + ("FACTORY_LONGPASSAGE", "Factory - Long passage", ""), + ("FACTORY_LARGEROOM", "Factory - Large room", ""), + ("FACTORY_HALL", "Factory - Hall", ""), + ("FACTORY_CUPBOARD", "Factory - Cupboard", ""), + ("FACTORY_COURTYARD", "Factory - Courtyard", ""), + ("FACTORY_SMALLROOM", "Factory - Small room", ""), + ("ICEPALACE_ALCOVE", "Ice palace - Alcove", ""), + ("ICEPALACE_SHORTPASSAGE", "Ice palace - Short passage", ""), + ("ICEPALACE_MEDIUMROOM", "Ice palace - Medium room", ""), + ("ICEPALACE_LONGPASSAGE", "Ice palace - Long passage", ""), + ("ICEPALACE_LARGEROOM", "Ice palace - Large room", ""), + ("ICEPALACE_HALL", "Ice palace - Hall", ""), + ("ICEPALACE_CUPBOARD", "Ice palace - Cupboard", ""), + ("ICEPALACE_COURTYARD", "Ice palace - Courtyard", ""), + ("ICEPALACE_SMALLROOM", "Ice palace - Small room", ""), + ("SPACESTATION_ALCOVE", "Space station - Alcove", ""), + ("SPACESTATION_MEDIUMROOM", "Space station - Medium room", ""), + ("SPACESTATION_SHORTPASSAGE", "Space station - Short passage", ""), + ("SPACESTATION_LONGPASSAGE", "Space station - Long passage", ""), + ("SPACESTATION_LARGEROOM", "Space station - Large room", ""), + ("SPACESTATION_HALL", "Space station - Hall", ""), + ("SPACESTATION_CUPBOARD", "Space station - Cupboard", ""), + ("SPACESTATION_SMALLROOM", "Space station - Small room", ""), + ("WOODEN_ALCOVE", "Wooden alcove", ""), + ("WOODEN_SHORTPASSAGE", "Wooden short passage", ""), + ("WOODEN_MEDIUMROOM", "Wooden medium room", ""), + ("WOODEN_LONGPASSAGE", "Wooden long passage", ""), + ("WOODEN_LARGEROOM", "Wooden large room", ""), + ("WOODEN_HALL", "Wooden hall", ""), + ("WOODEN_CUPBOARD", "Wooden cupboard", ""), + ("WOODEN_SMALLROOM", "Wooden small room", ""), + ("WOODEN_COURTYARD", "Wooden courtyard", ""), + ("SPORT_EMPTYSTADIUM", "Sport - Empty stadium", ""), + ("SPORT_SQUASHCOURT", "Sport - Squash court", ""), + ("SPORT_SMALLSWIMMINGPOOL", "Sport - Small swimming pool", ""), + ("SPORT_LARGESWIMMINGPOOL", "Sport - Large swimming pool", ""), + ("SPORT_GYMNASIUM", "Sport - Gymnasium", ""), + ("SPORT_FULLSTADIUM", "Sport - Full stadium", ""), + ("SPORT_STADIUMTANNOY", "Sport - Stadium tannoy", ""), + ("PREFAB_WORKSHOP", "Prefab - Workshop", ""), + ("PREFAB_SCHOOLROOM", "Prefab - Schoolroom", ""), + ("PREFAB_PRACTISEROOM", "Prefab - Practise room", ""), + ("PREFAB_OUTHOUSE", "Prefab - Outhouse", ""), + ("PREFAB_CARAVAN", "Prefab - Zandi's Trailer", ""), + ("DOME_TOMB", "Tomb dome", ""), + ("DOME_SAINTPAULS", "St Paul's Dome", ""), + ("PIPE_SMALL", "Pipe - small", ""), + ("PIPE_LONGTHIN", "Pipe - long & thin", ""), + ("PIPE_LARGE", "Pipe - large", ""), + ("PIPE_RESONANT", "Pipe - resonant", ""), + ("OUTDOORS_BACKYARD", "Outdoors - Backyard", ""), + ("OUTDOORS_ROLLINGPLAINS", "Outdoors - Rolling plains", ""), + ("OUTDOORS_DEEPCANYON", "Outdoors - Deep canyon", ""), + ("OUTDOORS_CREEK", "Outdoors - Creek", ""), + ("OUTDOORS_VALLEY", "Outdoors - Valley", ""), + ("MOOD_HEAVEN", "Mood - Heaven", ""), + ("MOOD_HELL", "Mood - Hell", ""), + ("MOOD_MEMORY", "Mood - Memory", ""), + ("DRIVING_COMMENTATOR", "Driving - Commentator", ""), + ("DRIVING_PITGARAGE", "Driving - In pit garage", ""), + ("DRIVING_INCAR_RACER", "Driving - In racer car", ""), + ("DRIVING_INCAR_SPORTS", "Driving - In sports car", ""), + ("DRIVING_INCAR_LUXURY", "Driving - In luxury car", ""), + ("DRIVING_FULLGRANDSTAND", "Driving - Full grand stand", ""), + ("DRIVING_EMPTYGRANDSTAND", "Driving - Empty grand stand", ""), + ("DRIVING_TUNNEL", "Driving - Tunnel", ""), + ("CITY_STREETS", "City - Streets", ""), + ("CITY_SUBWAY", "City - Subway", ""), + ("CITY_MUSEUM", "City - Museum", ""), + ("CITY_LIBRARY", "City - Library", ""), + ("CITY_UNDERPASS", "City - Underpass", ""), + ("CITY_ABANDONED", "City - Abandoned", ""), + ("DUSTYROOM", "Dusty room", ""), + ("CHAPEL", "Chapel", ""), + ("SMALLWATERROOM", "Small water room", ""), + ("CUSTOM", "Custom", "Setup your own environment")], + default="GENERIC", + options=set()) + + environment_size = FloatProperty(name="Environment Size", description="Environment Size", + default=7.5, min=1.0, max=100.0, + options=set()) + environment_diffusion = FloatProperty(name="Environment Diffusion", description="Environment Diffusion", + default=1.0, min=0.0, max=1.0, + options=set()) + room = IntProperty(name="Room", description="Room", + default=-1000, min=-10000, max=0, + options=set()) + room_hf = IntProperty(name="Room HF", description="Room High Frequency", + default=-100, min=-10000, max=0, + options=set()) + room_lf = IntProperty(name="Room LF", description="Room Low Frequency", + default=0, min=-10000, max=0, + options=set()) + decay_time = FloatProperty(name="Decay Time", description="Decay Time", + default=1.49, min=0.1, max=20.0, + options=set()) + decay_hf_ratio = FloatProperty(name="Decay HF Ratio", description="Decay High Frequency Ratio", + default=0.83, min=0.1, max=2.0, + options=set()) + decay_lf_ratio = FloatProperty(name="Decay LF Ratio", description="Decay Low Frequency Ratio", + default=1.0, min=0.1, max=2.0, + options=set()) + reflections = IntProperty(name="Reflections", description="Reflections", + default=-2602, min=-10000, max=1000, + options=set()) + reflections_delay = FloatProperty(name="Reflections Delay", description="Reflections Delay", + default=0.007, min=0.0, max=0.3, + options=set()) + reverb = IntProperty(name="Reverb", description="Reverb", + default=200, min=-10000, max=2000, + options=set()) + reverb_delay = FloatProperty(name="Reverb Delay", description="Reverb Delay", + default=0.011, min=0.0, max=0.3, + options=set()) + echo_time = FloatProperty(name="Echo Time", description="Echo Time", + default=0.25, min=0.1, max=0.5, + options=set()) + echo_depth = FloatProperty(name="Echo Depth", description="Echo Depth", + default=0.0, min=0.0, max=1.0, + options=set()) + modulation_time = FloatProperty(name="Modulation Time", description="Modulation Time", + default=0.25, min=0.1, max=5.0, + options=set()) + modulation_depth = FloatProperty(name="Modulation Depth", description="Modulation Depth", + default=0.0, min=0.0, max=1.0, + options=set()) + air_absorption_hf = FloatProperty(name="Air Absorption HF", description="Air Absorption High Frequency", + default=-5.0, min=-10.0, max=0.0, + options=set()) + hf_reference = FloatProperty(name="HF reference", description="High Frequency Reference", + default=5000.0, min=1000.0, max=20000.0, + options=set()) + lf_reference = FloatProperty(name="LF reference", description="Low Frequency Reference", + default=250.0, min=20.0, max=1000.0, + options=set()) + + flags = EnumProperty(name="Flags", + description="Reverb flags", + items=reverb_flags, + default={"kFlagDecayTimeScale", "kFlagReflectionsScale", "kFlagReflectionsDelayScale", + "kFlagReverbScale", "kFlagReverbDelayScale", "kFlagEchoTimeScale"}, + options={"ENUM_FLAG"}) + + def export(self, exporter, bo, so): + eax_listener = exporter.mgr.find_create_object(plEAXListenerMod, so=so) + eax_listener.softRegion = bo.plasma_modifiers.softvolume.get_key(exporter, so) + if self.preset == "CUSTOM": + # Someone's feeling exceedingly confident today... + props = EAXReverbProperties() + props.environment = 26 + props.environmentSize = self.environment_size + props.environmentDiffusion = self.environment_diffusion + props.room = self.room + props.roomHF = self.room_hf + props.roomLF = self.room_lf + props.decayTime = self.decay_time + props.decayHFRatio = self.decay_hf_ratio + props.decayLFRatio = self.decay_lf_ratio + props.reflections = self.reflections + props.reflectionsDelay = self.reflections_delay + props.reverb = self.reverb + props.reverbDelay = self.reverb_delay + props.echoTime = self.echo_time + props.echoDepth = self.echo_depth + props.modulationTime = self.modulation_time + props.modulationDepth = self.modulation_depth + props.airAbsorptionHF = self.air_absorption_hf + props.hfReference = self.hf_reference + props.lfReference = self.lf_reference + for flag in self.flags: + props.flags |= getattr(EAXReverbProperties, flag) + eax_listener.listenerProps = props + else: + eax_listener.listenerProps = getattr(EAXReverbProperties, "REVERB_PRESET_{}".format(self.preset)) + + class PlasmaSoftVolume(idprops.IDPropMixin, PlasmaModifierProperties): pl_id = "softvolume" diff --git a/korman/properties/modifiers/sound.py b/korman/properties/modifiers/sound.py index 7d0adca..e8abbe9 100644 --- a/korman/properties/modifiers/sound.py +++ b/korman/properties/modifiers/sound.py @@ -285,6 +285,12 @@ class PlasmaSound(idprops.IDPropMixin, bpy.types.PropertyGroup): options={"ANIMATABLE"}, subtype="PERCENTAGE") + reverb_amount = IntProperty(name="Reverb amount", + description="Amount of reverb to apply to this sound (at 0%, reverb volume is reduced by 100 dB and thus disabled)", + min=0, max=100, default=100, + options=set(), + subtype="PERCENTAGE") + fade_in = PointerProperty(type=PlasmaSfxFade, options=set()) fade_out = PointerProperty(type=PlasmaSfxFade, options=set()) @@ -425,6 +431,21 @@ class PlasmaSound(idprops.IDPropMixin, bpy.types.PropertyGroup): else: sound.channel = plWin32Sound.kRightChannel + # Reverb/EAX + if self.sfx_type in {"kSoundFX", "kNPCVoices"} and self.reverb_amount > 0: + eax = sound.eaxSettings + eax.enable = True + # Occlusion: the minimum is -100 dB (but the value is multiplied by 100 in EAX's API) + eax.room = int(-10000 * (1 - (self.reverb_amount / 100.0))) + + # I couldn't get doppler effect working ingame, nor could I figure out how soft + # starts/ends are supposed to work in conjunction with the soft region. + # If you have more infos about those, feel free to share. + + # Make sure we use sensible defaults for soft starts/ends + eax.softStarts.reset() + eax.softEnds.reset() + # Whew, that was a lot of work! return sound.key diff --git a/korman/ui/modifiers/region.py b/korman/ui/modifiers/region.py index bd0be1d..da53fb2 100644 --- a/korman/ui/modifiers/region.py +++ b/korman/ui/modifiers/region.py @@ -48,6 +48,36 @@ def paniclink(modifier, layout, context): _draw_bounds_prop(modifier, layout, context) layout.prop(modifier, "play_anim") +def reverb(modifier, layout, context): + layout.prop(modifier, "preset") + if modifier.preset == "MORE": + layout.prop(modifier, "preset_more") + elif modifier.preset == "CUSTOM": + split = layout.split() + colA = split.column() + colB = split.column() + colA.prop(modifier, "environment_size") + colA.prop(modifier, "environment_diffusion") + colB.prop(modifier, "room") + colB.prop(modifier, "room_hf") + colB.prop(modifier, "room_lf") + colA.prop(modifier, "decay_time") + colA.prop(modifier, "decay_hf_ratio") + colA.prop(modifier, "decay_lf_ratio") + colB.prop(modifier, "reflections") + colB.prop(modifier, "reflections_delay") + colB.prop(modifier, "reverb") + colB.prop(modifier, "reverb_delay") + colA.prop(modifier, "echo_time") + colA.prop(modifier, "echo_depth") + colA.prop(modifier, "modulation_time") + colA.prop(modifier, "modulation_depth") + colA.prop(modifier, "air_absorption_hf") + colB.prop(modifier, "hf_reference") + colB.prop(modifier, "lf_reference") + # colB.prop(modifier, "room_rolloff_factor") + layout.prop(modifier, "flags") + def softvolume(modifier, layout, context): row = layout.row() row.prop(modifier, "use_nodes", text="", icon="NODETREE") diff --git a/korman/ui/modifiers/sound.py b/korman/ui/modifiers/sound.py index 13d7a34..d9a7aba 100644 --- a/korman/ui/modifiers/sound.py +++ b/korman/ui/modifiers/sound.py @@ -117,6 +117,10 @@ def soundemit(modifier, layout, context): col.separator() _draw_fade_ui(sound.fade_out, col, "Fade Out:") + if sound.sfx_type in {"kSoundFX", "kNPCVoices"}: + col.separator() + col.prop(sound, "reverb_amount", text="Reverb Amount") + col = split.column() col.label("Cone Effect:") col.prop(sound, "inner_cone")