Skip to content
Snippets Groups Projects
Commit cb1d184f authored by cleemy desu wayo's avatar cleemy desu wayo
Browse files

Add file: add new sample of VRChat & SuperCollider working together

This sample is based on samples/2024/vrchat_multiple_ds2.rb.

In the case of samples/2024/vrchat_multiple_ds2.rb, wav files
were played using the OS command `play`. Unlike the previous
sample, this new sample offloads all sound stuff, including
playback of wav files and BPM management, to SuperCollider.

This sample consists of three files:

- samples/2025/osc_vrchat_supercollider.rb

  The OSC sender side. This Ruby script using Outvoke strictly
  focuses on reading pickup info from a VRChat log file and
  sending OSC messages.

- samples/2025/osc_vrchat_supercollider.scd

  The OSC receiver side. This requires SuperCollider.

- samples/2025/osc_vrchat_supercollider.pd

  A little something extra. It is a Pure Data (Purr Data) patch
  file for a dummy GUI that only sends OSC and does not need
  VRChat or Outvoke.
parent 3df1817f
Branches main
No related tags found
No related merge requests found
#N canvas 3119 99 698 728 12;
#X obj 20 20 import mrpeach;
#X obj 20 690 udpsend;
#X msg 20 60 connect localhost 9003;
#X obj 40 650 packOSC;
#X msg 120 120 sendtyped /music/tempo s up;
#X msg 120 160 sendtyped /music/tempo s down;
#X msg 400 280 sendtyped /music/cutin s guitar08;
#X msg 400 320 sendtyped /music/cutin s guitar10;
#X msg 400 360 sendtyped /music/cutin s guitar13;
#X msg 400 170 sendtyped /music/togglepart/kick i 1;
#X msg 400 210 sendtyped /music/togglepart/snare i 1;
#X msg 400 570 sendtyped /music/togglepattern/kick i 1;
#X msg 400 460 sendtyped /music/reverblevel s up;
#X msg 400 500 sendtyped /music/reverblevel s down;
#X msg 400 60 sendtyped /music/volume s up;
#X msg 400 100 sendtyped /music/volume s down;
#X connect 2 0 1 0;
#X connect 3 0 1 0;
#X connect 4 0 3 0;
#X connect 5 0 3 0;
#X connect 6 0 3 0;
#X connect 7 0 3 0;
#X connect 8 0 3 0;
#X connect 9 0 3 0;
#X connect 10 0 3 0;
#X connect 11 0 3 0;
#X connect 12 0 3 0;
#X connect 13 0 3 0;
#X connect 14 0 3 0;
#X connect 15 0 3 0;
# for Outvoke 0.1 (version 0.0.99.20250817.4 or later)
# written by cleemy desu wayo / Licensed under CC0 1.0
# 2025-09-20
# --------
#
# this version is current as of 2025-09-20 19:50 UTC
#
# possibly the updated version is here:
# https://gitlab.com/cleemy-desu-wayo/outvoke/-/tree/main/samples/2025/osc_vrchat_supercollider.rb
#
# --------
#
# This sample code is based on samples/2024/vrchat_multiple_ds2.rb.
#
# このサンプルコードのベースになっているのは samples/2024/vrchat_multiple_ds2.rb です。
#
# https://gitlab.com/cleemy-desu-wayo/outvoke/-/tree/main/samples/2024/vrchat_multiple_ds2.rb
#
# --------
#
# This code is for the OSC sender side. The osc_vrchat_supercollider.scd file for the
# SuperCollider side (the OSC receiver side) is available from the following location.
#
# このコードは、OSC送信の側です。SuperColliderの側(OSC受信の側)の
# osc_vrchat_supercollider.scd については、以下より入手可能です
#
# https://gitlab.com/cleemy-desu-wayo/outvoke/-/tree/main/samples/2025/osc_vrchat_supercollider.scd
#
# -------
#
# OTE: If you use this code to train an AI on the playback results, or if you combine this
# code with an AI that sends OSC data, or if you control a VRChat avatar with an AI, you may
# be violating the terms of use of the resource site.
#
# 注意: 再生結果をAIに学習させたり、OSCデータを送信するようなAIとこのコードを組み合わせて
# 使ったり、VRChatのアバターをAIによって操作していたりする場合、素材サイトの規約に違反する
# ことになるかもしれません。
#
# Rule of MaouDamashii (魔王魂 音楽利用のルール):
# https://maou.audio/rule/
#
# --------
#
# * Setup:
#
# 1. Save wav files.
# * "maoudamashii-inst-drum1-kick.wav" from https://maou.audio/se_inst_drum1_kick/
# * "maoudamashii-inst-drum1-snare.wav" from https://maou.audio/se_inst_drum1_snare/
# * "maoudamashii-inst-guitar08.wav" from https://maou.audio/se_inst_guitar08/
# * "maoudamashii-inst-guitar10.wav" from https://maou.audio/se_inst_guitar10/
# * "maoudamashii-inst-guitar13.wav" from https://maou.audio/se_inst_guitar13/
#
# 2. Edit osc_vrchat_supercollider.scd. Change the line that initializes baseDir (the
# directory where wav files are placed).
#
# 3. Launch SuperCollider and boot server.
#
# 4. Open the file osc_vrchat_supercollider.scd, and evaluate the main code block.
#
# 5. Evaluate some fragments toward the end of the file to see if the sound changes
# in real time.
#
# 6. If necessary, configure settings for realtime scheduling.
# https://jackaudio.org/faq/linux_rt_config.html
#
# 7. Place this sample code (osc_vrchat_supercollider.rb) and outvoke.rb in the current
# directory, and grant execute permission to outvoke.rb. Then execute as follows.
#
# $ ./outvoke.rb osc_vrchat_supercollider.rb
#
# 8. Start VRChat and go to "猫和室 - modern".
# (World ID: wrld_3a201318-33c2-4c4c-8310-163552f80d63)
#
# --------
#
# * Description-en:
#
# First, make sure samples/2024/vrchat_multiple_ds2.rb runs correctly. If SuperCollider
# is running stably on top of that, this sample should work too.
#
# In the case of samples/2024/vrchat_multiple_ds2.rb, wav files were played using the
# OS command `play`. Unlike the previous sample, this sample offloads all sound stuff,
# including playback of wav files and BPM management, to SuperCollider.
#
# In this sample, the code using Outvoke (this file) strictly focuses on reading
# pickup info from a VRChat log file and sending OSC messages.
#
# The sound will change as you pick up something in the VRChat world.
#
# * dango ....................... Uguisu_3Dango.000 turns the kick on/off
# Uguisu_3Dango.001 turns the snare on/off
# * castella .................... Uguisu_Castella.000 turns the tempo down
# Uguisu_Castella.001 turns the tempo up
# * donut ....................... plays a guitar sound (cut-in)
#
# * yunomi (japanese teacup) .... BDS_Yunomi.000 turns the volume down
# BDS_Yunomi.001 turns the volume up
#
# * CupCat Latte ................ change kick pattern
# * cat cushion ................. Neko.000 (white) turns the reverb level down
# Neko.001 (black) turns the reverb level up
#
# VRChat world "猫和室 - modern" was chosen only as an example of a world with plenty of
# objects to pick up. This sample may not work due to changes in the specifications of
# VRChat or updates to the world. Even if this sample doesn't work as expected, please
# do not contact the world creator.
# (BTW, "猫" (neko) means cats, and "和室" (washitsu) means japanese room)
#
# * Description-ja:
#
# まずは samples/2024/vrchat_multiple_ds2.rb を動作させるのが可能であることが前提です。
# その上で、SuperColliderが安定して動いているなら、このサンプルも動くかもしれません。
#
# samples/2024/vrchat_multiple_ds2.rb の場合は、OSコマンドとしての play によって
# wavファイルを再生していました。この時とは違い、今回のサンプルではwavファイル再生や
# BPM管理など音に関するすべてをSuperColliderに任せています。
#
# 今回のサンプルでは、Outvokeを利用したコード(このファイル)は、pickupの情報をVRChat
# のログファイルから読み取ることと、OSCを送信することだけに徹しています。
#
# VRChatのワールド内で何かをpickupすると、音の鳴り方が変化します。
#
# * だんご ................ Uguisu_3Dango.000 がキックのON/OFF
# Uguisu_3Dango.001 がスネアのON/OFF
# * カステラ .............. Uguisu_Castella.000 がテンポダウン
# Uguisu_Castella.001 がテンポアップ
# * ドーナッツ ............ ギターの音を鳴らす(カットイン)
#
# * 湯呑み ................ BDS_Yunomi.000 が音量ダウン
# BDS_Yunomi.001 が音量アップ
#
# * CupCat Latte .......... キックのパターンを変更
# * 猫のクッション ........ Neko.000(白)がリバーブレベルのダウン
# Neko.001(黒)がリバーブレベルのアップ
#
# VRChatのワールド「猫和室 - modern」はあくまでもpickupするものが豊富にあるワールドの
# 例として選んだだけです。VRChatの仕様変更やワールドのアップデートにより、このサンプルは
# 動かなくなる可能性があります。もしこのサンプルが期待通りに動かなかったとしても、
# ワールド制作者さんに問い合わせをしないようお願いします。
#
using OutvokeDSL
$outvoke.wait = 0.001
$outvoke["vrchat-001"].preset = ["osc", "localhost", 9003] # change this line appropriately
# output a object name
hook 'vrchat-001', /pickup object: .([^']*)/i do |e|
e.m[1].to "lo"
nil
end
# kick on/off (dango)
hook 'vrchat-001', /pickup object: .Uguisu_3Dango\.000/i do
"**MUSIC CONTROL** toggle kick".to "lo"
["/music/togglepart/kick", 1]
end
# snare on/off (dango)
hook 'vrchat-001', /pickup object: .Uguisu_3Dango\.001/i do
"**MUSIC CONTROL** toggle snare".to "lo"
["/music/togglepart/snare", 1]
end
# tempo down (castella)
hook 'vrchat-001', /pickup object: .Uguisu_Castella\.000/i do
"**MUSIC CONTROL** tempo up".to "lo"
["/music/tempo", "up"]
end
# tempo up (castella)
hook 'vrchat-001', /pickup object: .Uguisu_Castella\.001/i do
"**MUSIC CONTROL** tempo down".to "lo"
["/music/tempo", "down"]
end
# cutin (donut)
hook 'vrchat-001', /pickup object: .(Donuts_[^']*)/i do |e|
cutin_phrase = "guitar10"
case e.m[1]
when /(ChocoFashion|ChocoFrench|ChocoMint|ChocoMochi|MusicalFashion)/
cutin_phrase = "guitar08"
when /(FrenchCruller|IchigoFrench)/
cutin_phrase = "guitar13"
end
"**MUSIC CONTROL** cutin : #{cutin_phrase}".to "lo"
["/music/cutin", cutin_phrase]
end
# volume down (yunomi)
hook 'vrchat-001', /pickup object: .bds_yunomi\.000/i do
"**MUSIC CONTROL** volume down".to "lo"
["/music/volume", "down"]
end
# volume up (yunomi)
hook 'vrchat-001', /pickup object: .bds_yunomi\.001/i do
"**MUSIC CONTROL** volume up".to "lo"
["/music/volume", "up"]
end
# reverb level down (neko)
hook 'vrchat-001', /pickup object: .Neko.000/i do
"**MUSIC CONTROL** reverb level down".to "lo"
["/music/reverblevel", "down"]
end
# reverb level up (neko)
hook 'vrchat-001', /pickup object: .Neko.001/i do
"**MUSIC CONTROL** reverb level up".to "lo"
["/music/reverblevel", "up"]
end
# toggle (CupCat Latte)
hook 'vrchat-001', /pickup object: .CupCat_3_RigFinish/i do
"**MUSIC CONTROL** toggle pattern: kick".to "lo"
["/music/togglepattern/kick", 1]
end
hooklo
/*
* An Outvoke sample of VRChat & SuperCollider working together
* written by cleemy desu wayo / Licensed under CC0 1.0
* 2025-09-20
* ----
* this version is current as of 2025-09-20 19:50 UTC
*
* possibly the updated version is here:
* https://gitlab.com/cleemy-desu-wayo/outvoke/-/tree/main/samples/2025/osc_vrchat_supercollider.scd
*
* ----
* The author, cleemy desu wayo, has tested this code only on SuperCollider 3.13.0 running on Linux.
*
* 作者であるcleemy desu wayoは、Linux上で動く SuperCollider 3.13.0 でしか動作確認していません。
*
* ----
* This code is for the SuperCollider side (the OSC receiver side). The osc_vrchat_supercollider.rb
* file for the OSC sender side is available from the following location.
*
* このコードは、SuperColliderの側(OSC受信の側)です。OSCを送信する側の osc_vrchat_supercollider.rb
* については、以下より入手可能です。
*
* https://gitlab.com/cleemy-desu-wayo/outvoke/-/tree/main/samples/2025/osc_vrchat_supercollider.rb
*
* ----
* NOTE: If you use this code to train an AI on the playback results, or if you combine this
* code with an AI that sends OSC data, or if you control a VRChat avatar with an AI, you may
* be violating the terms of use of the resource site.
*
* 注意: 再生結果をAIに学習させたり、OSCデータを送信するようなAIとこのコードを組み合わせて
* 使ったり、VRChatのアバターをAIによって操作していたりする場合、素材サイトの規約に違反する
* ことになるかもしれません。
*
* Rule of MaouDamashii (魔王魂 音楽利用のルール):
* https://maou.audio/rule/
*
* ----
* the following preparations are required (以下の準備が必要):
* 1. save maoudamashii-inst-drum1-kick.wav from https://maou.audio/se_inst_drum1_kick/
* 2. save maoudamashii-inst-drum1-snare.wav from https://maou.audio/se_inst_drum1_snare/
* 3. save maoudamashii-inst-guitar08.wav from https://maou.audio/se_inst_guitar08/
* 4. save maoudamashii-inst-guitar10.wav from https://maou.audio/se_inst_guitar10/
* 5. save maoudamashii-inst-guitar13.wav from https://maou.audio/se_inst_guitar13/
* 6. change the line that initializes baseDir (the directory where wav files are placed)
*
* If the message "Couldn't set realtime scheduling priority 1: Operation not permitted"
* appearsin the Post window, you may need to configure realtime scheduling. Check the
* official JACK FAQ for details.
*
* Post window に「Couldn't set realtime scheduling priority 1: Operation not permitted」と
* 出る場合は、realtime scheduling のための設定が必要かもしれません。JACKの公式FAQを参考に
* してください。
*
* https://jackaudio.org/faq/linux_rt_config.html
*
* ----
* This code was written as sample code for a project called Outvoke. However, it works fine
* on its own too, independent of Outvoke. It is mainly for tweaking sounds with OSC input,
* but toward the bottom of this code, there are optional SuperCollider code fragments for
* real-time behavior changes.
*
* このコードは、Outvokeというプロジェクトのサンプルコードとして書かれたものです。ただし、
* Outvokeとは無関係に、このコード単体でも動作はします。基本的にはOSCの受信によって音の鳴らし方
* を変えるという主旨のものではありますが、このコードの下のほうにはリアルタイムで振る舞いを
* 変えるためのSuperColliderのコードの断片も置いてあります。
*
* Outvoke project is:
* https://gitlab.com/cleemy-desu-wayo/outvoke
*
* ----
* There is also a Pure Data (Purr Data) patch file for a dummy GUI that only sends OSC and
* does not need VRChat or Outvoke.
*
* VRChatもOutvokeも必要ない、OSC送信だけをするダミーのGUIのための Pure Data(Purr Data)の
* パッチファイルもあります。
*
* https://gitlab.com/cleemy-desu-wayo/outvoke/-/tree/main/samples/2025/osc_vrchat_supercollider.pd
*
*/
(
var port = 9003; // change this line appropriately
var baseDir = "/home/scz/wav/"; // change this line appropriately
var togglePart;
var pbindefKick;
var pbindefSnare;
~mainBus = Bus.audio(s, 2);
~mainVolume = 70;
~bpm = 80;
~enableKick = true;
~enableSnare = true;
~reverbLevel = 0;
~kickPattern = 0;
~wavDrumKick = Buffer.read(s, baseDir ++ "maoudamashii-inst-drum1-kick.wav");
~wavDrumSnare = Buffer.read(s, baseDir ++ "maoudamashii-inst-drum1-snare.wav");
~wavGuitar08 = Buffer.read(s, baseDir ++ "maoudamashii-inst-guitar08.wav");
~wavGuitar10 = Buffer.read(s, baseDir ++ "maoudamashii-inst-guitar10.wav");
~wavGuitar13 = Buffer.read(s, baseDir ++ "maoudamashii-inst-guitar13.wav");
SynthDef(\mainOut, { | amp = 1, reverb_mix = 0, reverb_room = 0, reverb_damp = 0 |
var sig = In.ar(~mainBus, 2);
var sig_reverb = FreeVerb.ar(sig, reverb_mix, reverb_room, reverb_damp);
Out.ar(0, sig_reverb * Lag.kr(amp, 1));
}).add;
SynthDef(\synthDrumKick, { | rate = 1, trig = 1, startPos = 0, amp = 1 |
var sig = PlayBuf.ar(2, ~wavDrumKick, BufRateScale.kr(~wavDrumKick) * rate, trig, startPos, 0, 2);
Out.ar(~mainBus, sig * amp);
}).add;
SynthDef(\synthDrumSnare, { | rate = 1, trig = 1, startPos = 0, amp = 1 |
var sig = PlayBuf.ar(2, ~wavDrumSnare, BufRateScale.kr(~wavDrumSnare) * rate, trig, startPos, 0, 2);
Out.ar(~mainBus, sig * amp);
}).add;
SynthDef(\synthGuitar08, { | rate = 1, trig = 1, startPos = 0, amp = 1 |
var sig = PlayBuf.ar(1, ~wavGuitar08, BufRateScale.kr(~wavGuitar08) * rate, trig, startPos, 0, 2);
Out.ar(~mainBus, sig * amp);
}).add;
SynthDef(\synthGuitar10, { | rate = 1, trig = 1, startPos = 0, amp = 1 |
var sig = PlayBuf.ar(1, ~wavGuitar10, BufRateScale.kr(~wavGuitar10) * rate, trig, startPos, 0, 2);
Out.ar(~mainBus, sig * amp);
}).add;
SynthDef(\synthGuitar13, { | rate = 1, trig = 1, startPos = 0, amp = 1 |
var sig = PlayBuf.ar(1, ~wavGuitar13, BufRateScale.kr(~wavGuitar13) * rate, trig, startPos, 0, 2);
Out.ar(~mainBus, sig * amp);
}).add;
OSCdef(\oscReceiveMainvolume, { |args|
case
{ args[1] == 'up' }
{ if (~mainVolume < 300, { ~mainVolume = ~mainVolume + 10 }, { ~mainVolume = 300 }) }
{ args[1] == 'down' }
{ if (~mainVolume >= 20, { ~mainVolume = ~mainVolume - 10 }, { ~mainVolume = 0 }) };
('volume: ' ++ ~mainVolume).postln;
m.set(\amp, ~mainVolume / 100);
}, '/music/volume', nil, port);
OSCdef(\oscReceiveTempo, { |args|
case
{ args[1] == 'up' }
{ if (~bpm < 600, { ~bpm = ~bpm + 10}, { ~bpm = 600 }) }
{ args[1] == 'down' }
{ if (~bpm >= 20, { ~bpm = ~bpm - 10}, { ~bpm = 10 }) };
('BPM: ' ++ ~bpm).postln;
t.tempo_(~bpm / 60);
}, '/music/tempo', nil, port);
OSCdef(\oscReceiveTogglePartKick, { |args|
if ( args[1] == 1, { if (~enableKick, { ~enableKick = false }, { ~enableKick = true }) });
togglePart.value(\pKick, 'kick', ~enableKick);
}, '/music/togglepart/kick', nil, port);
OSCdef(\oscReceiveTogglePartSnare, { |args|
if ( args[1] == 1, { if (~enableSnare, { ~enableSnare = false }, { ~enableSnare = true }) });
togglePart.value(\pSnare, 'snare', ~enableSnare);
}, '/music/togglepart/snare', nil, port);
OSCdef(\oscReceiveCutin, { |args|
case { args[1] == 'guitar08' } { Synth(\synthGuitar08) }
{ args[1] == 'guitar10' } { Synth(\synthGuitar10) }
{ args[1] == 'guitar13' } { Synth(\synthGuitar13) };
('cutin: ' ++ args[1]).postln;
}, '/music/cutin', nil, port);
OSCdef(\oscReceiveReverbLevel, { |args|
case
{ args[1] == 'up' }
{ if (~reverbLevel < 7, { ~reverbLevel = ~reverbLevel + 1}, { ~reverbLevel = 7 }) }
{ args[1] == 'down' }
{ if (~reverbLevel >= 2, { ~reverbLevel = ~reverbLevel - 1}, { ~reverbLevel = 0 }) };
('reverb level: ' ++ ~reverbLevel).postln;
~setReverb.value(~reverbLevel);
}, '/music/reverblevel', nil, port);
OSCdef(\oscReceiveTogglePatternKick, { |args|
if ( args[1] == 1, { if (~kickPattern == 0, { ~kickPattern = 1 }, { ~kickPattern = 0 }) });
if ( ~kickPattern == 0, {
Pbindef(\pKick, \dur, Pseq([2, 0.5, 1.5], inf));
},{
Pbindef(\pKick, \dur, Pseq([2, 0.125, 0.125, 0.125, 0.125, 1.5], inf));
});
('kick pattern: pattern' ++ ~kickPattern).postln;
}, '/music/togglepattern/kick', nil, port);
togglePart = { | part, part_str, enabled_part |
if (enabled_part, {
Pbindef(part, \amp, 1);
('toggle ' ++ part_str ++ ': ON').postln;
},{
Pbindef(part, \amp, 0);
('toggle ' ++ part_str ++ ': OFF').postln;
});
};
~setReverb = { | reverb_level |
case
{ reverb_level == 0 }
{ m.set(\reverb_mix, 0); }
{ reverb_level >= 1 }
{
m.set(\reverb_mix, reverb_level / 10);
m.set(\reverb_room, 0.2 + reverb_level / 10);
m.set(\reverb_damp, 0.3 + reverb_level / 10);
};
};
''.postln;
'---- buffer info (bufnum, numFrames, numChannels, sampleRate, path):'.postln;
('kick: ' ++ ~wavDrumKick).postln;
('snare: ' ++ ~wavDrumSnare).postln;
('guitar08: ' ++ ~wavGuitar08).postln;
('guitar10: ' ++ ~wavGuitar10).postln;
('guitar13: ' ++ ~wavGuitar13).postln;
''.postln;
'---- music start'.postln;
t = TempoClock.new(~bpm / 60);
m = Synth(\mainOut);
m.set(\amp, 1);
pbindefKick = Pbindef(
\pKick,
\instrument, \synthDrumKick,
\rate, 1,
\amp, 1,
\dur, Pseq([2, 0.5, 1.5], inf)
);
pbindefSnare = Pbindef(
\pSnare,
\instrument, \synthDrumSnare,
\startPos, Pseq([-1, 1],inf),
\rate, 1,
\amp, 1,
\dur, 1
);
pbindefKick.play(t);
pbindefSnare.play(t);
)
// ---- you can ignore the code below this line. it is for real-time testing purposes ----
// main volume
m.set(\amp, 0);
m.set(\amp, 0.2);
m.set(\amp, 0.4);
m.set(\amp, 0.6);
m.set(\amp, 0.8);
m.set(\amp, 1);
// tempo
t.tempo_(60 / 60);
t.tempo_(80 / 60);
t.tempo_(100 / 60);
t.tempo_(120 / 60);
t.tempo_(140 / 60);
t.tempo_(200 / 60);
t.tempo_(300 / 60);
t.tempo_(400 / 60);
// kick on/off
Pbindef(\pKick, \amp, 0);
Pbindef(\pKick, \amp, 1);
// snare on/off
Pbindef(\pSnare, \amp, 0);
Pbindef(\pSnare, \amp, 1);
// cutin
Synth(\synthGuitar08);
Synth(\synthGuitar10);
Synth(\synthGuitar13);
// revreb
~setReverb.value(0);
~setReverb.value(1);
~setReverb.value(2);
~setReverb.value(3);
~setReverb.value(4);
~setReverb.value(5);
~setReverb.value(6);
~setReverb.value(7);
// toggle kick pattern
Pbindef(\pKick, \dur, Pseq([2, 0.5, 1.5], inf));
Pbindef(\pKick, \dur, Pseq([2, 0.125, 0.125, 0.125, 0.125, 1.5], inf));
// pitch shift (just the test, there is no code written to pitch shift via OSC receiving)
Pbindef(\pKick, \rate, 1);
Pbindef(\pKick, \rate, 1.5);
Pbindef(\pKick, \rate, 2);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment