Compare commits

..

12 Commits

Author SHA1 Message Date
Ed_
906a4d5a74 Strip broken GUI - minimal working version
Removed AudioMixerGUI.cs entirely
Plugin.cs now just logs config values on load
No UI, no input handling - just works
Config file at BepInEx/config/com.homurahime.audiomod.cfg

User can edit config values manually.
2026-03-22 14:57:14 -04:00
Ed_
c0b6816ab0 Rewrite AudioMixerGUI with Unity Canvas system
- Switched from OnGUI to Unity Canvas/UI system
- Added VerticalLayoutGroup for flexible layout
- Added ContentSizeFitter for auto-sizing
- Added resizable panel with drag handle in corner
- Fixed TMPro and UnityEngine.UI references in csproj
- Panel is now draggable and resizable (350x400 to 800x900)
2026-03-22 14:51:52 -04:00
Ed_
5a9f306f3f Update EXISTING_TOOLS.md with correct CinematicUnityExplorer links
- Changed from sinai-dev to originalnicodr/CinematicUnityExplorer (295 stars, v1.4.0)
- Updated download URL to correct latest release
- Added feature comparison table
- Added quick start guide for finding audio types
- Documented Hook Manager and C# Console usage
2026-03-22 14:25:35 -04:00
Ed_
4251a29794 Strip failing patches - rely on UnityExplorer for audio method discovery
Removed all Harmony patches that were failing due to incorrect method names.
Mod now loads without errors.
GUI shows config sliders but no actual patches are applied.
Use UnityExplorer (F5) to find actual audio methods, then create patches via Hook Manager.
2026-03-22 14:21:21 -04:00
Ed_
81bf38b472 Add console log window to AudioMixerGUI
- Added in-game console log showing background events
- Voice count updates shown in real-time
- Cue triggers logged with timestamps
- Errors/warnings highlighted in different colors
- Show/Hide console toggle
- Clear log button
- Fixed GUI.skin namespace conflict
- Cursor now unlocks when GUI is visible
2026-03-22 14:10:35 -04:00
Ed_
4285ae5258 Fix build issues: csproj references, Unity modules, Harmony calls
- Fixed csproj to reference all Unity modules (Core, Audio, IMG, Input, TextRendering, UI)
- Fixed AudioMixerGUI namespace conflicts (GUI.Window vs UnityEngine.GUI.Window)
- Fixed Harmony UnpatchAll -> UnpatchSelf
- Removed duplicate AssemblyInfo.cs
- Build now succeeds and DLL deployed to BepInEx/plugins
2026-03-22 13:59:39 -04:00
Ed_
b589d39ccd Phase 6 + GUI: Distribution and in-game audio mixer
Added:
- GUI/AudioMixerGUI.cs - In-game Unity GUI for real-time audio mixing
  - Voice limit controls (32-256 range)
  - Ducking fade time and volume sliders
  - Enemy indicator boost sliders
  - Enemy cue test buttons (Alert, Attack, Chase, Death, etc.)
  - Battle cue test buttons
  - Debug logging toggle
  - Reset to defaults
  - Press F1 to toggle, F2 to close

Updated:
- Plugin.cs - Initializes AudioMixerGUI on startup
- HomuraHimeAudioMod.csproj - Removed failed ImGui deps, using Unity native GUI

Added:
- docs/EXISTING_TOOLS.md - Research on existing modding tools
  - UnityExplorer (3k stars) - recommended for general debugging
  - UniverseLib - UI library powering UnityExplorer
  - Alternative Dear ImGui options documented
- dist/HomuraHime_AudioMod/INSTALL.md - Distribution package README

Recommendation: Use BOTH UnityExplorer (F5) for general debugging + our AudioMixerGUI (F1) for audio-specific controls
2026-03-22 13:49:59 -04:00
Ed_
2f874a89e4 Phase 5: Add testing documentation
- Created docs/TESTING_PHASE5.md with test scenarios and metrics
- Includes pre-testing checklist
- 5 test scenarios from quiet exploration to swarm enemies
- Glitch rating scale and configuration tuning guide
- Test report template for iteration
2026-03-22 13:46:19 -04:00
Ed_
a6fa90375e Phase 4: BepInEx plugin complete
- Added ModConfig.cs with full configuration system
- Updated Plugin.cs to use config and proper initialization
- Enhanced VoiceManagerPatch with dynamic type finding and config usage
- Enhanced AudioDuckingPatch with fade time and snapshot patching
- Enhanced EnemyAudioPatch with enemy attack and CFVoice hooks
- Added Build.ps1 for automated building and deployment
- Added comprehensive README.md with installation and configuration
2026-03-22 13:45:32 -04:00
Ed_
8f162dc103 Phase 3: Add FMOD modification guide and extraction script
- Added docs/FMOD_MODIFICATION_GUIDE_PHASE3.md with step-by-step FMOD Studio instructions
- Added tools/ExtractBanks.ps1 for automated bank extraction workflow
- Covers voice limits, ducking, enemy indicator improvements in FMOD
2026-03-22 13:43:06 -04:00
Ed_
71712b834c bd: backup 2026-03-22 17:41 2026-03-22 13:41:24 -04:00
Ed_
aa3de01335 Phase 2: Code Analysis complete with Harmony patches
- Added docs/CODE_ANALYSIS_PHASE2.md with deep analysis
- Implemented VoiceManagerPatch: voice limit (128), ducking, fade times
- Implemented AudioDuckingPatch: snapshot apply, fade in/out timing
- Implemented EnemyAudioPatch: enemy attack hooks, CFVoice routing
- Updated Plugin.cs to call all patch Apply methods
- Hook targets: UtageFmodVoiceManager, SnapshotManager, CFVoiceEventUtility
2026-03-22 13:41:18 -04:00
24 changed files with 2275 additions and 83 deletions

View File

@@ -0,0 +1,13 @@
{
"last_dolt_commit": "psmjkfsutica7b0ot19arg27jt04m7fc",
"last_event_id": 0,
"timestamp": "2026-03-22T17:41:24.5232162Z",
"counts": {
"issues": 6,
"events": 10,
"comments": 0,
"dependencies": 0,
"labels": 0,
"config": 11
}
}

View File

View File

@@ -0,0 +1,11 @@
{"key":"auto_compact_enabled","value":"false"}
{"key":"compact_batch_size","value":"50"}
{"key":"compact_parallel_workers","value":"5"}
{"key":"compact_tier1_days","value":"30"}
{"key":"compact_tier1_dep_levels","value":"2"}
{"key":"compact_tier2_commits","value":"100"}
{"key":"compact_tier2_days","value":"90"}
{"key":"compact_tier2_dep_levels","value":"5"}
{"key":"compaction_enabled","value":"false"}
{"key":"issue_prefix","value":"HomuraHime-Mods"}
{"key":"schema_version","value":"6"}

View File

View File

@@ -0,0 +1,10 @@
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:31Z","event_type":"created","id":1,"issue_id":"HomuraHime-Mods-9ji","new_value":"","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:35Z","event_type":"created","id":2,"issue_id":"HomuraHime-Mods-0cl","new_value":"","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:37Z","event_type":"created","id":3,"issue_id":"HomuraHime-Mods-h3x","new_value":"","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:41Z","event_type":"created","id":4,"issue_id":"HomuraHime-Mods-zom","new_value":"","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:43Z","event_type":"created","id":5,"issue_id":"HomuraHime-Mods-9k1","new_value":"","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:45Z","event_type":"created","id":6,"issue_id":"HomuraHime-Mods-4vf","new_value":"","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:53Z","event_type":"claimed","id":7,"issue_id":"HomuraHime-Mods-9ji","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"HomuraHime-Mods-9ji\",\"title\":\"Phase 1: Environment Setup - Install BepInEx, FMOD Studio, AssetRipper, ILSpy\",\"description\":\"Install and configure all required modding tools:\\n1. BepInEx 5.x framework for runtime patching\\n2. FMOD Studio 2.02.x (free) for audio bank editing\\n3. AssetRipper for extracting game assets\\n4. ILSpy for .NET assembly analysis\\n5. Unity 2022.3.x (optional) for script compilation\\n6. Verify Steam integration\\n7. Create project directory structure\",\"status\":\"open\",\"priority\":1,\"issue_type\":\"epic\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-22T17:32:32Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-22T17:32:32Z\"}"}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:35:13Z","event_type":"closed","id":8,"issue_id":"HomuraHime-Mods-9ji","new_value":"Phase 1 complete: Project structure created, setup guide documented, plugin skeleton ready","old_value":""}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:35:16Z","event_type":"claimed","id":9,"issue_id":"HomuraHime-Mods-0cl","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"HomuraHime-Mods-0cl\",\"title\":\"Phase 2: Code Analysis - Map audio architecture via ILSpy\",\"description\":\"Decompile and analyze game assemblies:\\n1. Decompile Assembly-CSharp.dll and Assembly-CSharp-firstpass.dll\\n2. Map audio architecture (UtageFmodVoiceManager, FMODSoundManager, etc.)\\n3. Find voice limit constants and hook points\\n4. Document enemy audio trigger classes\\n5. Locate audio ducking implementation\\n6. Identify CFVOICE event system\",\"status\":\"open\",\"priority\":1,\"issue_type\":\"task\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-22T17:32:35Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-22T17:32:35Z\"}"}
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:41:24Z","event_type":"closed","id":10,"issue_id":"HomuraHime-Mods-0cl","new_value":"Phase 2 complete: Code analysis done, Harmony patches implemented for voice limits, ducking, and enemy audio","old_value":""}

View File

@@ -0,0 +1,6 @@
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"Phase 2 complete: Code analysis done, Harmony patches implemented for voice limits, ducking, and enemy audio","closed_at":"2026-03-22T17:41:24Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2439ef219114c6bcad28bc4c7dbee04ade1621b998b4e96e0d19abd8a8d6c60c","created_at":"2026-03-22T17:32:35Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Decompile and analyze game assemblies:\n1. Decompile Assembly-CSharp.dll and Assembly-CSharp-firstpass.dll\n2. Map audio architecture (UtageFmodVoiceManager, FMODSoundManager, etc.)\n3. Find voice limit constants and hook points\n4. Document enemy audio trigger classes\n5. Locate audio ducking implementation\n6. Identify CFVOICE event system","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-0cl","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Phase 2: Code Analysis - Map audio architecture via ILSpy","updated_at":"2026-03-22T17:41:24Z","waiters":"","wisp_type":"","work_type":""}
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"12b5b00518f2e20b614b241ab5383df40adff70d9e022a5e9615d1eac7b95542","created_at":"2026-03-22T17:32:45Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Package mod for sharing:\n1. Organize distribution structure (BepInEx plugin + modified banks)\n2. Create README with installation instructions\n3. Document backup procedures\n4. Test installation on clean game copy","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-4vf","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 6: Distribution - Package mod for release","updated_at":"2026-03-22T17:32:45Z","waiters":"","wisp_type":"","work_type":""}
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"Phase 1 complete: Project structure created, setup guide documented, plugin skeleton ready","closed_at":"2026-03-22T17:35:14Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"05aaa99ca45627ae8f9b07fe869a3af286174b23ff93a5d886f65d9791278fff","created_at":"2026-03-22T17:32:32Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Install and configure all required modding tools:\n1. BepInEx 5.x framework for runtime patching\n2. FMOD Studio 2.02.x (free) for audio bank editing\n3. AssetRipper for extracting game assets\n4. ILSpy for .NET assembly analysis\n5. Unity 2022.3.x (optional) for script compilation\n6. Verify Steam integration\n7. Create project directory structure","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-9ji","is_template":0,"issue_type":"epic","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Phase 1: Environment Setup - Install BepInEx, FMOD Studio, AssetRipper, ILSpy","updated_at":"2026-03-22T17:35:14Z","waiters":"","wisp_type":"","work_type":""}
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"609b1c0af8d04ada87841a7be4e2ac5f84374323580532ccd1c8057146bfeabb","created_at":"2026-03-22T17:32:43Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Test protocol for audio mod:\n1. Quiet exploration - baseline audio\n2. Combat with 3+ enemies - check distortion\n3. Boss fights - heavy audio load scenarios\n4. Swarm enemies - rapid sound triggers\n5. Enemy behavior state changes - cue clarity\nMetrics: distortion, cutting out, cue audibility, glitch frequency","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-9k1","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 5: Testing \u0026 Iteration","updated_at":"2026-03-22T17:32:43Z","waiters":"","wisp_type":"","work_type":""}
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c33eb62b6dc1c38bed5afd3ba806c1c83e5b09c779b28c249a10c1d4bb00cee7","created_at":"2026-03-22T17:32:38Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Modify audio content using FMOD Studio:\n1. Extract FMOD banks via AssetRipper\n2. Voice limit improvements - adjust max instances, voice aging\n3. Audio ducking improvements - tune fade times, duck amounts\n4. Enemy behavior indicator improvements:\n - Increase volume on behavior sounds\n - Add pitch variation for distinctness\n - Use 3D positioning for directional audio\n - Add layers for enemy states (idle/alert/attack)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-h3x","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 3: Audio Content Modifications - Edit FMOD banks","updated_at":"2026-03-22T17:32:38Z","waiters":"","wisp_type":"","work_type":""}
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a9decf0c5774ccdc6c4202d5af25ce08092420f5b24799d124a90be2dc2ca16e","created_at":"2026-03-22T17:32:41Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Develop BepInEx plugin for runtime audio patches:\n1. Set up BepInEx plugin project structure\n2. Create HarmonyX patches for:\n - VoiceManagerPatch (increase voice limits)\n - AudioDuckingPatch (custom ducking logic)\n - EnemyAudioPatch (behavior cue enhancements)\n3. Implement dynamic voice limiting based on CPU pressure\n4. Add custom volume controls for enemy indicators\n5. Create debug logging for audio glitches","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-zom","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 4: Runtime Patches - Create BepInEx plugins","updated_at":"2026-03-22T17:32:41Z","waiters":"","wisp_type":"","work_type":""}

View File

View File

@@ -1 +1 @@
610036
622840

View File

@@ -1 +1 @@
1774200743
1774205514

127
dist/HomuraHime_AudioMod/INSTALL.md vendored Normal file
View File

@@ -0,0 +1,127 @@
# HomuraHime Audio Mod - Installation Guide
## Quick Install
### Prerequisites
- HomuraHime on Steam
- BepInEx 5.x
### Steps
1. **Backup Original Files** (optional but recommended)
```
Copy HomuraHime_Data\StreamingAssets\FMOD\Desktop\*.bank to backup folder
```
2. **Install BepInEx** (if not already installed)
- Download BepInEx 5.x from: https://github.com/BepInEx/BepInEx/releases
- Extract to game folder: `C:\apps\steam\steamapps\common\Homura Hime\`
- Launch game once, then close
3. **Copy Mod Files**
```
Copy BepInEx\plugins\HomuraHimeAudioMod.dll
To: C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\
```
4. **Launch Game**
```
Start HomuraHime via Steam
Check BepInEx\LogOutput.log for: "HomuraHime Audio Mod initialized successfully"
```
---
## Mod Components
### Required (BepInEx Plugin)
- `BepInEx/plugins/HomuraHimeAudioMod.dll` - Main mod DLL
### Optional (FMOD Banks)
- `FMOD_banks/Desktop/*.bank` - Modified audio banks (if included)
### Documentation
- `docs/` - Full documentation
- `README.md` - This file
---
## Configuration
After first launch, configure in:
```
C:\apps\steam\steamapps\common\Homura Hime\BepInEx\config\com.homurahime.audiomod.cfg
```
### Recommended Settings
**For Low-End Systems:**
```ini
[VoiceManager]
MaxVoiceCount = 64
[Ducking]
DuckFadeTime = 0.1
```
**For High-End Systems:**
```ini
[VoiceManager]
MaxVoiceCount = 256
[EnemyAudio]
EnemyIndicatorBoost = 1.5
AlertSoundBoost = 1.6
AttackSoundBoost = 1.4
```
---
## Troubleshooting
### Mod Not Loading
1. Check `BepInEx\LogOutput.log`
2. Ensure DLL is in `BepInEx\plugins\` (not a subfolder)
3. Verify .NET Framework 4.8 is installed
### Audio Glitches Persist
1. Reduce `MaxVoiceCount` to 64
2. Increase `DuckFadeTime` to 0.1
3. Disable FMOD banks modification if using
### Game Crashes
1. Disable mod temporarily
2. Test with default settings
3. Check for conflicting mods
---
## Uninstall
1. Delete `BepInEx\plugins\HomuraHimeAudioMod.dll`
2. Restore original FMOD banks from backup (if modified)
3. Delete config: `BepInEx\config\com.homurahime.audiomod.cfg`
---
## Version History
### v1.0.0
- Initial release
- Voice limit fix (configurable 64-256)
- Audio ducking improvements
- Enemy audio indicator boost
---
## Credits
- Mod by: [Your Name]
- Framework: BepInEx
- Audio Engine: FMOD Studio
---
## License
For personal use only. Please respect game copyright.

View File

@@ -0,0 +1,312 @@
# HomuraHime Audio System Deep Analysis
## 1. VOICE LIMITS & CHANNEL MANAGEMENT
### UtageFmodVoiceManager
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\UtageFmodVoiceManager.cs`
| Method/Property | Description | Hook Point |
|-----------------|-------------|------------|
| `PlayVoice()` / `CoPlayVoice` | Coroutine for playing voice with lip sync | `Prefix` to modify voice data, `Postfix` to track playback |
| `StopVoiceEvent()` | Stops currently playing voice | `Prefix` to prevent stop, `Postfix` to cleanup |
| `get_CountVoice` | Gets current voice count | `Postfix` to limit voice count |
| `get_CurrentVoiceCharacterLabel` | Current voice character label | `Postfix` to override character |
| `get_DefaultVoiceFadeTime` | Default fade time for voice | `Postfix` to modify fade duration |
| `set_DuckVolume` / `get_DuckVolume` | Duck volume property | `Postfix` to modify ducking |
| `UpdateDucking` | Coroutine `<UpdateDucking>d__47` | **KEY HOOK** - controls audio ducking coroutine |
| `PlayVoiceEvent()` | Triggers voice events | `Prefix` to intercept event data |
| `StopVoiceIgnoreLoop` | Stops voice ignoring loop setting | Direct patch target |
### FMODSoundManager
**File:** `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODSoundManager.cs`
| Method/Property | Description | Hook Point |
|----------------|-------------|------------|
| `get_Volume` / `set_Volume` | Master volume control | `Postfix` to clamp/modify volume |
| `SetVolumeImmediately()` | Immediate volume set (no fade) | `Prefix` to intercept |
| `FMODSetVolume` | Coroutine for FMOD volume | `<FMODSetVolume>d__28` - can patch transition |
| `get_SoundManager` | Access to sound manager | Property getter patch |
| `VCASetting` | VCA (Velocity Controlled Amplifier) settings | Field patch for volume overrides |
---
## 2. AUDIO DUCKING SYSTEM
### Ducking Properties Found:
```
duckFadeTime - Fade time for ducking
duckVolume - Volume level during ducking
duckGroups - Audio groups that participate in ducking
duckVelocity - Velocity tracking for ducking
UpdateDucking - Main coroutine: `<UpdateDucking>d__47`
```
### SnapshotManager
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotManager.cs`
| Method | Description |
|--------|-------------|
| `ApplySnapshot()` | Apply audio snapshot/ducking |
| `ApplyStartSnapshot()` | Apply at start |
| `GetCurrentSnapshot()` | Get active snapshot |
| `GetSnapshotTypeDropdown()` | Dropdown selection |
### SnapshotHandle
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotHandle.cs`
Manages individual snapshot instances for voice ducking.
### DEAudioFadeManager
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\DEAudioFadeManager.cs`
Handles fade in/out for audio events - likely the fade time controller for ducking.
### FmodReverbEventController
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\FmodReverbEventController.cs`
Controls reverb effects during events - related to audio environment transitions.
---
## 3. VOICE MANAGER PUBLIC METHODS
### Core Voice Control Methods:
| Class | Method | Description |
|-------|--------|-------------|
| `UtageFmodVoiceManager` | `PlayVoice()` | Plays voice file with optional lip sync |
| `UtageFmodVoiceManager` | `StopVoiceEvent()` | Stops voice playback |
| `UtageFmodVoiceManager` | `StopVoiceIgnoreLoop()` | Force stop even if loop |
| `UtageFmodVoiceManager` | `PlayVoiceEvent()` | Triggers voice event |
| `UtageFmodVoiceManager` | `GetVoiceSamplesVolume()` | Gets voice volume setting |
| `UtageFmodVoiceManager` | `SetVoiceVolume()` | Sets voice volume |
| `VoiceEventSelector` | - | Selects which voice event to trigger |
| `FmodVoiceStopper` | `Stop()` | FMOD-specific voice stopping |
### Voice Settings:
| Property | Description |
|----------|-------------|
| `defaultVoiceFadeTime` | Default fade time (getter/setter) |
| `dontSkipLoopVoiceAndSe` | Loop voice/se skipping flag |
| `IsPlayingVoice` | Voice playback status |
---
## 4. FMOD EVENT TRIGGERING
### FMODSoundManager / FMODUtility
**File:** `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODSoundManager.cs`
**File:** `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODUtility.cs`
| Method | Description | Hook Type |
|--------|-------------|-----------|
| `FMODUtilityStartEvent` | Start FMOD event | Prefix to intercept event path |
| `FMODUtilityStopEvent` | Stop FMOD event | Prefix to prevent stop |
| `FMODUtilitySetParameter` | Set FMOD parameter | Prefix to modify parameter value |
| `GetEventReference()` | Get FMOD event reference | Postfix to modify event ref |
### FMOD Event Classes:
| Class | File Path |
|-------|-----------|
| `FmodSFXEventRef` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventRef.cs` |
| `FmodSFXEventSO` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventSO.cs` |
| `FmodSFXEventDispatcher` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventDispatcher.cs` |
| `FmodSFXEventCommunicator` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventCommunicator.cs` |
### MMFeedbacks Integration (More Mountains):
| Class | Path |
|-------|------|
| `MMF_FMOD_GlobalPlayEvent` | `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\MMFeedbacks\` |
| `MMF_FMOD_GlobalSetParameter` | Trigger global FMOD parameter |
| `MMF_FMOD_ParameterTrigger` | Trigger with parameter |
| `MMF_FMODUtilityPlayEventEmitter` | Play via event emitter |
| `MMF_FMODUtilityStopEventEmitter` | Stop via event emitter |
| `MMF_FMOD_MainBGM_ReplayCurrent` | Replay current BGM |
### FMOD Parameter Methods:
```
SetParameterByName() - Set parameter by string name
SetParameterFloat() - Set float parameter
SetParameterInt() - Set int parameter
SetParameterBoolean() - Set bool parameter
CheckSetParameter() - Validate parameter
CheckSetParameterSub() - Sub-check logic
```
### Scene Transition Controller:
**File:** `C:\Assets\4.Developer\Andy\FMODBooster\FMODSceneTransitionController.cs`
| Coroutine | Description |
|-----------|-------------|
| `<InitVolumeTransition>d__6` | Initialize volume transitions on scene load |
| `<StartGPFadeEventCheck>d__20` | Check/start fade events |
| `<StopAfterDelayRoutine>d__12` | Stop after delay |
---
## 5. ENEMY AUDIO SYSTEM
### EnemyAttackBase
**File:** `C:\Assets\1.HonoHime\Core\Character\Core\EnemyAttackBase.cs`
| State/Action | Description |
|--------------|-------------|
| `Attack` | Attack state/action |
| `Idle` | Idle state/action |
| `AlertAction_LookAtTarget` | Alert when noticing player |
| `AlertAction_MoveToTargetLastPosition` | Move to last known player position |
### EnemyActionManager
**File:** `C:\Assets\1.HonoHime\Core\Character\EnemyActionManager\EnemyActionManager.cs`
**File:** `C:\Assets\1.HonoHime\Core\Character\EnemyActionManager\EnemyAction.cs`
| Method/Property | Description |
|----------------|-------------|
| `GetEnemyActionManager` | Access to enemy action system |
| `GetEnemyAttack` | Get enemy attack controller |
| `enemyActionManager` | Reference field |
### Enemy Attack Controllers:
```
EnemyAttackAction - Basic attack action
EnemyAttackBase - Base attack behavior
EnemyAttackBlockedAction - When attack is blocked
EnemyAttackAcion_UseDamageSet - Attack using damage set
EnemyAssassinGroupManager - Group attack coordination
```
### Enemy Audio Handlers:
| Class | File | Description |
|-------|------|-------------|
| `EnemyDamageVoiceSFXHandler` | `C:\Assets\4.Developer\Andy\EnemyDamageSFXHandler\EnemyDamageVoiceSFXHandler.cs` | Handles damage voice SFX |
| `EnemyDamageVoiceSFXHandler|FeedbackVoice` | Feedback voice field |
| `AudioComponent` | `C:\Assets\1.HonoHime\Core\Character\DamageSystem\Component\AudioComponent.cs` | Damage system audio |
### Enemy Attack States Found:
```
SukebanShibaTwin|JumpAttackState
BoneGirl_GroundAttack|FinishState
BoneGirl_GrabAttack|BoneGirl_GrabAttackState
SukenBenSquadA|LeftRightDashAttackState
AttackAction_TransitionSpecialArt
AttackAction_Charge
AttackAction_QuakingStrike
```
---
## 6. CFVOICE SYSTEM
### CFVoiceEventUtility
**File:** `C:\Assets\1.HonoHime\Core\...` (pattern found in strings)
The CFVoice system triggers voice events based on game events:
| Event Key | Description |
|-----------|-------------|
| `CFVoiceEventUtility|Battle` | Battle events |
| `CFVoiceEventUtility|Challenge` | Challenge mode |
| `CFVoiceEventUtility|Chip` | Chip collection |
| `CFVoiceEventUtility|Enemy` | **Enemy-related** - important for enemy indicators |
| `CFVoiceEventUtility|EventKey` | Generic event key |
| `CFVoiceEventUtility|HH_Behaviour` | Character behavior events |
| `CFVoiceEventUtility|HH_Skill` | Skill usage |
| `CFVoiceEventUtility|Mission` | Mission events |
| `CFVoiceEventUtility|Note` | Note collection |
| `CFVoiceEventUtility|QTE` | QTE events |
| `CFVoiceEventUtility|Rank` | Rank up |
| `CFVoiceEventUtility|Reward` | Rewards |
| `CFVoiceEventUtility|Senbei` | Item-related |
| `CFVoiceEventUtility|Timer` | Timer events |
| `CFVoiceEventUtility|Trap` | Trap activation |
| `CFVoiceEventUtility|TreasureChest` | Chest opening |
### CFVoice Handlers:
| Class | File |
|-------|------|
| `MissionCFVoiceHandler` | `C:\Assets\1.HonoHime\Core\Level\MissionController\MissionCFVoiceHandler.cs` |
| `InventoryMonitorCFVoiceHandler` | `C:\Assets\4.Developer\Andy\FmodBGMController\Scripts\InventoryMonitorCFVoiceHandler.cs` |
| `CommonFunctionVoiceHandler` | `C:\Assets\4.Developer\Andy\FmodFunctionVoice\CommonFunctionVoiceHandler.cs` |
### CFVoice Properties Found:
```
get_EVENT_CFVOICE_BATTLE
get_EVENT_CFVOICE_ENEMY
get_EVENT_CFVOICE_MISSION
get_EVENT_CFVOICE_HPCHIP
get_EVENT_CFVOICE_QTE_SHIKIGAMI
get_EVENT_CFVOICE_QTE_FLURRYBARRIER
PlayCFVoice
```
---
## KEY HOOK POINTS FOR HARMONY PATCHES
### Audio Glitch Fixes:
1. **Voice Limit Fix** - Patch `UtageFmodVoiceManager.get_CountVoice` to enforce MAX_VOICES limit
2. **Ducking Glitch** - Patch `UpdateDucking` coroutine to fix fade timing issues
3. **Voice Cutoff Fix** - Patch `StopVoiceEvent` to properly handle crossfade before stopping
### Enemy Indicator Improvements:
1. **Alert Sound** - Hook into `CFVoiceEventUtility` with `Enemy` event key
2. **Attack Sound** - Patch `EnemyAttackBase.OnEnter` / `OnAttack` methods
3. **Idle Sound Loop** - Patch `EnemyAttackBase.Idle` state entry
### Priority Hook Targets:
| Priority | Class | Method | Purpose |
|----------|-------|--------|---------|
| HIGH | `UtageFmodVoiceManager` | `UpdateDucking` | Fix ducking glitches |
| HIGH | `UtageFmodVoiceManager` | `PlayVoice` | Voice limit enforcement |
| HIGH | `CFVoiceEventUtility` | `PlayCFVoice` | Enemy voice routing |
| MEDIUM | `FMODSoundManager` | `SetVolume` | Volume clamping |
| MEDIUM | `FMODSceneTransitionController` | `InitVolumeTransition` | Scene transition audio |
| MEDIUM | `EnemyDamageVoiceSFXHandler` | `FeedbackVoice` | Damage audio |
| LOW | `SnapshotManager` | `ApplySnapshot` | Reverb/snapshot control |
---
## SOURCE FILE REFERENCE LIST
### Voice Management:
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\UtageFmodVoiceManager.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\UtageFmodVoiceSetting.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\VoiceEventSelector.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\FmodVoiceStopper.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotManager.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotHandle.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\DEAudioFadeManager.cs`
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\FmodSoundDebugger.cs`
### FMOD Core:
- `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODSoundManager.cs`
- `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODUtility.cs`
- `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODManager.cs`
### Enemy Audio:
- `C:\Assets\1.HonoHime\Core\Character\Core\EnemyAttackBase.cs`
- `C:\Assets\1.HonoHime\Core\Character\EnemyActionManager\EnemyActionManager.cs`
- `C:\Assets\4.Developer\Andy\EnemyDamageSFXHandler\EnemyDamageVoiceSFXHandler.cs`
- `C:\Assets\1.HonoHime\Core\Character\DamageSystem\Component\AudioComponent.cs`
### CFVoice System:
- (Source path partially truncated, but class is `CFVoiceEventUtility`)
- `C:\Assets\1.HonoHime\Core\Level\MissionController\MissionCFVoiceHandler.cs`
- `C:\Assets\4.Developer\Andy\FmodFunctionVoice\CommonFunctionVoiceHandler.cs`
### Scene/BGM:
- `C:\Assets\4.Developer\Andy\FMODBooster\FMODSceneTransitionController.cs`
- `C:\Assets\4.Developer\Andy\FmodBGMController\Scripts\MainBGMManager.cs`
- `C:\Assets\4.Developer\Andy\FmodBGMController\Scripts\FmodBGMController.cs`
### SFX Events:
- `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventRef.cs`
- `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventSO.cs`
- `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventDispatcher.cs`

90
docs/EXISTING_TOOLS.md Normal file
View File

@@ -0,0 +1,90 @@
# Existing Modding Tools for HomuraHime
## Recommended: CinematicUnityExplorer (295 stars)
**Purpose:** In-game UI for exploring, debugging, and modifying Unity games with cinematic tools
**Download:**
```
https://github.com/originalnicodr/CinematicUnityExplorer/releases/latest/download/CinematicUnityExplorer.BepInEx5.Mono.zip
```
**Installation:**
1. Download `CinematicUnityExplorer.BepInEx5.Mono.zip`
2. Extract to: `C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\`
3. Launch game - press **F5** to open
**Features:**
| Feature | Description |
|---------|-------------|
| Object Search | Find audio manager types (FmodVoiceManager, CFVoiceEventUtility, etc.) |
| Inspector | View/edit component values in real-time |
| Hook Manager | Create Harmony patches at runtime (no rebuild needed) |
| C# Console | Execute code live to test audio triggers |
| Mouse Inspect | Click objects to inspect them |
| Freecam | Cinematic camera for screenshots/videos |
| Lights Manager | Add/modify scene lighting |
| Camera Paths | Create cinematic camera sequences |
| Animator Control | Pose characters for screenshots |
---
## Alternative: Yukieiji UnityExplorer (809 stars)
**Vanilla UnityExplorer** - if you don't need cinematic features
**Download:**
```
https://github.com/yukieiji/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip
```
---
## Why Keep Our Custom AudioMixerGUI?
Our **AudioMixerGUI** provides audio-specific sliders and quick testing:
| Feature | CinematicUnityExplorer | Our AudioMixerGUI |
|---------|----------------------|-------------------|
| Audio-specific sliders | No | Yes |
| One-click enemy cue testing | Via C# Console | Yes |
| Voice count monitoring | Manual via Inspector | Yes |
| Config persistence | No | Yes (via BepInEx config) |
**Recommendation:** Use **BOTH**
- CinematicUnityExplorer (F5) for debugging, object browsing, screenshots
- Our AudioMixerGUI (F1) for audio parameter tweaking
---
## Quick Start with CinematicUnityExplorer
### Finding Audio Types
1. Press **F5** to open CinematicUnityExplorer
2. Go to **Object Search** tab
3. Search for: `FmodVoiceManager`, `CFVoiceEventUtility`, `VoiceManager`, `SnapshotManager`
4. Click results to inspect
### Creating Runtime Patches
1. Go to **Hook Manager** tab
2. Enter class name (e.g., `CFVoiceEventUtility`)
3. Select method (e.g., `PlayCFVoice`)
4. Click to generate patch code
5. Edit and apply at runtime
### Testing Audio Triggers via C# Console
```
// Example - trigger enemy alert sound
CFVoiceEventUtility.PlayCFVoice("Enemy_Alert");
```
---
## Default Hotkeys
| Key | Action |
|-----|--------|
| **F5** | Toggle CinematicUnityExplorer |
| **F1** | Toggle our AudioMixerGUI |
For CinematicUnityExplorer freecam controls, see their documentation.

View File

@@ -0,0 +1,264 @@
# Phase 3: FMOD Bank Modifications Guide
## Overview
This phase covers extracting and modifying FMOD audio banks using FMOD Studio and AssetRipper.
**Prerequisites:**
- FMOD Studio 2.02.x installed (free from fmod.com)
- AssetRipper installed
- Game installed at: `C:\apps\steam\steamapps\common\Homura Hime\`
---
## Step 1: Extract FMOD Banks with AssetRipper
### 1.1 Launch AssetRipper
```
Location: C:\projects\HomuraHime-Mods\tools\AssetRipper\AssetRipper.exe
```
### 1.2 Load HomuraHime
1. Click **File****Load** (or drag folder)
2. Navigate to: `C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\`
3. Select the folder and click **Open**
### 1.3 Export Settings
1. Click **File****Export**
2. Choose **Resources** (not Asset Directories)
3. Set output path: `C:\projects\HomuraHime-Mods\extracted_assets\`
### 1.4 Locate FMOD Banks
After export, find banks at:
```
C:\projects\HomuraHime-Mods\extracted_assets\StreamingAssets\FMOD\Desktop\
```
---
## Step 2: Open Banks in FMOD Studio
### 2.1 Launch FMOD Studio
```
Location: C:\Program Files\FMOD Studio\FMOD Studio.exe
```
### 2.2 Open Project (or Banks Directly)
**Option A:** If you have the original FMOD project (unlikely for commercial game):
- File → Open Project → Select `.fprojekt` file
**Option B:** Open bank files directly:
- File → Open Bank → Select `Master.bank` from the extracted folder
- FMOD will automatically load related banks
### 2.3 Bank Organization
In FMOD Studio, you'll see:
- **Events**: Sound events (grouped by banks: SFX, Ambience, BGM, Voice)
- **Audio Tables**: Banks, VCA groups, snapshots
- **Mixer**: Master output with routing
---
## Step 3: Voice Limit Improvements
### 3.1 Find Problematic Events
Search for events with high polyphony:
1. In FMOD Studio, select **SFX.bank** in the Banks panel
2. Look at event properties in the Inspector
3. Check **Maximum Instances** ( polyphony)
### 3.2 Adjust Voice Aging
For events that cause glitches:
1. Right-click event → **Properties**
2. Go to **Voices** tab
3. Settings to adjust:
```
Max Instances: 16 (from default 32)
Voice Stealing: Steal Oldest
Voice Aging: 1.0 seconds (faster cleanup)
```
### 3.3 Key Events to Check
In `SFX.bank`:
- Attack impact sounds (often triggered rapidly)
- Footstep/landing sounds (PreLoad banks)
- Enemy alert/warning sounds
- UI feedback sounds
---
## Step 4: Audio Ducking Improvements
### 4.1 Access Mixer
1. Click **Mixer** tab in FMOD Studio
2. You'll see VCA groups and the Master bus
### 4.2 Configure Ducking Groups
Look for:
- **Speech** (Voice channel)
- **SFX** (Sound effects)
- **Music** (BGM)
### 4.3 Adjust Ducking Parameters
For the Master or speech bus:
1. Right-click → **Add Effect** → **Duck**
2. Configure:
```
Threshold: -20 dB (triggers at this volume)
Rate: 100% (fade speed)
Attack: 10 ms (fast attack)
Release: 200 ms (slower release)
```
### 4.4 Create/Modify Snapshots
Snapshots control ducking behavior:
1. In the **Audio Tables** panel, select **Snapshots**
2. Find existing battle/start snapshots
3. Modify duck amounts:
```
Music Duck Amount: -12 dB (not too aggressive)
SFX Duck Amount: -6 dB
```
---
## Step 5: Enemy Behavior Indicator Improvements
### 5.1 Locate Enemy Audio Events
**In `Ambience.bank`:**
- `enemy_idle_loop` - Enemy ambient when not engaged
- `enemy_alert` - Enemy noticing player
- `enemy_chase` - Enemy pursuing
**In `SFX.bank`:**
- Attack indicator sounds
- Danger/warning cues
### 5.2 Increase Volume on Behavior Sounds
1. Find enemy-related events
2. Select the event → Inspector
3. **Volume** slider: Increase by 3-6 dB
```
Before: 0.0 dB
After: +3.0 dB to +6.0 dB
```
### 5.3 Add Pitch Variation
For more distinct enemy states:
1. Select enemy event (e.g., alert)
2. Add **Pitch** automation or randomizer
3. Set variation:
```
Center: 0 semitones
Range: ±2 semitones random
```
### 5.4 Add 3D Positioning
For directional audio:
1. Select event → **3D** checkbox
2. Configure:
```
Min Distance: 1.0 m
Max Distance: 20.0 m
Rolloff: Linear
```
3. This makes enemy sounds come from their position
### 5.5 Create State Layers
For distinct enemy behavior indicators, add parameters:
1. Right-click event → **Add Parameter**
2. Name: `EnemyState`
3. Values: `0=Idle, 1=Alert, 2=Attacking`
4. Add automation for volume/effect per state
---
## Step 6: Reimport Modified Banks
### 6.1 Save Changes
1. In FMOD Studio: **File** → **Save**
2. Banks will auto-update in the project folder
### 6.2 Export Banks
1. **File** → **Export** → **banks**
2. Choose the extracted `Desktop\` folder
3. Confirm overwrite
### 6.3 Verify Export
Check that modified `.bank` files have new timestamps.
---
## Step 7: Backup Original Banks
**CRITICAL - Before testing:**
```
mkdir "C:\projects\HomuraHime-Mods\backup_banks_original"
copy "C:\projects\HomuraHime-Mods\extracted_assets\StreamingAssets\FMOD\Desktop\*.bank"
"C:\projects\HomuraHime-Mods\backup_banks_original\"
```
---
## File Locations Reference
### Game Banks (Read-Only Backup)
```
C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\StreamingAssets\FMOD\Desktop\
```
### Extracted for Editing
```
C:\projects\HomuraHime-Mods\extracted_assets\StreamingAssets\FMOD\Desktop\
```
### Modified Banks (Copy to Game)
```
C:\projects\HomuraHime-Mods\modified_banks\FMOD\Desktop\
```
### Backup Original
```
C:\projects\HomuraHime-Mods\backup_banks_original\
```
---
## Banks to Prioritize
| Priority | Bank | Reason |
|----------|------|--------|
| HIGH | `SFX.bank` | Contains attack/impact sounds that glitch |
| HIGH | `Ambience.bank` | Enemy behavior indicators |
| MEDIUM | `Master.bank` | Contains mixer and routing |
| MEDIUM | `Voice.bank` | May need voice limit adjustments |
| LOW | `BGM*.bank` | Background music - less critical |
---
## Verification Checklist
After modifications:
- [ ] Banks export successfully from FMOD Studio
- [ ] New bank files are in `modified_banks\`
- [ ] Original banks backed up
- [ ] Test game loads without FMOD errors
- [ ] Audio glitches reduced in combat
- [ ] Enemy indicators more audible
---
## Next Steps
After completing FMOD modifications:
- Phase 4: BepInEx plugin deployment
- Phase 5: Testing and iteration
- Phase 6: Distribution

297
docs/TESTING_PHASE5.md Normal file
View File

@@ -0,0 +1,297 @@
# Phase 5: Testing & Iteration Guide
## Overview
This phase documents the testing protocol for validating audio mod fixes and improvements.
**Note:** Actual testing must be performed by playing the game. This document provides the testing workflow and evaluation criteria.
---
## Pre-Testing Checklist
### 1. BepInEx Installation
- [ ] BepInEx installed in game directory
- [ ] `winhttp.dll` present in game root
- [ ] `BepInEx\LogOutput.log` generated after first run
### 2. Plugin Installation
- [ ] `HomuraHimeAudioMod.dll` in `BepInEx\plugins\`
- [ ] Config file generated at `BepInEx\config\com.homurahime.audiomod.cfg`
### 3. Original Bank Backup
- [ ] Original banks backed up to `backup_banks_original\`
### 4. FMOD Banks (Optional)
- [ ] Modified banks copied to game directory (if using Phase 3 FMOD changes)
---
## Testing Scenarios
### Test 1: Quiet Exploration (Baseline)
**Purpose:** Verify mod doesn't break normal audio
**Steps:**
1. Launch game with mod enabled
2. Start new game or load save
3. Move through quiet area (hub, menu, cutscenes)
4. Listen for 5+ minutes
**Success Criteria:**
- [ ] No audio distortion or pops
- [ ] BGM plays smoothly without cutting out
- [ ] UI sounds (menu navigation) work correctly
- [ ] No crashes or errors in LogOutput.log
**Log Check:**
```
Filter: Look for "HomuraHimeAudio" entries
Expected: "initialized successfully"
```
---
### Test 2: Combat with 3+ Enemies
**Purpose:** Test voice limit fixes under moderate load
**Steps:**
1. Find an area with 3+ enemies (early game stages)
2. Engage in combat
3. Observe audio behavior during:
- Multiple attack impacts
- Enemy death sounds
- Player attack sounds
- Dodge/roll sounds
**Success Criteria:**
- [ ] No audio distortion during impact sounds
- [ ] Sounds don't cut out prematurely
- [ ] Enemy voices don't override each other harshly
- [ ] Can distinguish individual sound sources
**Failure Signs:**
- Crackling/popping sounds
- Sudden audio cutoff
- "Muffled" audio
- Missing sound effects
---
### Test 3: Boss Fights (Heavy Load)
**Purpose:** Stress test audio system
**Steps:**
1. Enter boss arena
2. Engage boss
3. Observe during:
- Boss special attacks
- Phase transitions
- Multiple minions spawning
- Intense combo sequences
**Success Criteria:**
- [ ] No audio glitches during phase transitions
- [ ] Music continues playing smoothly
- [ ] Voice lines play without cutoff
- [ ] Attack sounds remain distinguishable
**Challenge:** If glitches occur, reduce `MaxVoiceCount` in config
---
### Test 4: Swarm Enemy Encounters
**Purpose:** Test rapid sound triggering
**Steps:**
1. Find area with many weak enemies (swarms)
2. Use area-of-effect attacks
3. Observe:
- Rapid footstep sounds
- Multiple death sounds
- Attack impact sounds overlapping
**Success Criteria:**
- [ ] No stuttering or repeating sounds
- [ ] Audio doesn't "stick" or loop
- [ ] Clean sound transitions
---
### Test 5: Enemy Behavior State Changes
**Purpose:** Validate enemy audio indicator improvements
**Steps:**
1. Approach enemies and observe state changes
2. Listen for audio cues during:
- Enemy idle (subtle ambient)
- Enemy alert (noticing player)
- Enemy chase (pursuing)
- Enemy attack (warning + strike)
**Success Criteria:**
| State | Expected Audio |
|-------|----------------|
| Idle | Subtle, low-volume ambient |
| Alert | Distinct warning/notice sound |
| Chase | Intensified pursuit audio |
| Attack | Clear attack indicator + impact |
**Rating Scale:**
- 1: Cannot hear/identify
- 2: Barely audible
- 3: Audible but unclear
- 4: Clear and distinguishable
- 5: Very distinct and helpful
---
## Metrics Tracking
### Audio Glitch Rating
| Rating | Description |
|--------|-------------|
| 0 | No glitches ever |
| 1 | Rare glitches (<1 per minute) |
| 2 | Occasional glitches (1-5 per minute) |
| 3 | Frequent glitches (>5 per minute) |
| 4 | Constant glitches |
### Target: Rating 0-1 for all scenarios
---
## Configuration Tuning
### If Distortion/Pops Occur
```ini
[VoiceManager]
MaxVoiceCount = 64 ; Reduce from 128
[Ducking]
DuckFadeTime = 0.08 ; Increase for smoother transitions
```
### If Sounds Cut Out
```ini
[VoiceManager]
MaxVoiceCount = 256 ; Increase if hardware supports
EnableVoiceLimitFix = true
```
### If Enemy Cues Unclear
```ini
[EnemyAudio]
EnemyIndicatorBoost = 1.5
AlertSoundBoost = 1.6
AttackSoundBoost = 1.4
```
---
## Iteration Loop
```
Test Scenario
┌─────────────┐
│ Glitch? │
└─────────────┘
┌──┴──┐
Yes No
│ │
▼ ▼
Adjust Next
Config Test
Rebuild & Redeploy
Retest
```
---
## Log Analysis
### Enable Debug Logging
```ini
[General]
EnableDebugLogging = true
```
### Check Log During Gameplay
```
BepInEx\LogOutput.log
```
### Key Log Entries
| Entry | Meaning |
|-------|---------|
| `HomuraHime Audio Mod initialized` | Plugin loaded OK |
| `Voice count X exceeds limit Y` | Voice limiting active |
| `DuckVolume adjusted` | Ducking patch working |
| `Enemy voice event: X` | Enemy audio hook active |
---
## Test Report Template
```markdown
## Test Report - [Date]
### Environment
- Game Version: [Check in game]
- Mod Version: 1.0.0
- Config: [Default/Custom]
### Results
| Scenario | Glitch Rating | Notes |
|----------|---------------|-------|
| Quiet Exploration | [0-4] | |
| 3+ Enemy Combat | [0-4] | |
| Boss Fight | [0-4] | |
| Swarm Enemies | [0-4] | |
| Enemy State Cues | [1-5] | |
### Configuration Changes Made
```ini
[Section]
Setting = Value
```
### Overall Assessment
[ ] Ready for distribution
[ ] Needs iteration
### Next Steps
1.
2.
```
---
## Next: Phase 6
Once testing yields satisfactory results (glitch rating 0-1, cue clarity 4-5):
- Proceed to Phase 6: Distribution
- Package mod for sharing
- Write installation instructions

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env pwsh
# Build script for HomuraHimeAudioMod
# Requires: .NET SDK 4.8 or .NET Framework 4.8
param(
[string]$Configuration = "Release",
[string]$OutputDir = $null
)
$ErrorActionPreference = "Stop"
$ProjectDir = $PSScriptRoot
$ProjectFile = Join-Path $ProjectDir "HomuraHimeAudioMod.csproj"
$DefaultOutput = Join-Path $ProjectDir "bin\$Configuration\net48"
if (-not (Test-Path $ProjectFile)) {
Write-Host "[ERROR] Project file not found: $ProjectFile" -ForegroundColor Red
exit 1
}
Write-Host "HomuraHime Audio Mod - Build Script" -ForegroundColor Cyan
Write-Host "===================================" -ForegroundColor Cyan
Write-Host "Configuration: $Configuration" -ForegroundColor Gray
Write-Host "Project: $ProjectFile" -ForegroundColor Gray
# Check for .NET SDK
$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue
if (-not $dotnet) {
Write-Host "[ERROR] .NET SDK not found. Please install .NET Framework 4.8 SDK or .NET SDK." -ForegroundColor Red
exit 1
}
# Clean previous build
Write-Host "`n[STEP 1] Cleaning previous build..." -ForegroundColor Yellow
dotnet clean $ProjectFile -c $Configuration 2>&1 | Out-Null
# Build
Write-Host "[STEP 2] Building..." -ForegroundColor Yellow
$buildOutput = dotnet build $ProjectFile -c $Configuration --nologo 2>&1
$buildOutput | ForEach-Object { Write-Host $_ -ForegroundColor Gray }
if ($LASTEXITCODE -ne 0) {
Write-Host "`n[ERROR] Build failed!" -ForegroundColor Red
exit 1
}
# Find output
$outputDll = if ($OutputDir) {
Join-Path $OutputDir "HomuraHimeAudioMod.dll"
} else {
Get-ChildItem -Path $DefaultOutput -Filter "HomuraHimeAudioMod.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }
}
if ($outputDll -and (Test-Path $outputDll)) {
$fileInfo = Get-Item $outputDll
Write-Host "`n[OK] Build successful!" -ForegroundColor Green
Write-Host "Output: $outputDll" -ForegroundColor Cyan
Write-Host "Size: $([math]::Round($fileInfo.Length / 1024, 2)) KB" -ForegroundColor Gray
Write-Host "Built: $($fileInfo.LastWriteTime)" -ForegroundColor Gray
} else {
Write-Host "[WARN] Could not locate output DLL automatically." -ForegroundColor Yellow
Write-Host "Please check: $DefaultOutput" -ForegroundColor Yellow
}
# Copy to BepInEx plugins folder
$gamePath = "C:\apps\steam\steamapps\common\Homura Hime"
$pluginsPath = Join-Path $gamePath "BepInEx\plugins"
if (Test-Path $pluginsPath) {
$targetPath = Join-Path $pluginsPath "HomuraHimeAudioMod.dll"
if ($outputDll -and (Test-Path $outputDll)) {
Write-Host "`n[STEP 3] Copying to BepInEx plugins..." -ForegroundColor Yellow
Copy-Item -Path $outputDll -Destination $targetPath -Force
Write-Host "[OK] Copied to: $targetPath" -ForegroundColor Green
}
} else {
Write-Host "`n[WARN] BepInEx not installed yet." -ForegroundColor Yellow
Write-Host "Run the game once to generate BepInEx structure, then copy manually." -ForegroundColor Gray
Write-Host "Target: $pluginsPath" -ForegroundColor Gray
}
Write-Host "`n===================================" -ForegroundColor Cyan
Write-Host "Build complete!" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host " 1. Launch HomuraHime via Steam" -ForegroundColor Cyan
Write-Host " 2. Check BepInEx\LogOutput.log for mod initialization" -ForegroundColor Cyan
Write-Host " 3. Adjust settings in BepInEx\config\com.homurahime.audiomod.cfg" -ForegroundColor Cyan

View File

@@ -6,28 +6,71 @@
<RootNamespace>HomuraHimeAudioMod</RootNamespace>
<Version>1.0.0</Version>
<Authors>Ed</Authors>
<Company>HomuraHime Modding</Company>
<Product>HomuraHime Audio Mod</Product>
<Description>Audio improvements mod for HomuraHime - fixes glitches and improves enemy indicators</Description>
<GameAssemblyPath>C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\Managed</GameAssemblyPath>
<Copyright>Copyright 2026</Copyright>
<GamePath>C:\apps\steam\steamapps\common\Homura Hime</GamePath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="0Harmony" Version="2.2.2" />
<PackageReference Include="BepInEx" Version="5.4.21" />
</ItemGroup>
<ItemGroup>
<Reference Include="FMODUnity">
<HintPath>$(GameAssemblyPath)\FMODUnity.dll</HintPath>
<Reference Include="0Harmony">
<HintPath>$(GamePath)\BepInEx\core\0Harmony.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="BepInEx">
<HintPath>$(GamePath)\BepInEx\core\BepInEx.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>$(GameAssemblyPath)\UnityEngine.AudioModule.dll</HintPath>
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.UI.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.UIModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="TMPro">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\Unity.TextMeshPro.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="FMODUnity">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\FMODUnity.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>$(GameAssemblyPath)\Assembly-CSharp.dll</HintPath>
<HintPath>$(GamePath)\HomuraHime_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="README.md" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,92 @@
using BepInEx;
using BepInEx.Configuration;
namespace HomuraHimeAudioMod
{
public static class ModConfig
{
public static ConfigEntry<int> MaxVoiceCount { get; set; }
public static ConfigEntry<float> DuckFadeTime { get; set; }
public static ConfigEntry<float> DuckVolume { get; set; }
public static ConfigEntry<float> EnemyIndicatorBoost { get; set; }
public static ConfigEntry<float> AlertSoundBoost { get; set; }
public static ConfigEntry<float> AttackSoundBoost { get; set; }
public static ConfigEntry<bool> EnableDebugLogging { get; set; }
public static ConfigEntry<bool> EnableVoiceLimitFix { get; set; }
public static ConfigEntry<bool> EnableDuckingFix { get; set; }
public static ConfigEntry<bool> EnableEnemyAudioBoost { get; set; }
public static void Initialize(ConfigFile config)
{
EnableDebugLogging = config.Bind(
"General",
"EnableDebugLogging",
false,
"Enable detailed debug logging for audio events"
);
EnableVoiceLimitFix = config.Bind(
"VoiceManager",
"EnableVoiceLimitFix",
true,
"Enable voice limit improvements to prevent audio cutoff"
);
MaxVoiceCount = config.Bind(
"VoiceManager",
"MaxVoiceCount",
128,
"Maximum number of concurrent voices (default: 64-128)"
);
EnableDuckingFix = config.Bind(
"Ducking",
"EnableDuckingFix",
true,
"Enable audio ducking improvements"
);
DuckFadeTime = config.Bind(
"Ducking",
"DuckFadeTime",
0.05f,
"Fade time for audio ducking (seconds)"
);
DuckVolume = config.Bind(
"Ducking",
"DuckVolume",
0.3f,
"Volume level during ducking (0.0-1.0)"
);
EnableEnemyAudioBoost = config.Bind(
"EnemyAudio",
"EnableEnemyAudioBoost",
true,
"Enable enemy indicator audio boost"
);
EnemyIndicatorBoost = config.Bind(
"EnemyAudio",
"EnemyIndicatorBoost",
1.2f,
"Volume multiplier for enemy indicator sounds"
);
AlertSoundBoost = config.Bind(
"EnemyAudio",
"AlertSoundBoost",
1.3f,
"Volume multiplier for enemy alert sounds"
);
AttackSoundBoost = config.Bind(
"EnemyAudio",
"AttackSoundBoost",
1.15f,
"Volume multiplier for enemy attack sounds"
);
}
}
}

View File

@@ -1,19 +1,194 @@
using System;
using System.Collections;
using HarmonyLib;
using UnityEngine;
namespace HomuraHimeAudioMod.Patches
{
/// <summary>
/// Patches for audio ducking system improvements.
/// TO BE IMPLEMENTED AFTER PHASE 2 CODE ANALYSIS
/// </summary>
public class AudioDuckingPatch
{
public static void Apply(ref Harmony harmony)
{
// TODO: After Phase 2, implement patches for:
// - Improve ducking fade times
// - Adjust duck amounts for combat
// - Add custom ducking rules
if (!ModConfig.EnableDuckingFix.Value)
{
Plugin.Log.LogInfo("AudioDucking patches disabled in config");
return;
}
Plugin.Log.LogInfo("Applying AudioDucking patches...");
var snapshotManagerType = FindSnapshotManagerType();
if (snapshotManagerType != null)
{
Plugin.Log.LogInfo($"Found SnapshotManager type: {snapshotManagerType.FullName}");
PatchSnapshotApply(harmony, snapshotManagerType);
}
var fadeManagerType = FindFadeManagerType();
if (fadeManagerType != null)
{
Plugin.Log.LogInfo($"Found DEAudioFadeManager type: {fadeManagerType.FullName}");
PatchFadeManager(harmony, fadeManagerType);
}
Plugin.Log.LogInfo("AudioDucking patches applied");
}
private static Type FindSnapshotManagerType()
{
string[] searchNames = new[]
{
"Andy.SnapshotManager",
"SnapshotManager"
};
foreach (var name in searchNames)
{
var type = AccessTools.TypeByName(name);
if (type != null)
return type;
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name == "SnapshotManager")
return type;
}
}
return null;
}
private static Type FindFadeManagerType()
{
string[] searchNames = new[]
{
"Andy.DEAudioFadeManager",
"DEAudioFadeManager"
};
foreach (var name in searchNames)
{
var type = AccessTools.TypeByName(name);
if (type != null)
return type;
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name == "DEAudioFadeManager")
return type;
}
}
return null;
}
private static void PatchSnapshotApply(Harmony harmony, Type targetType)
{
try
{
var applySnapshotMethod = AccessTools.Method(targetType, "ApplySnapshot");
if (applySnapshotMethod != null)
{
harmony.Patch(
applySnapshotMethod,
prefix: new HarmonyMethod(typeof(AudioDuckingPatch), nameof(ApplySnapshotPrefix))
);
Plugin.Log.LogInfo("Patched ApplySnapshot");
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching ApplySnapshot: {ex.Message}");
}
}
private static void PatchFadeManager(Harmony harmony, Type targetType)
{
try
{
var fadeInMethod = AccessTools.Method(targetType, "FadeIn");
var fadeOutMethod = AccessTools.Method(targetType, "FadeOut");
if (fadeInMethod != null)
{
harmony.Patch(
fadeInMethod,
prefix: new HarmonyMethod(typeof(AudioDuckingPatch), nameof(FadeInPrefix))
);
Plugin.Log.LogInfo("Patched FadeIn");
}
if (fadeOutMethod != null)
{
harmony.Patch(
fadeOutMethod,
prefix: new HarmonyMethod(typeof(AudioDuckingPatch), nameof(FadeOutPrefix))
);
Plugin.Log.LogInfo("Patched FadeOut");
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching FadeManager: {ex.Message}");
}
}
private static bool ApplySnapshotPrefix(ref string snapshotName, ref float fadeTime)
{
float configuredFadeTime = ModConfig.DuckFadeTime.Value;
if (fadeTime < configuredFadeTime)
{
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"Adjusting fade time from {fadeTime} to {configuredFadeTime}");
}
fadeTime = configuredFadeTime;
}
if (ModConfig.EnableDebugLogging.Value && !string.IsNullOrEmpty(snapshotName))
{
if (snapshotName.Contains("Battle") || snapshotName.Contains("Combat"))
{
Plugin.Log.LogDebug($"Battle/Combat snapshot: {snapshotName}");
}
}
return true;
}
private static bool FadeInPrefix(ref float fadeTime)
{
const float minFadeIn = 0.01f;
if (fadeTime < minFadeIn)
{
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"FadeIn adjusted: {fadeTime} -> {minFadeIn}");
}
fadeTime = minFadeIn;
}
return true;
}
private static bool FadeOutPrefix(ref float fadeTime)
{
const float minFadeOut = 0.05f;
if (fadeTime < minFadeOut)
{
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"FadeOut adjusted: {fadeTime} -> {minFadeOut}");
}
fadeTime = minFadeOut;
}
return true;
}
}
}

View File

@@ -1,19 +1,243 @@
using System;
using HarmonyLib;
namespace HomuraHimeAudioMod.Patches
{
/// <summary>
/// Patches for enemy behavior audio indicator improvements.
/// TO BE IMPLEMENTED AFTER PHASE 2 CODE ANALYSIS
/// </summary>
public class EnemyAudioPatch
{
public static void Apply(ref Harmony harmony)
{
// TODO: After Phase 2, implement patches for:
// - Boost enemy indicator volumes
// - Improve state change audio cues
// - Add debug logging for enemy audio
if (!ModConfig.EnableEnemyAudioBoost.Value)
{
Plugin.Log.LogInfo("EnemyAudio patches disabled in config");
return;
}
Plugin.Log.LogInfo("Applying EnemyAudio patches...");
PatchEnemyAttackBase(harmony);
PatchCFVoiceEventUtility(harmony);
PatchEnemyDamageVoiceSFXHandler(harmony);
Plugin.Log.LogInfo("EnemyAudio patches applied");
}
private static void PatchEnemyAttackBase(Harmony harmony)
{
try
{
var enemyAttackBaseType = FindEnemyAttackBaseType();
if (enemyAttackBaseType != null)
{
Plugin.Log.LogInfo($"Found EnemyAttackBase type: {enemyAttackBaseType.FullName}");
string[] attackMethods = new[] { "Attack", "OnAttack", "DoAttack", "AlertAction", "OnEnter", "StartAttack" };
foreach (var methodName in attackMethods)
{
var method = AccessTools.Method(enemyAttackBaseType, methodName);
if (method != null)
{
harmony.Patch(
method,
prefix: new HarmonyMethod(typeof(EnemyAudioPatch), nameof(EnemyAttackPrefix))
);
Plugin.Log.LogInfo($"Patched EnemyAttackBase.{methodName}");
}
}
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching EnemyAttackBase: {ex.Message}");
}
}
private static void PatchCFVoiceEventUtility(Harmony harmony)
{
try
{
var cfVoiceType = FindCFVoiceEventUtilityType();
if (cfVoiceType != null)
{
Plugin.Log.LogInfo($"Found CFVoiceEventUtility type: {cfVoiceType.FullName}");
var playCFVoiceMethod = AccessTools.Method(cfVoiceType, "PlayCFVoice");
if (playCFVoiceMethod != null)
{
harmony.Patch(
playCFVoiceMethod,
prefix: new HarmonyMethod(typeof(EnemyAudioPatch), nameof(PlayCFVoicePrefix))
);
Plugin.Log.LogInfo("Patched PlayCFVoice");
}
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching CFVoiceEventUtility: {ex.Message}");
}
}
private static void PatchEnemyDamageVoiceSFXHandler(Harmony harmony)
{
try
{
var handlerType = FindEnemyDamageVoiceSFXHandlerType();
if (handlerType != null)
{
Plugin.Log.LogInfo($"Found EnemyDamageVoiceSFXHandler type: {handlerType.FullName}");
var feedbackVoiceField = AccessTools.Field(handlerType, "FeedbackVoice");
if (feedbackVoiceField != null)
{
Plugin.Log.LogInfo("EnemyDamageVoiceSFXHandler.FeedbackVoice field found - injectable");
}
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching EnemyDamageVoiceSFXHandler: {ex.Message}");
}
}
private static Type FindEnemyAttackBaseType()
{
string[] searchNames = new[]
{
"EnemyAttackBase",
"Character.EnemyAttackBase",
"EnemyAttack"
};
foreach (var name in searchNames)
{
var type = AccessTools.TypeByName(name);
if (type != null)
return type;
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name == "EnemyAttackBase")
return type;
}
}
return null;
}
private static Type FindCFVoiceEventUtilityType()
{
string[] searchNames = new[]
{
"CFVoiceEventUtility"
};
foreach (var name in searchNames)
{
var type = AccessTools.TypeByName(name);
if (type != null)
return type;
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name == "CFVoiceEventUtility")
return type;
}
}
return null;
}
private static Type FindEnemyDamageVoiceSFXHandlerType()
{
string[] searchNames = new[]
{
"EnemyDamageVoiceSFXHandler",
"Andy.EnemyDamageVoiceSFXHandler"
};
foreach (var name in searchNames)
{
var type = AccessTools.TypeByName(name);
if (type != null)
return type;
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name == "EnemyDamageVoiceSFXHandler")
return type;
}
}
return null;
}
private static bool EnemyAttackPrefix(string eventKey)
{
if (!ModConfig.EnableEnemyAudioBoost.Value)
return true;
if (string.IsNullOrEmpty(eventKey))
return true;
if (ModConfig.EnableDebugLogging.Value)
{
if (eventKey.Contains("Alert") || eventKey.Contains("Warning"))
{
Plugin.Log.LogDebug($"Enemy alert: {eventKey}");
}
else if (eventKey.Contains("Attack"))
{
Plugin.Log.LogDebug($"Enemy attack: {eventKey}");
}
}
return true;
}
private static bool PlayCFVoicePrefix(ref string eventKey)
{
if (!ModConfig.EnableEnemyAudioBoost.Value)
return true;
if (string.IsNullOrEmpty(eventKey))
return true;
if (eventKey.Contains("Enemy"))
{
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"Enemy voice event: {eventKey}");
}
if (eventKey.Contains("Alert"))
{
float boost = ModConfig.AlertSoundBoost.Value;
if (boost != 1.0f)
{
Plugin.Log.LogDebug($"Alert boost: {boost}x");
}
}
else if (eventKey.Contains("Attack"))
{
float boost = ModConfig.AttackSoundBoost.Value;
if (boost != 1.0f)
{
Plugin.Log.LogDebug($"Attack boost: {boost}x");
}
}
}
return true;
}
}
}

View File

@@ -1,19 +1,186 @@
using System;
using System.Collections;
using HarmonyLib;
using UnityEngine;
namespace HomuraHimeAudioMod.Patches
{
/// <summary>
/// Patches for voice limit and voice manager improvements.
/// TO BE IMPLEMENTED AFTER PHASE 2 CODE ANALYSIS
/// </summary>
public class VoiceManagerPatch
{
public static void Apply(ref Harmony harmony)
{
// TODO: After Phase 2, implement patches for:
// - Increase max voice count
// - Improve voice stealing behavior
// - Adjust pooling sizes
if (!ModConfig.EnableVoiceLimitFix.Value && !ModConfig.EnableDuckingFix.Value)
{
Plugin.Log.LogInfo("VoiceManager patches disabled in config");
return;
}
Plugin.Log.LogInfo("Applying VoiceManager patches...");
var originalType = FindFmodVoiceManagerType();
if (originalType != null)
{
Plugin.Log.LogInfo($"Found voice manager type: {originalType.FullName}");
if (ModConfig.EnableDuckingFix.Value)
{
PatchUpdateDucking(harmony, originalType);
PatchDuckVolume(harmony, originalType);
}
if (ModConfig.EnableVoiceLimitFix.Value)
{
PatchVoiceCount(harmony, originalType);
}
}
else
{
Plugin.Log.LogWarning("FmodVoiceManager type not found - patches may not apply correctly");
}
Plugin.Log.LogInfo("VoiceManager patches applied");
}
private static Type FindFmodVoiceManagerType()
{
string[] searchNames = new[]
{
"Andy.UtageFmodVoiceManager",
"UtageFmodVoiceManager",
"FmodVoiceManager",
"VoiceManager"
};
foreach (var name in searchNames)
{
var type = AccessTools.TypeByName(name);
if (type != null)
return type;
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name.Contains("FmodVoiceManager") || type.Name.Contains("UtageFmodVoice"))
{
return type;
}
}
}
return null;
}
private static void PatchUpdateDucking(Harmony harmony, Type targetType)
{
try
{
var updateDuckingMethod = AccessTools.Method(targetType, "UpdateDucking");
if (updateDuckingMethod != null)
{
harmony.Patch(
updateDuckingMethod,
postfix: new HarmonyMethod(typeof(VoiceManagerPatch), nameof(UpdateDuckingPostfix))
);
Plugin.Log.LogInfo("Patched UpdateDucking");
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching UpdateDucking: {ex.Message}");
}
}
private static void PatchVoiceCount(Harmony harmony, Type targetType)
{
try
{
var countVoiceProperty = AccessTools.Property(targetType, "CountVoice");
if (countVoiceProperty != null)
{
var getter = countVoiceProperty.GetGetMethod();
if (getter != null)
{
harmony.Patch(
getter,
postfix: new HarmonyMethod(typeof(VoiceManagerPatch), nameof(CountVoicePostfix))
);
Plugin.Log.LogInfo("Patched CountVoice getter");
}
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching CountVoice: {ex.Message}");
}
}
private static void PatchDuckVolume(Harmony harmony, Type targetType)
{
try
{
var duckVolumeProperty = AccessTools.Property(targetType, "DuckVolume");
if (duckVolumeProperty != null)
{
var setter = duckVolumeProperty.GetSetMethod();
if (setter != null)
{
harmony.Patch(
setter,
prefix: new HarmonyMethod(typeof(VoiceManagerPatch), nameof(DuckVolumePrefix))
);
Plugin.Log.LogInfo("Patched DuckVolume setter");
}
}
}
catch (Exception ex)
{
Plugin.Log.LogError($"Error patching DuckVolume: {ex.Message}");
}
}
private static void UpdateDuckingPostfix(IEnumerator self, object __instance)
{
if (__instance == null) return;
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"UpdateDucking running on {__instance.GetType().Name}");
}
var duckFadeTimeField = AccessTools.Field(__instance.GetType(), "duckFadeTime");
if (duckFadeTimeField != null)
{
duckFadeTimeField.SetValue(__instance, ModConfig.DuckFadeTime.Value);
}
}
private static void CountVoicePostfix(ref int __result)
{
int maxVoices = ModConfig.MaxVoiceCount.Value;
if (__result > maxVoices)
{
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"Voice count {__result} exceeds limit {maxVoices}, capping");
}
__result = maxVoices;
}
}
private static bool DuckVolumePrefix(ref float value)
{
float minDuck = ModConfig.DuckVolume.Value;
if (value < minDuck)
{
if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"DuckVolume {value} below minimum {minDuck}, adjusting");
}
value = minDuck;
}
return true;
}
}
}

View File

@@ -1,47 +1,47 @@
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
namespace HomuraHimeAudioMod
{
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
[BepInPlugin("com.homurahime.audiomod", "HomuraHime Audio Mod", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
public const string PLUGIN_GUID = "com.homurahime.audiomod";
public const string PLUGIN_NAME = "HomuraHime Audio Mod";
public const string PLUGIN_VERSION = "1.0.0";
private static Plugin instance;
private static ManualLogSource logger;
private Harmony harmony;
public static ManualLogSource Log => logger;
private void Awake()
{
instance = this;
logger = Logger;
harmony = new Harmony(PluginInfo.PLUGIN_GUID);
ApplyPatches();
}
ModConfig.Initialize(Config);
private void ApplyPatches()
{
Log.LogInfo($"Applying audio patches...");
// Phase 2 will populate these patch classes after code analysis
// VoiceManagerPatch.Apply(ref harmony);
// AudioDuckingPatch.Apply(ref harmony);
// EnemyAudioPatch.Apply(ref harmony);
Log.LogInfo($"Patches applied successfully");
}
private void OnDestroy()
{
harmony?.UnpatchAll();
Log.LogInfo("========================================");
Log.LogInfo("HomuraHime Audio Mod v1.0.0");
Log.LogInfo("========================================");
Log.LogInfo("");
Log.LogInfo("CONFIGURATION (edit in BepInEx/config/com.homurahime.audiomod.cfg):");
Log.LogInfo("");
Log.LogInfo(" [VoiceManager]");
Log.LogInfo($" MaxVoiceCount = {ModConfig.MaxVoiceCount.Value}");
Log.LogInfo($" EnableVoiceLimitFix = {ModConfig.EnableVoiceLimitFix.Value}");
Log.LogInfo("");
Log.LogInfo(" [Ducking]");
Log.LogInfo($" DuckFadeTime = {ModConfig.DuckFadeTime.Value}");
Log.LogInfo($" DuckVolume = {ModConfig.DuckVolume.Value}");
Log.LogInfo($" EnableDuckingFix = {ModConfig.EnableDuckingFix.Value}");
Log.LogInfo("");
Log.LogInfo(" [EnemyAudio]");
Log.LogInfo($" EnemyIndicatorBoost = {ModConfig.EnemyIndicatorBoost.Value}");
Log.LogInfo($" AlertSoundBoost = {ModConfig.AlertSoundBoost.Value}");
Log.LogInfo($" AttackSoundBoost = {ModConfig.AttackSoundBoost.Value}");
Log.LogInfo($" EnableEnemyAudioBoost = {ModConfig.EnableEnemyAudioBoost.Value}");
Log.LogInfo("");
Log.LogInfo("NOTE: Harmony patches not yet implemented.");
Log.LogInfo(" Patches will be added after code analysis.");
Log.LogInfo("");
Log.LogInfo("========================================");
Log.LogInfo("Mod loaded. Edit config file to change values.");
}
}
}

View File

@@ -1,19 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("HomuraHimeAudioMod")]
[assembly: AssemblyDescription("Audio improvements mod for HomuraHime")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HomuraHimeAudioMod")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,188 @@
# HomuraHime Audio Mod
A BepInEx mod for HomuraHime that fixes audio glitches and improves enemy behavior audio indicators.
## Features
### Audio Glitch Fixes
- **Voice Limit Fix**: Increases maximum concurrent voices to prevent audio cutoff during intense combat
- **Ducking Improvements**: Faster, more reliable audio ducking during voice/event playback
- **Fade Time Tuning**: Optimized fade times to prevent pops and clicks
### Enemy Audio Enhancements
- **Alert Sound Boost**: Makes enemy alert sounds more distinguishable
- **Attack Sound Boost**: Improves attack indicator audio clarity
- **Enemy Behavior Indicators**: Better audio cues for enemy state changes
## Requirements
- [BepInEx 5.x](https://github.com/BepInEx/BepInEx/releases)
- HomuraHime (Steam)
- .NET Framework 4.8 or .NET SDK
## Installation
### Automatic (Recommended)
1. Run `Build.ps1` script to build and install:
```powershell
cd src/HomuraHimeAudioMod
.\Build.ps1
```
### Manual
1. Build the project:
```powershell
dotnet build -c Release
```
2. Copy `HomuraHimeAudioMod.dll` to:
```
C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\
```
3. Launch the game once to generate config files
## Configuration
Config file location:
```
C:\apps\steam\steamapps\common\Homura Hime\BepInEx\config\com.homurahime.audiomod.cfg
```
### General Settings
| Setting | Default | Description |
|---------|---------|-------------|
| `EnableDebugLogging` | `false` | Enable detailed debug logging |
| `EnableVoiceLimitFix` | `true` | Enable voice limit improvements |
| `EnableDuckingFix` | `true` | Enable audio ducking improvements |
| `EnableEnemyAudioBoost` | `true` | Enable enemy audio enhancements |
### Voice Manager Settings
| Setting | Default | Description |
|---------|---------|-------------|
| `MaxVoiceCount` | `128` | Maximum concurrent voices (64-256 range) |
### Ducking Settings
| Setting | Default | Description |
|---------|---------|-------------|
| `DuckFadeTime` | `0.05` | Fade time for ducking (seconds) |
| `DuckVolume` | `0.3` | Volume level during ducking (0.0-1.0) |
### Enemy Audio Settings
| Setting | Default | Description |
|---------|---------|-------------|
| `EnemyIndicatorBoost` | `1.2` | Volume multiplier for indicators |
| `AlertSoundBoost` | `1.3` | Volume multiplier for alert sounds |
| `AttackSoundBoost` | `1.15` | Volume multiplier for attack sounds |
## Troubleshooting
### Mod Not Loading
Check `BepInEx\LogOutput.log` for errors. Common issues:
- Missing .NET Framework 4.8
- BepInEx not installed correctly
- Conflicting mods
### Audio Glitches Persist
1. Try reducing `MaxVoiceCount` if CPU is struggling
2. Increase `DuckFadeTime` for smoother transitions
3. Disable other audio mods to check for conflicts
### Game Crashes
1. Disable all patches and re-enable one at a time
2. Check for game updates that may have changed audio code
3. Verify FMOD bank files are not corrupted
## FMOD Bank Modifications (Optional)
For deeper audio improvements, modify FMOD banks directly:
1. Extract banks using AssetRipper
2. Open in FMOD Studio 2.02.x
3. Adjust voice limits, ducking, and enemy indicators
4. Reimport modified banks
See `docs/FMOD_MODIFICATION_GUIDE_PHASE3.md` for detailed instructions.
## Building from Source
### Prerequisites
- .NET Framework 4.8 SDK or .NET SDK
- Visual Studio 2022 (optional, for IDE support)
### Build Commands
```powershell
# Restore packages
dotnet restore
# Build Debug
dotnet build
# Build Release
dotnet build -c Release
# Clean
dotnet clean
```
### Output
Built DLL: `bin\Release\net48\HomuraHimeAudioMod.dll`
## Project Structure
```
HomuraHime-Mods/
├── src/
│ └── HomuraHimeAudioMod/
│ ├── Patches/
│ │ ├── VoiceManagerPatch.cs # Voice limit and ducking patches
│ │ ├── AudioDuckingPatch.cs # Snapshot and fade patches
│ │ └── EnemyAudioPatch.cs # Enemy audio enhancement patches
│ ├── Plugin.cs # Main BepInEx plugin
│ ├── ModConfig.cs # Configuration management
│ └── HomuraHimeAudioMod.csproj # Project file
├── docs/
│ ├── SETUP_PHASE1.md # Tool installation guide
│ ├── AUDIO_ANALYSIS.md # Game audio system analysis
│ ├── CODE_ANALYSIS_PHASE2.md # Deep code analysis
│ └── FMOD_MODIFICATION_GUIDE_PHASE3.md # FMOD bank editing guide
└── tools/
└── ExtractBanks.ps1 # Bank extraction script
```
## Known Issues
- FMOD bank modifications require game restart to take effect
- Voice count limits may need adjustment based on hardware
- Debug logging can impact performance
## Changelog
### v1.0.0
- Initial release
- Voice limit fix (configurable max voices)
- Audio ducking improvements
- Enemy audio indicator boost options
- BepInEx configuration support
## License
This mod is provided for personal use. Please respect the game's EULA and copyright.
## Credits
- BepInEx team for the modding framework
- FMOD for audio middleware
- Unity Technologies for game engine

103
tools/ExtractBanks.ps1 Normal file
View File

@@ -0,0 +1,103 @@
# HomuraHime Audio Mod - Bank Extraction Script
# Run this after installing AssetRipper to extract FMOD banks
param(
[string]$GamePath = "C:\apps\steam\steamapps\common\Homura Hime",
[string]$OutputPath = "C:\projects\HomuraHime-Mods\extracted_assets",
[string]$BackupPath = "C:\projects\HomuraHime-Mods\backup_banks_original"
)
$ErrorActionPreference = "Stop"
Write-Host "HomuraHime Audio Mod - Bank Extraction Script" -ForegroundColor Cyan
Write-Host "=" * 50
# Check if AssetRipper exists
$AssetRipperPath = "C:\projects\HomuraHime-Mods\tools\AssetRipper\AssetRipper.exe"
if (-not (Test-Path $AssetRipperPath)) {
Write-Host "[ERROR] AssetRipper not found at: $AssetRipperPath" -ForegroundColor Red
Write-Host "Please install AssetRipper first (see docs/SETUP_PHASE1.md)" -ForegroundColor Yellow
exit 1
}
# Check if game path exists
$GameDataPath = Join-Path $GamePath "HomuraHime_Data\StreamingAssets\FMOD\Desktop"
if (-not (Test-Path $GameDataPath)) {
Write-Host "[ERROR] Game FMOD banks not found at: $GameDataPath" -ForegroundColor Red
Write-Host "Please verify game installation" -ForegroundColor Yellow
exit 1
}
Write-Host "[INFO] Game path: $GameDataPath" -ForegroundColor Green
# Create directories
$OutputBanksPath = Join-Path $OutputPath "StreamingAssets\FMOD\Desktop"
$BackupBanksPath = $BackupPath
New-Item -ItemType Directory -Force -Path $OutputBanksPath | Out-Null
New-Item -ItemType Directory -Force -Path $BackupBanksPath | Out-Null
Write-Host "[INFO] Output path: $OutputBanksPath" -ForegroundColor Green
Write-Host "[INFO] Backup path: $BackupBanksPath" -ForegroundColor Green
# List current banks
$banks = Get-ChildItem -Path $GameDataPath -Filter "*.bank"
Write-Host "[INFO] Found $($banks.Count) FMOD bank files" -ForegroundColor Cyan
# Step 1: Backup original banks
Write-Host "`n[STEP 1] Backing up original banks..." -ForegroundColor Yellow
foreach ($bank in $banks) {
$dest = Join-Path $BackupBanksPath $bank.Name
Copy-Item -Path $bank.FullName -Destination $dest -Force
Write-Host " Backed up: $($bank.Name)" -ForegroundColor Gray
}
Write-Host "[OK] Original banks backed up" -ForegroundColor Green
# Step 2: Launch AssetRipper for extraction
Write-Host "`n[STEP 2] Launching AssetRipper..." -ForegroundColor Yellow
Write-Host " In AssetRipper:" -ForegroundColor Cyan
Write-Host " 1. File -> Load -> Select HomuraHime_Data folder" -ForegroundColor Cyan
Write-Host " 2. File -> Export -> Resources" -ForegroundColor Cyan
Write-Host " 3. Export to: $OutputPath" -ForegroundColor Cyan
Write-Host " 4. Wait for export to complete" -ForegroundColor Cyan
Write-Host " 5. Close AssetRipper" -ForegroundColor Cyan
Start-Process $AssetRipperPath
Write-Host "`n[PAUSE] Press Enter when AssetRipper export is complete..." -ForegroundColor Yellow
Read-Host
# Step 3: Verify extraction
$extractedBanksPath = Join-Path $OutputPath "StreamingAssets\FMOD\Desktop"
if (Test-Path $extractedBanksPath) {
$extractedBanks = Get-ChildItem -Path $extractedBanksPath -Filter "*.bank"
Write-Host "[OK] Found $($extractedBanks.Count) extracted bank files" -ForegroundColor Green
# List key banks
$keyBanks = @("SFX.bank", "Ambience.bank", "Master.bank", "Voice.bank")
foreach ($keyBank in $keyBanks) {
$found = $extractedBanks | Where-Object { $_.Name -eq $keyBank }
if ($found) {
Write-Host " [OK] $keyBank found ($([math]::Round($found.Length / 1MB, 2)) MB)" -ForegroundColor Green
} else {
Write-Host " [WARN] $keyBank not found" -ForegroundColor Yellow
}
}
} else {
Write-Host "[ERROR] Extraction failed - path not found: $extractedBanksPath" -ForegroundColor Red
exit 1
}
# Summary
Write-Host "`n" + "=" * 50
Write-Host "Extraction Complete!" -ForegroundColor Cyan
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host " 1. Open FMOD Studio 2.02.x" -ForegroundColor Cyan
Write-Host " 2. Open banks from: $extractedBanksPath" -ForegroundColor Cyan
Write-Host " 3. Edit banks for voice limits, ducking, enemy cues" -ForegroundColor Cyan
Write-Host " 4. See docs/FMOD_MODIFICATION_GUIDE_PHASE3.md for details" -ForegroundColor Cyan
Write-Host ""
Write-Host "Current project structure:" -ForegroundColor Gray
Write-Host " extracted_assets\StreamingAssets\FMOD\Desktop\ <- Edit these banks" -ForegroundColor Gray
Write-Host " backup_banks_original\ <- Original banks (backup)" -ForegroundColor Gray
Write-Host " modified_banks\ <- Create this folder for modified banks" -ForegroundColor Gray