Initial Commit
commit
159a721ff0
|
@ -0,0 +1 @@
|
|||
SFX/
|
|
@ -0,0 +1,36 @@
|
|||
# VolkorSoundBoard (VSB)
|
||||
|
||||
VSB is a cool python-based, OSC-enabled vrchat soundboard.
|
||||
It uses a avatars radial menu to trigger .wav files to play via a audio device, mostly voicemeeter.
|
||||
|
||||
## Software setup
|
||||
|
||||
0. Ensure python 3.10 (or older!!) is installed, and you have the requirements installed. `pip install -r requirements.txt`
|
||||
1. In the SFX folder, create a folder for the avatarid, for example `avtr_3ec08725-aa6c-42ec-b411-a1466f69fb61`, and then move the audio files in.
|
||||
2. Ensure that the files are named properly, with proper numbering at the start of the filename.
|
||||
3. Ensure that the files are in WAV format. I don't think the sound library supports playing anything else (and I haven't added it working)
|
||||
4. Edit the config.toml to match your settings.
|
||||
|
||||
## Avatar setup
|
||||
|
||||
1. Create a submenu with the corresponding `osc-parameter` settings triggered by a button.
|
||||
1. TODO: add a photo or something to show my example
|
||||
2. Enable OSC in game (or test with [AV3Emulator!](https://github.com/lyuma/Av3Emulator))
|
||||
3. Upload avatar (or play mode if AV3Emu) and run the button, and see if it is registered in the program.
|
||||
|
||||
## Python 3.11 or newer
|
||||
|
||||
We use the tomli library for toml, but python 3.11 and newer has tomllib built into python, so you'll have to change the import and references.
|
||||
|
||||
## Stuff I learned making this
|
||||
|
||||
- How OSC works! (osc4py3 is neat, especially multithreading stuff)
|
||||
- signals library for clean ctrl+c exiting
|
||||
- playing audio on windows from python (sounddevice, soundfile)
|
||||
- reading toml files from python.
|
||||
|
||||
## File naming
|
||||
|
||||
Files MUST be named correctly. `1. <name>`.
|
||||
The number in the filename corresponds to the number in the parameter menu in VRC.
|
||||
Files MUST be in .wav format. Seriously. I'm not requiring ffmpeg for a small size reduction.
|
|
@ -0,0 +1,13 @@
|
|||
[networking]
|
||||
# Change me to talk to a different pc (although idk how you're piping audio to another pc but nice)
|
||||
ip = "127.0.0.1"
|
||||
port = 9001
|
||||
|
||||
[audio]
|
||||
# Sets the default playback device, use `python -m sounddevice` to list all devices, and select the one you want by setting below. Moron.
|
||||
# You'd want to find the VoiceMeeter (MME) ones.
|
||||
# You can also use a name for this, it'll match text.
|
||||
audio-device = "VoiceMeeter Aux Input (VB-Audio"
|
||||
|
||||
[vrchat]
|
||||
osc-parameter = "OSC-VoiceLine"
|
|
@ -0,0 +1,72 @@
|
|||
import time
|
||||
from osc4py3.as_comthreads import *
|
||||
from osc4py3 import oscmethod as osm
|
||||
import sounddevice as sd
|
||||
import soundfile as sf
|
||||
import glob
|
||||
from pathlib import Path
|
||||
import tomli
|
||||
import signal
|
||||
|
||||
# README
|
||||
|
||||
# TODO
|
||||
## 1. set up persistence and have different folders for different avid's
|
||||
## will need to listen for '/avatar/change' messages, adding that into the play_voiceline thing.
|
||||
|
||||
# Setup config stuffs
|
||||
with open("config.toml", mode="rb") as fp:
|
||||
config = tomli.load(fp)
|
||||
osc_parameter = "/avatar/parameters/" + config["vrchat"]["osc-parameter"]
|
||||
|
||||
sd.default.device = config["audio"]["audio-device"]
|
||||
|
||||
def get_voiceline_path(VoiceLine):
|
||||
# Returns the FIRST result from finding "<number>."
|
||||
print(f"[VSB] Searching for file: {VoiceLine}")
|
||||
# Calculates the file to search for.
|
||||
search_path = Path('./SFX')/ f'{VoiceLine}.*.wav'
|
||||
print(f"[VSB] Searching for path: {search_path}")
|
||||
# Do the search and save the output
|
||||
file = glob.glob(str(search_path))
|
||||
return file[0]
|
||||
|
||||
def play_voiceline(VoiceLine, VoiceLinePath):
|
||||
# Load selected file into ram. (I imagine this will be GC'd at some point)
|
||||
print(f"[VSB] Loading Voiceline: ({VoiceLine}) {VoiceLinePath}")
|
||||
data, samplerate = sf.read(VoiceLinePath)
|
||||
sd.play(data, samplerate)
|
||||
print(f"[VSB] Playing Voiceline: ({VoiceLine}) {VoiceLinePath}")
|
||||
sd.wait() # This forces the program to wait until playback is finished!!!
|
||||
print(f"[VSB] Finished Voiceline: ({VoiceLine}) {VoiceLinePath}")
|
||||
|
||||
# Only called when voiceline detected.
|
||||
def main(VoiceLine):
|
||||
print(f"[VSB] Received message: {VoiceLine}")
|
||||
VoiceLinePath = get_voiceline_path(VoiceLine)
|
||||
play_voiceline(VoiceLine, VoiceLinePath)
|
||||
|
||||
# Startup the server (this is a function because im shit at coding)
|
||||
def init_osc():
|
||||
osc_startup()
|
||||
osc_udp_server(config["networking"]["ip"], config["networking"]["port"], "VolkorSoundBoard")
|
||||
osc_method(osc_parameter, main)
|
||||
|
||||
def exit_handler(signum, frame):
|
||||
print(f"[VSB] Exiting, thanks for flying VSB Airlines!")
|
||||
osc_terminate()
|
||||
exit(0)
|
||||
|
||||
# Start the system.
|
||||
print(f"[VSB] Initializing...")
|
||||
signal.signal(signal.SIGINT, exit_handler)
|
||||
init_osc()
|
||||
|
||||
# Periodically call osc4py3 processing method in your event loop.
|
||||
finished = False
|
||||
slep = 0
|
||||
killtime = 100
|
||||
while not finished:
|
||||
osc_process()
|
||||
time.sleep(0.1) # Stops it from using 10% cpu on my 5800x (delays processing by ~50ms)
|
||||
slep = slep + 1
|
|
@ -0,0 +1,3 @@
|
|||
sounddevice
|
||||
soundfile
|
||||
osc4py3
|
Loading…
Reference in New Issue