Skip to content

Commit 7413fc4

Browse files
authored
Merge pull request #4 from ashkeel/feature/multiple-sounds
Multiple hit sounds
2 parents 8308479 + 9bb3d54 commit 7413fc4

File tree

4 files changed

+106
-25
lines changed

4 files changed

+106
-25
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ After logging in it should look like this:
5353

5454
- *Reaction power:* How strong your model will react
5555

56-
- *Play hit sound:* If a sound effect should be played when items hit your model
57-
* Trashy loads the hit sound effect from `VTube Studio\BepInEx\plugins\Trashy\hit.mp3` Simply replace the file with any mp3 and press `Reload hit sound` in the Trashy settings
56+
- *Play hit sound:* If enabled, a sound effect will be played when items hit your model
5857

5958
### Setting up triggers
6059

@@ -107,3 +106,16 @@ Groups are made by creating folders in `VTube Studio\BepInEx\plugins\Trashy\Item
107106
Example: Create a folder `pokemon` in `VTube Studio\BepInEx\plugins\Trashy\Items` and drop some png files into the `pokemon` folder. Now you have the group `pokemon` available in the trigger settings and it will only throw items from the `pokemon` folder.
108107

109108
Make sure to click on `Reload Items` in the settings after adding or changing png files.
109+
110+
### Additional hit sounds
111+
112+
Trashy comes with a default hit sound, but you can replace it and/or add other sounds by just adding files to the `VTube Studio\BepInEx\plugins\Trashy\Sounds` folder, provided they are in one of these supported formats:
113+
114+
- `.mp3` MPEG Layer 3
115+
- `.ogg` Ogg Vorbis (not Opus!)
116+
- `.wav` Microsoft Wave
117+
- `.aiff` Audio Interchange File Format
118+
119+
Which sound is played when hit is chosen at random.
120+
121+
If you add/remove sounds while VTube Studio is open, remember to click `Reload hit sounds` to apply the changes.

src/Trashy/Extensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ public static T Random<T>(this IReadOnlyList<T> list)
113113
if (list.Count == 0)
114114
return default;
115115

116+
if (list.Count == 1)
117+
return list[0];
118+
116119
return list[UnityEngine.Random.Range(0, list.Count)];
117120
}
118121
}

src/Trashy/SoundManager.cs

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ public class SoundManager : MonoBehaviour
1313
{
1414
private const int MaxSounds = 6;
1515

16+
private static readonly List<AudioClip> s_audioClips = new List<AudioClip>();
1617
private static readonly Queue<AudioSource> s_queue = new Queue<AudioSource>();
1718
private static readonly List<AudioSource> s_active = new List<AudioSource>();
18-
private static AudioClip s_audioClip;
1919
private static float s_lastPlay;
2020

2121
public static void Play()
@@ -24,18 +24,44 @@ public static void Play()
2424
if (Time.realtimeSinceStartup - s_lastPlay < 0.05f)
2525
return;
2626

27-
if (s_queue.Count > 0 && s_audioClip != null)
27+
if (s_queue.Count > 0 && s_audioClips.Count > 0)
2828
{
29+
// Pick audio clip at random
30+
var audioClip = s_audioClips.Random();
2931
var audioSource = s_queue.Dequeue();
30-
audioSource.clip = s_audioClip;
32+
audioSource.clip = audioClip;
3133
audioSource.volume = ConfigManager.HitSoundVolume.Value / 100f;
3234
audioSource.Play();
3335
s_active.Add(audioSource);
3436
s_lastPlay = Time.realtimeSinceStartup;
3537
}
3638
}
3739

38-
public static async Task LoadAudioClip()
40+
private static async Task<AudioClip> LoadClip(string fileName)
41+
{
42+
// Get audio type from file name
43+
var audioType = AudioTypeForFile(fileName);
44+
if (audioType == AudioType.UNKNOWN)
45+
{
46+
Log.Error<SoundManager>($"Unable to detect format for audio file: {fileName}");
47+
return null;
48+
}
49+
50+
// Load file
51+
using (var request = UnityWebRequestMultimedia.GetAudioClip(new Uri(fileName), audioType))
52+
{
53+
await request.SendWebRequest();
54+
if (request.result != UnityWebRequest.Result.Success)
55+
{
56+
Log.Error<SoundManager>($"Unable to load audio file: {fileName}");
57+
return null;
58+
}
59+
60+
return DownloadHandlerAudioClip.GetContent(request);
61+
}
62+
}
63+
64+
public static async Task LoadAudioClips()
3965
{
4066
foreach (var audioSource in s_active)
4167
{
@@ -46,32 +72,29 @@ public static async Task LoadAudioClip()
4672

4773
s_active.Clear();
4874

49-
if (s_audioClip != null)
75+
// Unload all audio clips
76+
if (s_audioClips.Count > 0)
5077
{
51-
AudioClip.Destroy(s_audioClip);
52-
s_audioClip = null;
53-
}
78+
foreach (var audioClip in s_audioClips)
79+
AudioClip.Destroy(audioClip);
5480

55-
var fileName = Path.Combine(Paths.PluginPath, "Trashy", "hit.mp3");
56-
if (!File.Exists(fileName))
57-
return;
81+
s_audioClips.Clear();
82+
}
5883

59-
using (var request = UnityWebRequestMultimedia.GetAudioClip(
60-
new Uri(fileName),
61-
AudioType.MPEG
62-
))
84+
var soundsDirectory = Path.Combine(Paths.PluginPath, "Trashy", "Sounds");
85+
foreach (var file in Directory.GetFiles(soundsDirectory, "*.*"))
6386
{
64-
await request.SendWebRequest();
65-
if (request.result != UnityWebRequest.Result.Success)
66-
Log.Error<SoundManager>("Unable to load hit.mp3");
67-
else
68-
s_audioClip = DownloadHandlerAudioClip.GetContent(request);
87+
// Load audio clip and add it to the list
88+
var clip = await LoadClip(file);
89+
if (clip)
90+
s_audioClips.Add(clip);
6991
}
7092
}
7193

7294
private async void Start()
7395
{
74-
await LoadAudioClip();
96+
Migrate1();
97+
await LoadAudioClips();
7598
for (var i = 0; i < MaxSounds; ++i)
7699
{
77100
var audioSource = gameObject.AddComponent<AudioSource>();
@@ -93,5 +116,48 @@ private void Update()
93116
}
94117
}
95118
}
119+
120+
private static AudioType AudioTypeForFile(string fileName)
121+
{
122+
var extension = Path.GetExtension(fileName);
123+
switch (extension.ToLower())
124+
{
125+
case ".wav":
126+
return AudioType.WAV;
127+
128+
case ".mp3":
129+
return AudioType.MPEG;
130+
131+
case ".ogg":
132+
return AudioType.OGGVORBIS;
133+
134+
case ".aiff":
135+
case ".aif":
136+
return AudioType.AIFF;
137+
138+
default:
139+
return AudioType.UNKNOWN;
140+
}
141+
}
142+
143+
private static void Migrate1()
144+
{
145+
// Migrate from 0.3.3
146+
// Sounds are now in Trashy/Sounds, move old hit.mp3 to that directory
147+
148+
var fileName = Path.Combine(Paths.PluginPath, "Trashy", "hit.mp3");
149+
if (!File.Exists(fileName))
150+
return;
151+
152+
var soundsDirectory = Path.Combine(Paths.PluginPath, "Trashy", "Sounds");
153+
if (!Directory.Exists(soundsDirectory))
154+
Directory.CreateDirectory(soundsDirectory);
155+
156+
var newFileName = Path.Combine(soundsDirectory, "hit.mp3");
157+
if (File.Exists(newFileName))
158+
File.Delete(newFileName);
159+
160+
File.Move(fileName, Path.Combine(soundsDirectory, "hit.mp3"));
161+
}
96162
}
97163
}

src/Trashy/UI/Windows/GeneralConfigWindow.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ private async void DrawWindow(int windowId)
100100
if (GUILayout.Button("Reload Items"))
101101
_spriteManager.Load();
102102

103-
if (GUILayout.Button("Reload hit sound"))
104-
await SoundManager.LoadAudioClip();
103+
if (GUILayout.Button("Reload hit sounds"))
104+
await SoundManager.LoadAudioClips();
105105

106106
if (TwitchAuth.IsValidating)
107107
{

0 commit comments

Comments
 (0)