Compare commits

...

9 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
14 changed files with 1256 additions and 152 deletions

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.

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.

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> <RootNamespace>HomuraHimeAudioMod</RootNamespace>
<Version>1.0.0</Version> <Version>1.0.0</Version>
<Authors>Ed</Authors> <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> <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> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="0Harmony" Version="2.2.2" /> <Reference Include="0Harmony">
<PackageReference Include="BepInEx" Version="5.4.21" /> <HintPath>$(GamePath)\BepInEx\core\0Harmony.dll</HintPath>
</ItemGroup> <Private>false</Private>
</Reference>
<ItemGroup> <Reference Include="BepInEx">
<Reference Include="FMODUnity"> <HintPath>$(GamePath)\BepInEx\core\BepInEx.dll</HintPath>
<HintPath>$(GameAssemblyPath)\FMODUnity.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> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.AudioModule"> <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> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>$(GameAssemblyPath)\Assembly-CSharp.dll</HintPath> <HintPath>$(GamePath)\HomuraHime_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="README.md" />
</ItemGroup>
</Project> </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

@@ -7,28 +7,24 @@ namespace HomuraHimeAudioMod.Patches
{ {
public class AudioDuckingPatch public class AudioDuckingPatch
{ {
public const float COMBAT_DUCK_AMOUNT = 0.25f;
public const float COMBAT_DUCK_FADE_IN = 0.03f;
public const float COMBAT_DUCK_FADE_OUT = 0.15f;
public const float VOICE_DUCK_AMOUNT = 0.35f;
public const float SFX_DUCK_AMOUNT = 0.4f;
public static void Apply(ref Harmony harmony) public static void Apply(ref Harmony harmony)
{ {
if (!ModConfig.EnableDuckingFix.Value)
{
Plugin.Log.LogInfo("AudioDucking patches disabled in config");
return;
}
Plugin.Log.LogInfo("Applying AudioDucking patches..."); Plugin.Log.LogInfo("Applying AudioDucking patches...");
var originalType = AccessTools.TypeByName("Andy.SnapshotManager"); var snapshotManagerType = FindSnapshotManagerType();
if (originalType != null) if (snapshotManagerType != null)
{ {
Plugin.Log.LogInfo($"Found SnapshotManager type: {originalType.FullName}"); Plugin.Log.LogInfo($"Found SnapshotManager type: {snapshotManagerType.FullName}");
PatchSnapshotApply(harmony, originalType); PatchSnapshotApply(harmony, snapshotManagerType);
}
else
{
Plugin.Log.LogWarning("SnapshotManager type not found");
} }
var fadeManagerType = AccessTools.TypeByName("Andy.DEAudioFadeManager"); var fadeManagerType = FindFadeManagerType();
if (fadeManagerType != null) if (fadeManagerType != null)
{ {
Plugin.Log.LogInfo($"Found DEAudioFadeManager type: {fadeManagerType.FullName}"); Plugin.Log.LogInfo($"Found DEAudioFadeManager type: {fadeManagerType.FullName}");
@@ -38,6 +34,60 @@ namespace HomuraHimeAudioMod.Patches
Plugin.Log.LogInfo("AudioDucking patches applied"); 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) private static void PatchSnapshotApply(Harmony harmony, Type targetType)
{ {
try try
@@ -91,14 +141,23 @@ namespace HomuraHimeAudioMod.Patches
private static bool ApplySnapshotPrefix(ref string snapshotName, ref float fadeTime) private static bool ApplySnapshotPrefix(ref string snapshotName, ref float fadeTime)
{ {
if (fadeTime < COMBAT_DUCK_FADE_IN) float configuredFadeTime = ModConfig.DuckFadeTime.Value;
if (fadeTime < configuredFadeTime)
{ {
fadeTime = COMBAT_DUCK_FADE_IN; if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"Adjusting fade time from {fadeTime} to {configuredFadeTime}");
}
fadeTime = configuredFadeTime;
} }
if (snapshotName != null && snapshotName.Contains("Battle")) if (ModConfig.EnableDebugLogging.Value && !string.IsNullOrEmpty(snapshotName))
{ {
Plugin.Log.LogDebug($"Battle snapshot detected: {snapshotName}, fadeTime: {fadeTime}"); if (snapshotName.Contains("Battle") || snapshotName.Contains("Combat"))
{
Plugin.Log.LogDebug($"Battle/Combat snapshot: {snapshotName}");
}
} }
return true; return true;
@@ -106,18 +165,28 @@ namespace HomuraHimeAudioMod.Patches
private static bool FadeInPrefix(ref float fadeTime) private static bool FadeInPrefix(ref float fadeTime)
{ {
if (fadeTime < 0.01f) const float minFadeIn = 0.01f;
if (fadeTime < minFadeIn)
{ {
fadeTime = 0.01f; if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"FadeIn adjusted: {fadeTime} -> {minFadeIn}");
}
fadeTime = minFadeIn;
} }
return true; return true;
} }
private static bool FadeOutPrefix(ref float fadeTime) private static bool FadeOutPrefix(ref float fadeTime)
{ {
if (fadeTime < 0.05f) const float minFadeOut = 0.05f;
if (fadeTime < minFadeOut)
{ {
fadeTime = 0.05f; if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"FadeOut adjusted: {fadeTime} -> {minFadeOut}");
}
fadeTime = minFadeOut;
} }
return true; return true;
} }

View File

@@ -1,17 +1,18 @@
using System; using System;
using HarmonyLib; using HarmonyLib;
using UnityEngine;
namespace HomuraHimeAudioMod.Patches namespace HomuraHimeAudioMod.Patches
{ {
public class EnemyAudioPatch public class EnemyAudioPatch
{ {
public const float ENEMY_INDICATOR_VOLUME_BOOST = 1.2f;
public const float ALERT_SOUND_VOLUME_BOOST = 1.3f;
public const float ATTACK_SOUND_VOLUME_BOOST = 1.15f;
public static void Apply(ref Harmony harmony) public static void Apply(ref Harmony harmony)
{ {
if (!ModConfig.EnableEnemyAudioBoost.Value)
{
Plugin.Log.LogInfo("EnemyAudio patches disabled in config");
return;
}
Plugin.Log.LogInfo("Applying EnemyAudio patches..."); Plugin.Log.LogInfo("Applying EnemyAudio patches...");
PatchEnemyAttackBase(harmony); PatchEnemyAttackBase(harmony);
@@ -25,12 +26,12 @@ namespace HomuraHimeAudioMod.Patches
{ {
try try
{ {
var enemyAttackBaseType = AccessTools.TypeByName("EnemyAttackBase"); var enemyAttackBaseType = FindEnemyAttackBaseType();
if (enemyAttackBaseType != null) if (enemyAttackBaseType != null)
{ {
Plugin.Log.LogInfo($"Found EnemyAttackBase type: {enemyAttackBaseType.FullName}"); Plugin.Log.LogInfo($"Found EnemyAttackBase type: {enemyAttackBaseType.FullName}");
var attackMethods = new[] { "Attack", "OnAttack", "DoAttack", "AlertAction" }; string[] attackMethods = new[] { "Attack", "OnAttack", "DoAttack", "AlertAction", "OnEnter", "StartAttack" };
foreach (var methodName in attackMethods) foreach (var methodName in attackMethods)
{ {
var method = AccessTools.Method(enemyAttackBaseType, methodName); var method = AccessTools.Method(enemyAttackBaseType, methodName);
@@ -44,10 +45,6 @@ namespace HomuraHimeAudioMod.Patches
} }
} }
} }
else
{
Plugin.Log.LogWarning("EnemyAttackBase type not found");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -59,7 +56,7 @@ namespace HomuraHimeAudioMod.Patches
{ {
try try
{ {
var cfVoiceType = AccessTools.TypeByName("CFVoiceEventUtility"); var cfVoiceType = FindCFVoiceEventUtilityType();
if (cfVoiceType != null) if (cfVoiceType != null)
{ {
Plugin.Log.LogInfo($"Found CFVoiceEventUtility type: {cfVoiceType.FullName}"); Plugin.Log.LogInfo($"Found CFVoiceEventUtility type: {cfVoiceType.FullName}");
@@ -74,10 +71,6 @@ namespace HomuraHimeAudioMod.Patches
Plugin.Log.LogInfo("Patched PlayCFVoice"); Plugin.Log.LogInfo("Patched PlayCFVoice");
} }
} }
else
{
Plugin.Log.LogWarning("CFVoiceEventUtility type not found");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -89,7 +82,7 @@ namespace HomuraHimeAudioMod.Patches
{ {
try try
{ {
var handlerType = AccessTools.TypeByName("EnemyDamageVoiceSFXHandler"); var handlerType = FindEnemyDamageVoiceSFXHandlerType();
if (handlerType != null) if (handlerType != null)
{ {
Plugin.Log.LogInfo($"Found EnemyDamageVoiceSFXHandler type: {handlerType.FullName}"); Plugin.Log.LogInfo($"Found EnemyDamageVoiceSFXHandler type: {handlerType.FullName}");
@@ -97,7 +90,7 @@ namespace HomuraHimeAudioMod.Patches
var feedbackVoiceField = AccessTools.Field(handlerType, "FeedbackVoice"); var feedbackVoiceField = AccessTools.Field(handlerType, "FeedbackVoice");
if (feedbackVoiceField != null) if (feedbackVoiceField != null)
{ {
Plugin.Log.LogInfo("Found FeedbackVoice field - enemy damage audio hookable"); Plugin.Log.LogInfo("EnemyDamageVoiceSFXHandler.FeedbackVoice field found - injectable");
} }
} }
} }
@@ -107,32 +100,141 @@ namespace HomuraHimeAudioMod.Patches
} }
} }
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) private static bool EnemyAttackPrefix(string eventKey)
{ {
if (!string.IsNullOrEmpty(eventKey)) if (!ModConfig.EnableEnemyAudioBoost.Value)
{ return true;
Plugin.Log.LogDebug($"Enemy attack event: {eventKey}");
if (string.IsNullOrEmpty(eventKey))
return true;
if (ModConfig.EnableDebugLogging.Value)
{
if (eventKey.Contains("Alert") || eventKey.Contains("Warning")) if (eventKey.Contains("Alert") || eventKey.Contains("Warning"))
{ {
Plugin.Log.LogDebug($"Enemy alert sound detected: {eventKey}"); Plugin.Log.LogDebug($"Enemy alert: {eventKey}");
} }
else if (eventKey.Contains("Attack")) else if (eventKey.Contains("Attack"))
{ {
Plugin.Log.LogDebug($"Enemy attack sound: {eventKey}"); Plugin.Log.LogDebug($"Enemy attack: {eventKey}");
} }
} }
return true; return true;
} }
private static bool PlayCFVoicePrefix(ref string eventKey) private static bool PlayCFVoicePrefix(ref string eventKey)
{ {
if (!ModConfig.EnableEnemyAudioBoost.Value)
return true;
if (string.IsNullOrEmpty(eventKey)) if (string.IsNullOrEmpty(eventKey))
return true; return true;
if (eventKey.Contains("Enemy")) if (eventKey.Contains("Enemy"))
{ {
Plugin.Log.LogDebug($"Enemy voice event triggered: {eventKey}"); 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; return true;

View File

@@ -7,42 +7,69 @@ namespace HomuraHimeAudioMod.Patches
{ {
public class VoiceManagerPatch public class VoiceManagerPatch
{ {
public const int MAX_VOICE_COUNT = 128;
public const float DEFAULT_DUCK_FADE_TIME = 0.05f;
public const float DEFAULT_DUCK_VOLUME = 0.3f;
public static void Apply(ref Harmony harmony) public static void Apply(ref Harmony harmony)
{ {
if (!ModConfig.EnableVoiceLimitFix.Value && !ModConfig.EnableDuckingFix.Value)
{
Plugin.Log.LogInfo("VoiceManager patches disabled in config");
return;
}
Plugin.Log.LogInfo("Applying VoiceManager patches..."); Plugin.Log.LogInfo("Applying VoiceManager patches...");
var originalType = AccessTools.TypeByName("Andy.UtageFmodVoiceManager"); var originalType = FindFmodVoiceManagerType();
if (originalType != null) if (originalType != null)
{ {
Plugin.Log.LogInfo($"Found UtageFmodVoiceManager type: {originalType.FullName}"); Plugin.Log.LogInfo($"Found voice manager type: {originalType.FullName}");
PatchUpdateDucking(harmony, originalType); if (ModConfig.EnableDuckingFix.Value)
PatchVoiceCount(harmony, originalType); {
PatchDuckVolume(harmony, originalType); PatchUpdateDucking(harmony, originalType);
PatchDuckVolume(harmony, originalType);
}
if (ModConfig.EnableVoiceLimitFix.Value)
{
PatchVoiceCount(harmony, originalType);
}
} }
else else
{ {
Plugin.Log.LogWarning("UtageFmodVoiceManager type not found by name, searching..."); Plugin.Log.LogWarning("FmodVoiceManager type not found - patches may not apply correctly");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) }
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())
{ {
foreach (var type in assembly.GetTypes()) if (type.Name.Contains("FmodVoiceManager") || type.Name.Contains("UtageFmodVoice"))
{ {
if (type.Name.Contains("FmodVoiceManager")) return type;
{
Plugin.Log.LogInfo($"Found alternative: {type.FullName}");
PatchUpdateDucking(harmony, type);
PatchVoiceCount(harmony, type);
break;
}
} }
} }
} }
Plugin.Log.LogInfo("VoiceManager patches applied"); return null;
} }
private static void PatchUpdateDucking(Harmony harmony, Type targetType) private static void PatchUpdateDucking(Harmony harmony, Type targetType)
@@ -58,10 +85,6 @@ namespace HomuraHimeAudioMod.Patches
); );
Plugin.Log.LogInfo("Patched UpdateDucking"); Plugin.Log.LogInfo("Patched UpdateDucking");
} }
else
{
Plugin.Log.LogWarning("UpdateDucking method not found");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -86,10 +109,6 @@ namespace HomuraHimeAudioMod.Patches
Plugin.Log.LogInfo("Patched CountVoice getter"); Plugin.Log.LogInfo("Patched CountVoice getter");
} }
} }
else
{
Plugin.Log.LogWarning("CountVoice property not found");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -121,37 +140,47 @@ namespace HomuraHimeAudioMod.Patches
} }
} }
private static void UpdateDuckingPostfix(IEnumerator self, UtageFmodVoiceManager __instance) private static void UpdateDuckingPostfix(IEnumerator self, object __instance)
{ {
if (__instance != null) if (__instance == null) return;
if (ModConfig.EnableDebugLogging.Value)
{ {
var duckFadeTimeField = AccessTools.Field(__instance.GetType(), "duckFadeTime"); Plugin.Log.LogDebug($"UpdateDucking running on {__instance.GetType().Name}");
if (duckFadeTimeField != null) }
{
duckFadeTimeField.SetValue(__instance, DEFAULT_DUCK_FADE_TIME); var duckFadeTimeField = AccessTools.Field(__instance.GetType(), "duckFadeTime");
} if (duckFadeTimeField != null)
{
duckFadeTimeField.SetValue(__instance, ModConfig.DuckFadeTime.Value);
} }
} }
private static void CountVoicePostfix(ref int __result) private static void CountVoicePostfix(ref int __result)
{ {
if (__result > MAX_VOICE_COUNT) int maxVoices = ModConfig.MaxVoiceCount.Value;
if (__result > maxVoices)
{ {
__result = MAX_VOICE_COUNT; if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"Voice count {__result} exceeds limit {maxVoices}, capping");
}
__result = maxVoices;
} }
} }
private static bool DuckVolumePrefix(ref float value) private static bool DuckVolumePrefix(ref float value)
{ {
if (value < DEFAULT_DUCK_VOLUME) float minDuck = ModConfig.DuckVolume.Value;
if (value < minDuck)
{ {
value = DEFAULT_DUCK_VOLUME; if (ModConfig.EnableDebugLogging.Value)
{
Plugin.Log.LogDebug($"DuckVolume {value} below minimum {minDuck}, adjusting");
}
value = minDuck;
} }
return true; return true;
} }
} }
public class UtageFmodVoiceManager : MonoBehaviour
{
}
} }

View File

@@ -1,50 +1,47 @@
using BepInEx; using BepInEx;
using BepInEx.Logging; using BepInEx.Logging;
using HarmonyLib;
namespace HomuraHimeAudioMod 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 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 static ManualLogSource logger;
private Harmony harmony;
public static ManualLogSource Log => logger; public static ManualLogSource Log => logger;
private void Awake() private void Awake()
{ {
instance = this;
logger = Logger; logger = Logger;
harmony = new Harmony(PluginInfo.PLUGIN_GUID);
Log.LogInfo($"HomuraHime Audio Mod v{PluginInfo.PLUGIN_VERSION} initializing..."); ModConfig.Initialize(Config);
ApplyPatches(); Log.LogInfo("========================================");
Log.LogInfo("HomuraHime Audio Mod v1.0.0");
Log.LogInfo("HomuraHime Audio Mod initialized successfully"); Log.LogInfo("========================================");
} Log.LogInfo("");
Log.LogInfo("CONFIGURATION (edit in BepInEx/config/com.homurahime.audiomod.cfg):");
private void ApplyPatches() Log.LogInfo("");
{ Log.LogInfo(" [VoiceManager]");
Log.LogInfo("Applying audio patches..."); Log.LogInfo($" MaxVoiceCount = {ModConfig.MaxVoiceCount.Value}");
Log.LogInfo($" EnableVoiceLimitFix = {ModConfig.EnableVoiceLimitFix.Value}");
VoiceManagerPatch.Apply(ref harmony); Log.LogInfo("");
AudioDuckingPatch.Apply(ref harmony); Log.LogInfo(" [Ducking]");
EnemyAudioPatch.Apply(ref harmony); Log.LogInfo($" DuckFadeTime = {ModConfig.DuckFadeTime.Value}");
Log.LogInfo($" DuckVolume = {ModConfig.DuckVolume.Value}");
Log.LogInfo("All patches applied successfully"); Log.LogInfo($" EnableDuckingFix = {ModConfig.EnableDuckingFix.Value}");
} Log.LogInfo("");
Log.LogInfo(" [EnemyAudio]");
private void OnDestroy() Log.LogInfo($" EnemyIndicatorBoost = {ModConfig.EnemyIndicatorBoost.Value}");
{ Log.LogInfo($" AlertSoundBoost = {ModConfig.AlertSoundBoost.Value}");
harmony?.UnpatchAll(PluginInfo.PLUGIN_GUID); 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