From 1f038540a59ff96aed3cda901449a298b6f1c11c Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Wed, 19 Jul 2023 23:16:59 +1000 Subject: [PATCH] Port in the voice settings menu --- .../interface/opensb/voicechat/activity.png | Bin 0 -> 201 bytes .../opensb/voicechat/activityback.png | Bin 0 -> 326 bytes .../interface/opensb/voicechat/bigbutton.png | Bin 0 -> 223 bytes .../opensb/voicechat/bigbuttonback.png | Bin 0 -> 496 bytes .../interface/opensb/voicechat/body.png | Bin 0 -> 1049 bytes .../interface/opensb/voicechat/device.png | Bin 0 -> 195 bytes .../interface/opensb/voicechat/deviceback.png | Bin 0 -> 447 bytes .../interface/opensb/voicechat/footer.png | Bin 0 -> 250 bytes .../interface/opensb/voicechat/header.png | Bin 0 -> 569 bytes .../{ => opensb}/voicechat/indicator/back.png | Bin .../voicechat/indicator/front.png | Bin .../voicechat/indicator/front_muted.png | Bin .../interface/opensb/voicechat/pushtotalk.png | Bin 0 -> 207 bytes .../opensb/voicechat/pushtotalkback.png | Bin 0 -> 378 bytes .../opensb/voicechat/voicechat.config | 174 ++++++++++++++ .../interface/opensb/voicechat/voicechat.lua | 227 ++++++++++++++++++ .../interface/optionsmenu/body_blank.png | Bin 0 -> 1017 bytes .../optionsmenu/duocontrolsbutton.png | Bin 0 -> 159 bytes .../optionsmenu/duocontrolsbuttonhover.png | Bin 0 -> 154 bytes .../optionsmenu/optionsmenu.config.patch | 40 ++- ...ntrolsbutton.png => tricontrolsbutton.png} | Bin ...onhover.png => tricontrolsbuttonhover.png} | Bin .../opensb/universeclient/voicemanager.lua | 2 +- source/client/StarClientApplication.cpp | 9 +- source/frontend/CMakeLists.txt | 2 + source/frontend/StarMainMixer.cpp | 3 + source/frontend/StarOptionsMenu.cpp | 14 +- source/frontend/StarOptionsMenu.hpp | 3 + source/frontend/StarVoice.cpp | 50 ++-- source/frontend/StarVoice.hpp | 2 +- source/frontend/StarVoiceLuaBindings.cpp | 12 +- source/frontend/StarVoiceLuaBindings.hpp | 2 +- source/frontend/StarVoiceSettingsMenu.cpp | 23 ++ source/frontend/StarVoiceSettingsMenu.hpp | 24 ++ source/windowing/StarPane.cpp | 4 + 35 files changed, 552 insertions(+), 39 deletions(-) create mode 100644 assets/opensb/interface/opensb/voicechat/activity.png create mode 100644 assets/opensb/interface/opensb/voicechat/activityback.png create mode 100644 assets/opensb/interface/opensb/voicechat/bigbutton.png create mode 100644 assets/opensb/interface/opensb/voicechat/bigbuttonback.png create mode 100644 assets/opensb/interface/opensb/voicechat/body.png create mode 100644 assets/opensb/interface/opensb/voicechat/device.png create mode 100644 assets/opensb/interface/opensb/voicechat/deviceback.png create mode 100644 assets/opensb/interface/opensb/voicechat/footer.png create mode 100644 assets/opensb/interface/opensb/voicechat/header.png rename assets/opensb/interface/{ => opensb}/voicechat/indicator/back.png (100%) rename assets/opensb/interface/{ => opensb}/voicechat/indicator/front.png (100%) rename assets/opensb/interface/{ => opensb}/voicechat/indicator/front_muted.png (100%) create mode 100644 assets/opensb/interface/opensb/voicechat/pushtotalk.png create mode 100644 assets/opensb/interface/opensb/voicechat/pushtotalkback.png create mode 100644 assets/opensb/interface/opensb/voicechat/voicechat.config create mode 100644 assets/opensb/interface/opensb/voicechat/voicechat.lua create mode 100644 assets/opensb/interface/optionsmenu/body_blank.png create mode 100644 assets/opensb/interface/optionsmenu/duocontrolsbutton.png create mode 100644 assets/opensb/interface/optionsmenu/duocontrolsbuttonhover.png rename assets/opensb/interface/optionsmenu/{controlsbutton.png => tricontrolsbutton.png} (100%) rename assets/opensb/interface/optionsmenu/{controlsbuttonhover.png => tricontrolsbuttonhover.png} (100%) create mode 100644 source/frontend/StarVoiceSettingsMenu.cpp create mode 100644 source/frontend/StarVoiceSettingsMenu.hpp diff --git a/assets/opensb/interface/opensb/voicechat/activity.png b/assets/opensb/interface/opensb/voicechat/activity.png new file mode 100644 index 0000000000000000000000000000000000000000..20286e7d4f44bdd52c1a1f25c8ea46a57c985fd6 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^0YEIp!3HE1F5O-Zq!^2X+?^QKos)S9a~60+7BevL9RguSQ4OyKpkSP*i(^QJ^V^#{1rHc-FgQN^SN+XA>%hJe z^QJFtJ(l_1_wO|rZw}vAk@xbP0l+XkK D5vDZ& literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/opensb/voicechat/activityback.png b/assets/opensb/interface/opensb/voicechat/activityback.png new file mode 100644 index 0000000000000000000000000000000000000000..8308c3decccb666508fdf277e0ea116458e5d357 GIT binary patch literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^0YEIp!3HE1F5O-Zq!^2X+?^QKos)S9a~60+7BevL9RguSQ4OyKpx|v!7srqc=eIK*`C1eNSR8AY%YQy*Eo$a_ zDJ>;(vTywENhckQ-GXJ#9xvRI8Yz?&dp+*lFxs(FpoYSxu*DB|I8m}qWfx> zT${D-^)Bw;hO?ip-C8p5d9B#>IWyNFVdQ&MBb@0Muc5Jpcdz literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/opensb/voicechat/bigbutton.png b/assets/opensb/interface/opensb/voicechat/bigbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..2908feb97dee1d4b967cc7b67d0113ed3ccc50e5 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^-+)+(gAGWwOj&3Mq!^2X+?^QKos)S9a~60+7BevL9RguSQ4OyKpkRroi(^Pd+}pb+c@G$HFgQ;BSA8yVXXl!< zxPSv)>c#uTCTu!9Z{}vE`tNsNy*}g6u|d(}hJZ>AhmaWyr*u;buj8RMg+%8G#0tx? dIh>2)pLUF2?ckJ6w}JLEc)I$ztaD0e0s#COMhO4_ literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/opensb/voicechat/bigbuttonback.png b/assets/opensb/interface/opensb/voicechat/bigbuttonback.png new file mode 100644 index 0000000000000000000000000000000000000000..fe107707200e5c34a4d5b4e8a73a5f1f7ea9fd86 GIT binary patch literal 496 zcmeAS@N?(olHy`uVBq!ia0vp^-+)+(gAGWwOj&3Mq!^2X+?^QKos)S9a~60+7BevL9ROj*tZSW|Kn<;)E{-7;ac}P)%sb@3)AF!|{cQKsW6hIJ zc`Wwpot|@P_SgDJQ{Svfe0i|I`+DvD{?gd#fvmCe{l|~*o4)#6@l(6_-+Rw}^(i%- z{ZqO`es7fb*5^yl*cS(=-CJyT-sSf^?bX-joww?HytruH*VkWvO=dm6^<3nM*LQ@& z*Be;rKe4?VURri;YEH?z`{AzxcV@+lK40a2J#a7k@1HgMr1y6|dy}`U^s4l_J?+2! z#nR4kq}N`3y)X6ZyxEoi4Q{Ld20~u zb@fgRTMi~L^BUf8aON?)AkfTfa)b)pKZhGaUUhfHtzCNSbI`N-mKwtE1LvnNsy#n7 zH2Zw|(a*<=pT@kt$oywD`=@HLPgdWLT-EM5$6e|FJ#_BeVpZw6ufJcd3R!n6{OHbC f7q_lU{>gawXa0ddZtwYkvBco%>gTe~DWM4fTwvq~ literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/opensb/voicechat/body.png b/assets/opensb/interface/opensb/voicechat/body.png new file mode 100644 index 0000000000000000000000000000000000000000..37f73a8f555dbe37ec57f453f7ce9209925cf058 GIT binary patch literal 1049 zcmeAS@N?(olHy`uVBq!ia0vp^zkv7z2OE$)824v3kYX$ja(7}_cTVOdki(Mh=X?n_UsGSt#|L= z*K^#pf2=^9g?9aX%~GE}zc{dS^M&x$SGPYmUKTfda{SvpbBfFU9hr0HZtdwgn?Fyp zoVok=#o~<-f2IOi*6HVNR-OmS{XJos{`~2jKRN09@7I6o<7+NXli@ixv(UgI{g~qf zqWoMVC(HKqj75h9_umqEiJtNji8;oPcVt|?KfP|*{^K(XW41j1eD06W{qr*m1ym>u zR(6Ymi8cowe&_wk!2aM)s{ zKD=O#B)eex!uju*n|~^Pd0SS!i#KN1%IlWlj~M@jJd(@#G={PHRE_~VaNn{UeOW6nugv#(t0 ST@5fhGkCiCxvXa~60+7BevL9RguSQ4OyKpkRcji(^QJ^V{1yc^M2j7#xfLey^Dy(-Ja~60+7BevL9RguSQ4OyKpoS1n7srqc=eM&R^I8IUoZ0zK9lcQ)kYPJj z+h0vyX<4`1r^z~k$AA9uJ<^pKdp&v9vf~~N=Re!*{}SSLt)Iz;f5rLdYy4B=M2yqF zw{4P1?45T_#CEUtiFa~)EsReEHrRe$C3F1a>e~DFZLYmo$$q83^h-xt#OebtVlUiW zvualJCZ4?&)7PATuB{mBrAA4UYZE-m~JE$${=H zA7+vme`ep_j@=Ir^rf|ar*@bA`OCFJ{`CvNvm3%1FYaEjIWp9gJyo*mqh93v^Wj08 h>`bce@9sM;KKp+18ZWL*n}LDO;OXk;vd$@?2>=*U$EW}R literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/opensb/voicechat/footer.png b/assets/opensb/interface/opensb/voicechat/footer.png new file mode 100644 index 0000000000000000000000000000000000000000..39069c9325f930e6a50d1cb13781169b8b00096c GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^zkryZgAGWY-F#^}kYX$ja(7}_cTVOdki(Mh=_9MIcC=XQ@X0-#$z#? tm~a+3w=G`P)X1^@s67(6o(00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T0=7Noon4>IFIr5>yI>&Y#w~76K5(4bRIpAK6=;6a~KA(U5LjXH- zyWPJ1!Z{+v#y>0Sr^6?7MJ0uJ=U1zy5F1)Y!seUh+$`sV;Tc3?LL z`sQ|Zc649>1RWRvU)`0*%ib%@e>!r;fWthpxyeFt3A>zg0$k`C_N zYn9_hdT(kFbi8aOD5YW72)m7-8+5a5p>bS8>zf~5g?oK+^FwBU{+g~~?laKui=4BC z&unW5x)L^5s`>SNO>?zq%%!ziuX7Dc>M56$wwzbsO)9Tofu%iQ= zV@C%DLC}E#5OiPw1RWRvK?epf{px7G#>MFZ$7i<{qL&-Rrj(nR~XLi#+z?YOLw{_cK3B?^=BO zXyvm~x3$SV7jKouZa1_)|5^DtkL|CHos%uoXI$PD`?dGjui6Er=PupY&1&2CxNboL z!?76|Z&&)=|LQ1y{Z{bYxYrBLh5IY~GxwiRZoAg@>dEap&i#MIyf$-g&TX@=`wMgTe~DWM4f_bj8* literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/opensb/voicechat/voicechat.config b/assets/opensb/interface/opensb/voicechat/voicechat.config new file mode 100644 index 0000000..20e88d9 --- /dev/null +++ b/assets/opensb/interface/opensb/voicechat/voicechat.config @@ -0,0 +1,174 @@ +{ + "scripts" : ["/interface/opensb/voicechat/voicechat.lua"], + "scriptDelta" : 1, + "scriptWidgetCallbacks" : [ + "selectDevice", + "voiceToggle", + "switchVoiceMode" + ], + + "canvasClickCallbacks" : { + "inputVolume" : "inputVolume", + "voiceVolume" : "voiceVolume", + "threshold" : "threshold" + }, + + "gui" : { + "panefeature" : { + "type" : "panefeature", + "positionLocked" : false + }, + "background" : { + "type" : "background", + "fileHeader" : "/interface/opensb/voicechat/header.png", + "fileBody" : "/interface/opensb/voicechat/body.png", + "fileFooter" : "/interface/opensb/voicechat/footer.png" + }, + + "voiceVolumeLabel" : { + "type" : "label", + "value" : "THEIR VOLUME", + "position" : [26, 178], + "wrapWidth" : 48, + "lineSpacing" : 0.75, + "hAnchor" : "mid", + "vAnchor" : "mid" + }, + "voiceVolume" : { + "type" : "canvas", + "rect" : [50, 171, 247, 186], + "captureMouseEvents" : true, + "captureKeyboardEvents" : false + }, + + "inputVolumeLabel" : { + "type" : "label", + "value" : "YOUR VOLUME", + "position" : [26, 158], + "wrapWidth" : 48, + "lineSpacing" : 0.75, + "hAnchor" : "mid", + "vAnchor" : "mid" + }, + "inputVolume" : { + "type" : "canvas", + "rect" : [50, 151, 247, 166], + "captureMouseEvents" : true, + "captureKeyboardEvents" : false + }, + + "enableVoiceToggleBack" : { + "type" : "image", + "file" : "/interface/opensb/voicechat/bigbuttonback.png?multiply=0f0", + "position" : [2, 189], + "zlevel" : -1 + }, + "enableVoiceToggle" : { + "type" : "button", + "pressedOffset" : [0, 0], + "position" : [2, 189], + "base" : "/interface/opensb/voicechat/bigbutton.png?replace;fff=fff0;000=0007", + "hover" : "/interface/opensb/voicechat/bigbutton.png?replace;fff=fff7;000=3337", + "press" : "/interface/opensb/voicechat/bigbutton.png?replace;fff=000;000=7777", + "callback" : "voiceToggle", + "fontSize" : 16, + "zlevel" : 1 + }, + + + "voiceModeLabel" : { + "type" : "label", + "value" : "VOICE MODE", + "position" : [26, 133], + "wrapWidth" : 32, + "lineSpacing" : 0.75, + "hAnchor" : "mid", + "vAnchor" : "mid" + }, + "pushToTalkBack" : { + "type" : "image", + "file" : "/interface/opensb/voicechat/pushtotalkback.png?multiply=0f0", + "position" : [50, 121], + "zlevel" : -1 + }, + "pushToTalk" : { + "type" : "button", + "pressedOffset" : [0, 0], + "position" : [50, 121], + "base" : "/interface/opensb/voicechat/pushtotalk.png?replace;fff=fff0;000=0007", + "hover" : "/interface/opensb/voicechat/pushtotalk.png?replace;fff=fff7;000=3337", + "press" : "/interface/opensb/voicechat/pushtotalk.png?replace;fff=000;000=7777", + "callback" : "switchVoiceMode", + "fontSize" : 16, + "zlevel" : 1 + }, + + "voiceActivityBack" : { + "type" : "image", + "file" : "/interface/opensb/voicechat/activityback.png?multiply=0f0", + "position" : [167, 121], + "zlevel" : -1 + }, + "voiceActivity" : { + "type" : "button", + "pressedOffset" : [0, 0], + "position" : [167, 121], + "base" : "/interface/opensb/voicechat/activity.png?replace;fff=fff0;000=0007", + "hover" : "/interface/opensb/voicechat/activity.png?replace;fff=fff7;000=3337", + "press" : "/interface/opensb/voicechat/activity.png?replace;fff=000;000=7777", + "callback" : "switchVoiceMode", + "fontSize" : 16, + "zlevel" : 1 + }, + + "thresholdLevel" : { + "type" : "label", + "value" : "THRESHOLD", + "position" : [26, 109], + "wrapWidth" : 48, + "lineSpacing" : 0.75, + "hAnchor" : "mid", + "vAnchor" : "mid" + }, + "threshold" : { + "type" : "canvas", + "rect" : [50, 102, 247, 117], + "captureMouseEvents" : true, + "captureKeyboardEvents" : false + }, + + "devices" : { + "type" : "scrollArea", + "rect" : [3, 16, 248, 98], + "children" : { + "list" : { + "type" : "list", + "schema" : { + "selectedBG" : "/interface/opensb/voicechat/deviceback.png?multiply=0f0", + "unselectedBG" : "/interface/opensb/voicechat/deviceback.png?multiply=222", + "spacing" : [0, 1], + "memberSize" : [234, 16], + "listTemplate" : { + "background" : { + "type" : "image", + "file" : "/interface/opensb/voicechat/deviceback.png?multiply=222", + "position" : [0, 0], + "zlevel" : -1 + }, + "button" : { + "type" : "button", + "callback" : "selectDevice", + "caption" : "Unnamed", + "base" : "/interface/opensb/voicechat/device.png?replace;fff=fff0;000=0007", + "hover" : "/interface/opensb/voicechat/device.png?replace;fff=fff7;000=3337", + "press" : "/interface/opensb/voicechat/device.png?replace;fff=000;000=7777", + "pressedOffset" : [0, 0], + "position" : [0, 0] + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/assets/opensb/interface/opensb/voicechat/voicechat.lua b/assets/opensb/interface/opensb/voicechat/voicechat.lua new file mode 100644 index 0000000..4e9f7e7 --- /dev/null +++ b/assets/opensb/interface/opensb/voicechat/voicechat.lua @@ -0,0 +1,227 @@ +--constants +local PATH = "/interface/opensb/voicechat/" +local DEVICE_LIST_WIDGET = "devices.list" +local DEFAULT_DEVICE_NAME = "Use System Default" +local NULL_DEVICE_NAME = "No Audio Device" +local COLD_COLOR = {25, 255, 255, 225} +local HOT_COLOR = {255, 96, 96, 225} +local MINIMUM_DB = -80 +local VOICE_MAX, INPUT_MAX = 1.75, 1.0 +local MID = 7.5 + +local fmt = string.format + +local debugging = false +local function log(...) + if not debugging then return end + sb.logInfo(...) +end + +local function mapToRange(x, min, max) + return math.min(1, math.max(0, (x - min)) / max) +end + +local function linear(a, b, c) + return a + (b - a) * c +end + +local settings = {} + +local function set(k, v) + settings[k] = v + local newSettings = jobject() + newSettings[k] = v + voice.mergeSettings(newSettings) + return v +end + +local devicesToWidgets = {} +local widgetsToDevices = {} +local nullWidget +local function addDeviceToList(deviceName) + local name = widget.addListItem(DEVICE_LIST_WIDGET) + widget.setText(fmt("%s.%s.button", DEVICE_LIST_WIDGET, name), deviceName) + widgetsToDevices[name] = deviceName + devicesToWidgets[deviceName] = name + log("Added audio device '%s' to list", deviceName) + return name +end + +function selectDevice() + local selected = widget.getListSelected(DEVICE_LIST_WIDGET) + if selected == nullWidget then + set("inputEnabled", false) + widget.setListSelected(DEVICE_LIST_WIDGET, nullWidget) + end + local deviceName = widgetsToDevices[selected] + if deviceName == DEFAULT_DEVICE_NAME then deviceName = nil end + + if settings.deviceName == deviceName then + local inputEnabled = set("inputEnabled", not settings.inputEnabled) + widget.setListSelected(DEVICE_LIST_WIDGET, inputEnabled and selected or nullWidget) + else + set("deviceName", deviceName) + set("inputEnabled", true) + end +end + +local function initCallbacks() + widget.registerMemberCallback(DEVICE_LIST_WIDGET, "selectDevice", selectDevice) +end + +local function updateVoiceButton() + local enabled = settings.enabled + widget.setText("enableVoiceToggle", enabled and "^#0f0;disable voice chat" or "^#f00;enable voice chat") + widget.setImage("enableVoiceToggleBack", PATH .. "bigbuttonback.png?multiply=" .. (enabled and "0f0" or "f00")) +end + +local function updateVoiceModeButtons() + local pushToTalk = settings.inputMode:lower() == "pushtotalk" + widget.setImage("pushToTalkBack", PATH .. "pushtotalkback.png?multiply=" .. (pushToTalk and "0f0" or "f00")) + widget.setImage("voiceActivityBack", PATH .. "activityback.png?multiply=" .. (pushToTalk and "f00" or "0f0")) + widget.setText("pushToTalk", pushToTalk and "^#0f0;PUSH TO TALK" or "^#f00;PUSH TO TALK") + widget.setText("voiceActivity", pushToTalk and "^#f00;ACTIVITY" or "^#0f0;ACTIVITY") +end + +local voiceCanvas, inputCanvas = nil, nil +local function updateVolumeCanvas(canvas, volume, multiplier) + canvas:clear() + local lineEnd = 1 + volume * 195 + local lineColor = {95, 110, 255, 225} + local multiplied = volume * multiplier + if multiplied > 1 then + local level = (multiplied - 1) / (multiplier - 1) + for i = 1, 4 do + lineColor[i] = linear(lineColor[i], HOT_COLOR[i], level) + end + else + for i = 1, 4 do + lineColor[i] = linear(lineColor[i], COLD_COLOR[i], 1 - multiplied) + end + end + + canvas:drawLine({1, MID}, {lineEnd, MID}, lineColor, 60) + canvas:drawLine({lineEnd - 0.5, MID}, {lineEnd + 0.5, MID}, {255, 255, 255, 200}, 60) + local str = volume == 0 and "^#f00,shadow;MUTED" or fmt("^shadow;%s%%", math.floor(volume * multiplier * 1000) * 0.1) + canvas:drawText(str, {position = {92.5, MID}, horizontalAnchor = "mid", verticalAnchor = "mid"}, 16, {255, 255, 255, 255}) +end + +local thresholdCanvas = nil +local function updateThresholdCanvas(canvas, dB) + canvas:clear() + local scale = pane.scale() + local lineEnd = 1 + (1 - (dB / MINIMUM_DB)) * 195 + local lineColor = {255, 255, 0, 127} + canvas:drawLine({1, 2}, {lineEnd, 2}, lineColor, scale) + canvas:drawLine({1, 13}, {lineEnd, 13}, lineColor, scale) + lineColor[4] = 64 + canvas:drawLine({lineEnd, 2}, {196, 2}, lineColor, scale) + canvas:drawLine({lineEnd, 13}, {196, 13}, lineColor, scale) + canvas:drawLine({lineEnd - 0.5, MID}, {lineEnd + 0.5, MID}, {255, 255, 255, 200}, 60) + + local loudness = 1 - (voice.speaker().smoothDecibels / MINIMUM_DB) + local loudnessEnd = math.min(1 + loudness * 195, 196) + if loudnessEnd > 0 then + lineColor[4] = 200 + canvas:drawLine({1, MID}, {loudnessEnd, MID}, lineColor, 4 * scale) + end + + local str = fmt("^shadow;%sdB", math.floor(dB * 10) * 0.1) + canvas:drawText(str, {position = {92.5, MID}, horizontalAnchor = "mid", verticalAnchor = "mid"}, 16, {255, 255, 255, 255}) +end + +function init() + settings = voice.getSettings() + voiceCanvas = widget.bindCanvas("voiceVolume") + inputCanvas = widget.bindCanvas("inputVolume") + thresholdCanvas = widget.bindCanvas("threshold") +end + +function displayed() + devicesToWidgets = {} + widgetsToDevices = {} + widget.clearListItems(DEVICE_LIST_WIDGET) + initCallbacks() + + addDeviceToList(DEFAULT_DEVICE_NAME) + + for i, v in pairs(voice.devices()) do + addDeviceToList(v) + end + + nullWidget = widget.addListItem(DEVICE_LIST_WIDGET) + local nullWidgetPath = fmt("%s.%s", DEVICE_LIST_WIDGET, nullWidget) + widget.setPosition(nullWidgetPath, {0, 10000}) + widget.setVisible(nullWidgetPath, false) + + local preferredDeviceWidget = devicesToWidgets[settings.deviceName or DEFAULT_DEVICE_NAME] + if preferredDeviceWidget and settings.inputEnabled then + widget.setListSelected(DEVICE_LIST_WIDGET, preferredDeviceWidget) + end + + updateVoiceButton() + updateVoiceModeButtons() + updateVolumeCanvas(voiceCanvas, settings.outputVolume / VOICE_MAX, VOICE_MAX) + updateVolumeCanvas(inputCanvas, settings.inputVolume / INPUT_MAX, INPUT_MAX) + updateThresholdCanvas(thresholdCanvas, settings.threshold) +end + +function update() + updateThresholdCanvas(thresholdCanvas, settings.threshold) +end + +local function sliderToValue(x) + return mapToRange(x, 5, 187) +end + +local function mouseInSlider(mouse) + return mouse[1] > 0 and mouse[1] < 197 + and mouse[2] > 0 and mouse[2] < 16 +end + +local function handleVolume(canvas, mouse, multiplier, setter) + if not mouseInSlider(mouse) then return end + + local volumePreMul = sliderToValue(mouse[1]) + local volume = volumePreMul * multiplier; + if math.abs(volume - 1) < 0.01 then + volumePreMul = 1 / multiplier + volume = 1 + end + + updateVolumeCanvas(canvas, volumePreMul, multiplier) + setter(volume) +end + +function voiceVolume(mouse, button) + if button ~= 0 then return end + handleVolume(voiceCanvas, mouse, VOICE_MAX, function(v) set("outputVolume", v) end) +end + +function inputVolume(mouse, button) + if button ~= 0 then return end + handleVolume(inputCanvas, mouse, INPUT_MAX, function(v) set("inputVolume", v) end) +end + +function threshold(mouse, button) + if button ~= 0 then return end + if not mouseInSlider(mouse) then return end + local dB = (1 - sliderToValue(mouse[1])) * MINIMUM_DB + set("threshold", dB) + + updateThresholdCanvas(thresholdCanvas, dB) +end + +function switchVoiceMode(mode) + log("switching voice mode to %s", tostring(mode)) + local success, err = pcall(function() + set("inputMode", mode) + updateVoiceModeButtons() + end) + if not success then log("%s", err) end +end + +function voiceToggle() + set("enabled", not settings.enabled) + updateVoiceButton() +end \ No newline at end of file diff --git a/assets/opensb/interface/optionsmenu/body_blank.png b/assets/opensb/interface/optionsmenu/body_blank.png new file mode 100644 index 0000000000000000000000000000000000000000..7d5b0f1b36716d0682417c0ee9c487dc8286a2ed GIT binary patch literal 1017 zcmeAS@N?(olHy`uVBq!ia0vp^Z-96e2OE%FcWH4dkYX$ja(7}_cTVOdki(Mh=PLc+$DlcYiT)y$g%I}l-c;DpgcPI}Jyw7GEp*Owx z^y$;TXV0F!`IAt9&+^Odq0;uJF6ZtKzrFs`5C68Of*cAF97-7I((41AH*>y4#4TF% zv!-reolO7na((@Pt0_j7rUgt=Di|6tjX)E*blU&O8sFu?*{=^+uUX67E2P6Bhy%SW z%~*8x^#k42*BuwLa61CgEW7r*YeM|i^@q)@udDkPS@!>QfyJM=GV7P0WwzZ+I9g@% zPwcJLjiX70q4Ua&qPMwjlPN+P^PG{>3{^}=3s$$Nxv+_+!Q0zcL9+MBw;;#nEyglHd zo@H=prSj**hqZ22FlSfUw#U}`+iUwbCS5(&TpW=x0ZB8$po9NyZ=Yevs^yx$|FCxR z|Am1~tqP=~Yu6fWH*e!J@!BBZf`pFb&ShA9@x`t;`!+`CtUIL_|77#=!?|%%+KH{VRI^m6_D^H0rR z3%UOHad8V)olGfurDbSxqBi2jX1n)?xy%l@FV5JP6(zpjuG)rYPR50AdrqGXySJz* z>bNtK^ISG4ZhIb^w+-Twyw?Zj$-b$La2DooUf9`w^nweD-G~5#Imjdbpq;JlRNI93 znnNfNj2zz+?$w!oeDvtizi;2Jou6}v50R>X39E|x+mk0t-Z1`tBrMj%fgL6Bu+=j> YJ#*MM_LAl%V0LBjboFyt=akR{0B3HORR910 literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/optionsmenu/duocontrolsbutton.png b/assets/opensb/interface/optionsmenu/duocontrolsbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..1d40af3d116d84529b86e0aa81b275a3c62c8c34 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^?m*1P!3HF2pEK?TQfZzpjv*CsZ?7NZY%t()dpLbk zF3Z84OD>CC7c60PERcPD&^xed+k^kU0{2uq3s2j0N=^u#rNMc`;t|8=O`p{K9^Nqj zx9&B2p1_Jp4wpsT3&E)A;Z#}u^kc7&&o}v7acg_?P1dASf=SoCCZ+&wWbkzLb6Mw< G&;$U_mOVQF literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/optionsmenu/duocontrolsbuttonhover.png b/assets/opensb/interface/optionsmenu/duocontrolsbuttonhover.png new file mode 100644 index 0000000000000000000000000000000000000000..bdeb055393e8395a94148ad05b2d822a45eb3765 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^?m*1P!3HF2pEK?TQi+}}jv*CsZ?7A2H5l-?J)Ax% znp4bXN5H|@Duveak?)xl(mHPjqw3PR(;Rw)`6hfmIM<@rH22S4p>!>d zFHf`!Mcg5%>0z_1e)_@V$LH_)^>Ir&`)2N?Qv|2Vc!<6MTE^h%>gTe~DWM4fm`FDT literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/optionsmenu/optionsmenu.config.patch b/assets/opensb/interface/optionsmenu/optionsmenu.config.patch index fe5def8..68b27f9 100644 --- a/assets/opensb/interface/optionsmenu/optionsmenu.config.patch +++ b/assets/opensb/interface/optionsmenu/optionsmenu.config.patch @@ -1,25 +1,47 @@ { "paneLayout" : { + "voiceLabel" : { + "type" : "label", + "position" : [119, 185], + "hAnchor" : "mid", + "value" : "VOICE" + }, + "showVoiceSettings" : { + "type" : "button", + "position" : [30, 169], + "caption" : "Settings", + "base" : "/interface/optionsmenu/duocontrolsbutton.png", + "hover" : "/interface/optionsmenu/duocontrolsbuttonhover.png" + }, + "showVoicePlayers" : { + "type" : "button", + "disabled" : true, + "position" : [133, 169], + "caption" : "^#a0a000,font=iosevka-semiboldoblique;TODO^#aa7;:^reset; Players", + "base" : "/interface/optionsmenu/duocontrolsbutton.png", + "hover" : "/interface/optionsmenu/duocontrolsbuttonhover.png" + }, "showKeybindings" : { "type" : "button", - "position" : [150, 95], + "position" : [153, 95], "caption" : "Game Binds", - "base" : "/interface/optionsmenu/controlsbutton.png", - "hover" : "/interface/optionsmenu/controlsbuttonhover.png" + "base" : "/interface/optionsmenu/tricontrolsbutton.png", + "hover" : "/interface/optionsmenu/tricontrolsbuttonhover.png" }, "showModBindings" : { "type" : "button", "position" : [87, 95], "caption" : "Mod Binds", - "base" : "/interface/optionsmenu/controlsbutton.png", - "hover" : "/interface/optionsmenu/controlsbuttonhover.png" + "base" : "/interface/optionsmenu/tricontrolsbutton.png", + "hover" : "/interface/optionsmenu/tricontrolsbuttonhover.png" }, "showGraphics" : { "type" : "button", - "position" : [24, 95], + "position" : [21, 95], "caption" : "Graphics", - "base" : "/interface/optionsmenu/controlsbutton.png", - "hover" : "/interface/optionsmenu/controlsbuttonhover.png" - } + "base" : "/interface/optionsmenu/tricontrolsbutton.png", + "hover" : "/interface/optionsmenu/tricontrolsbuttonhover.png" + }, + "sfxValueLabel" : { "position" : [192, 142] } // this is 2px too low in vanilla lol } } \ No newline at end of file diff --git a/assets/opensb/interface/optionsmenu/controlsbutton.png b/assets/opensb/interface/optionsmenu/tricontrolsbutton.png similarity index 100% rename from assets/opensb/interface/optionsmenu/controlsbutton.png rename to assets/opensb/interface/optionsmenu/tricontrolsbutton.png diff --git a/assets/opensb/interface/optionsmenu/controlsbuttonhover.png b/assets/opensb/interface/optionsmenu/tricontrolsbuttonhover.png similarity index 100% rename from assets/opensb/interface/optionsmenu/controlsbuttonhover.png rename to assets/opensb/interface/optionsmenu/tricontrolsbuttonhover.png diff --git a/assets/opensb/scripts/opensb/universeclient/voicemanager.lua b/assets/opensb/scripts/opensb/universeclient/voicemanager.lua index b7468ce..2f19f6a 100644 --- a/assets/opensb/scripts/opensb/universeclient/voicemanager.lua +++ b/assets/opensb/scripts/opensb/universeclient/voicemanager.lua @@ -7,7 +7,7 @@ local module = {} modules.voice_manager = module --constants -local INDICATOR_PATH = "/interface/voicechat/indicator/" +local INDICATOR_PATH = "/interface/opensb/voicechat/indicator/" local BACK_INDICATOR_IMAGE = INDICATOR_PATH .. "back.png" local FRONT_INDICATOR_IMAGE = INDICATOR_PATH .. "front.png" local FRONT_MUTED_INDICATOR_IMAGE = INDICATOR_PATH .. "front_muted.png" diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index d8d79fd..b264f15 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -376,6 +376,13 @@ void ClientApplication::update() { else if (m_state > MainAppState::Title) updateRunning(); + // swallow leftover encoded data incase we aren't in-game yet to allow mic read to continue. + // TODO: directly disable encoding at menu so we don't have to do this + { + DataStreamBuffer ext; + m_voice->send(ext); + } + m_guiContext->cleanup(); m_edgeKeyEvents.clear(); m_input->reset(); @@ -499,7 +506,7 @@ void ClientApplication::changeState(MainAppState newState) { m_statistics = make_shared(m_root->toStoragePath("player"), appController()->statisticsService()); m_universeClient = make_shared(m_playerStorage, m_statistics); m_universeClient->setLuaCallbacks("input", LuaBindings::makeInputCallbacks()); - m_universeClient->setLuaCallbacks("voice", LuaBindings::makeVoiceCallbacks(m_voice.get())); + m_universeClient->setLuaCallbacks("voice", LuaBindings::makeVoiceCallbacks()); m_mainMixer->setUniverseClient(m_universeClient); m_titleScreen = make_shared(m_playerStorage, m_mainMixer->mixer()); diff --git a/source/frontend/CMakeLists.txt b/source/frontend/CMakeLists.txt index 4c6b9c8..b002155 100644 --- a/source/frontend/CMakeLists.txt +++ b/source/frontend/CMakeLists.txt @@ -58,6 +58,7 @@ SET (star_frontend_HEADERS StarWireInterface.hpp StarVoice.hpp StarVoiceLuaBindings.hpp + StarVoiceSettingsMenu.hpp ) SET (star_frontend_SOURCES @@ -108,6 +109,7 @@ SET (star_frontend_SOURCES StarWireInterface.cpp StarVoice.cpp StarVoiceLuaBindings.cpp + StarVoiceSettingsMenu.cpp ) ADD_LIBRARY (star_frontend OBJECT ${star_frontend_SOURCES} ${star_frontend_HEADERS}) diff --git a/source/frontend/StarMainMixer.cpp b/source/frontend/StarMainMixer.cpp index 6d68ea2..d4a2e73 100644 --- a/source/frontend/StarMainMixer.cpp +++ b/source/frontend/StarMainMixer.cpp @@ -121,6 +121,9 @@ void MainMixer::update(bool muteSfx, bool muteMusic) { if (m_mixer->hasEffect("echo")) m_mixer->removeEffect("echo", 0); + if (Voice* voice = Voice::singletonPtr()) + voice->update(); + m_mixer->update(); } } diff --git a/source/frontend/StarOptionsMenu.cpp b/source/frontend/StarOptionsMenu.cpp index cef2e7d..caa1b21 100644 --- a/source/frontend/StarOptionsMenu.cpp +++ b/source/frontend/StarOptionsMenu.cpp @@ -7,6 +7,7 @@ #include "StarLabelWidget.hpp" #include "StarAssets.hpp" #include "StarKeybindingsMenu.hpp" +#include "StarVoiceSettingsMenu.hpp" #include "StarBindingsMenu.hpp" #include "StarGraphicsMenu.hpp" @@ -49,8 +50,14 @@ OptionsMenu::OptionsMenu(PaneManager* manager) reader.registerCallback("showKeybindings", [=](Widget*) { displayControls(); }); + reader.registerCallback("showVoiceSettings", [=](Widget*) { + displayVoiceSettings(); + }); + reader.registerCallback("showVoicePlayers", [=](Widget*) { + + }); reader.registerCallback("showModBindings", [=](Widget*) { - displayModBindings(); + displayModBindings(); }); reader.registerCallback("showGraphics", [=](Widget*) { displayGraphics(); @@ -74,6 +81,7 @@ OptionsMenu::OptionsMenu(PaneManager* manager) m_sfxSlider->setRange(m_sfxRange, assets->json("/interface/optionsmenu/optionsmenu.config:sfxDelta").toInt()); m_musicSlider->setRange(m_musicRange, assets->json("/interface/optionsmenu/optionsmenu.config:musicDelta").toInt()); + m_voiceSettingsMenu = make_shared(assets->json(config.getString("voiceSettingsPanePath", "/interface/opensb/voicechat/voicechat.config"))); m_modBindingsMenu = make_shared(assets->json(config.getString("bindingsPanePath", "/interface/opensb/bindings/bindings.config"))); m_keybindingsMenu = make_shared(); m_graphicsMenu = make_shared(); @@ -169,6 +177,10 @@ void OptionsMenu::displayControls() { m_paneManager->displayPane(PaneLayer::ModalWindow, m_keybindingsMenu); } +void OptionsMenu::displayVoiceSettings() { + m_paneManager->displayPane(PaneLayer::ModalWindow, m_voiceSettingsMenu); +} + void OptionsMenu::displayModBindings() { m_paneManager->displayPane(PaneLayer::ModalWindow, m_modBindingsMenu); } diff --git a/source/frontend/StarOptionsMenu.hpp b/source/frontend/StarOptionsMenu.hpp index bd8c4ba..b984f02 100644 --- a/source/frontend/StarOptionsMenu.hpp +++ b/source/frontend/StarOptionsMenu.hpp @@ -10,6 +10,7 @@ namespace Star { STAR_CLASS(SliderBarWidget); STAR_CLASS(ButtonWidget); STAR_CLASS(LabelWidget); +STAR_CLASS(VoiceSettingsMenu); STAR_CLASS(KeybindingsMenu); STAR_CLASS(GraphicsMenu); STAR_CLASS(BindingsMenu); @@ -38,6 +39,7 @@ private: void syncGuiToConf(); void displayControls(); + void displayVoiceSettings(); void displayModBindings(); void displayGraphics(); @@ -59,6 +61,7 @@ private: JsonObject m_origConfig; JsonObject m_localChanges; + VoiceSettingsMenuPtr m_voiceSettingsMenu; BindingsMenuPtr m_modBindingsMenu; KeybindingsMenuPtr m_keybindingsMenu; GraphicsMenuPtr m_graphicsMenu; diff --git a/source/frontend/StarVoice.cpp b/source/frontend/StarVoice.cpp index b7f8a6c..843203f 100644 --- a/source/frontend/StarVoice.cpp +++ b/source/frontend/StarVoice.cpp @@ -312,15 +312,19 @@ void Voice::readAudioData(uint8_t* stream, int len) { size_t sampleCount = len / 2; if (active) { - float volume = m_inputVolume; float decibels = getAudioLoudness((int16_t*)stream, sampleCount); + if (!m_loopback) + m_clientSpeaker->decibelLevel = getAudioLoudness((int16_t*)stream, sampleCount, m_inputVolume); + if (m_inputMode == VoiceInputMode::VoiceActivity) { if (decibels > m_threshold) m_lastThresholdTime = now; active = now - m_lastThresholdTime < 50; } } + else if (!m_loopback) + m_clientSpeaker->decibelLevel = -96.0f; if (!m_loopback) { if (active && !m_clientSpeaker->playing) @@ -405,6 +409,7 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) { } else { speaker->playing = false; + speaker->decibelLevel = -96.0f; it = m_activeSpeakers.erase(it); } } @@ -423,23 +428,25 @@ void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) { } void Voice::update(PositionalAttenuationFunction positionalAttenuationFunction) { - if (positionalAttenuationFunction) { - for (auto& entry : m_speakers) { - if (SpeakerPtr& speaker = entry.second) { - speaker->channelVolumes = { - 1.0f - positionalAttenuationFunction(0, speaker->position, 1.0f), + for (auto& entry : m_speakers) { + if (SpeakerPtr& speaker = entry.second) { + if (positionalAttenuationFunction) { + speaker->channelVolumes = { + 1.0f - positionalAttenuationFunction(0, speaker->position, 1.0f), 1.0f - positionalAttenuationFunction(1, speaker->position, 1.0f) - }; + }; + } + else + speaker->channelVolumes = Vec2F::filled(1.0f); - auto& dbHistory = speaker->dbHistory; - memcpy(&dbHistory[1], &dbHistory[0], (dbHistory.size() - 1) * sizeof(float)); - dbHistory[0] = speaker->decibelLevel; - float smoothDb = 0.0f; - for (float dB : dbHistory) - smoothDb += dB; + auto& dbHistory = speaker->dbHistory; + memcpy(&dbHistory[1], &dbHistory[0], (dbHistory.size() - 1) * sizeof(float)); + dbHistory[0] = speaker->decibelLevel; + float smoothDb = 0.0f; + for (float dB : dbHistory) + smoothDb += dB; - speaker->smoothDb = smoothDb / dbHistory.size(); - } + speaker->smoothDb = smoothDb / dbHistory.size(); } } @@ -467,6 +474,7 @@ StringList Voice::availableDevices() { for (size_t i = 0; i != devices; ++i) deviceList.emplace_back(SDL_GetAudioDeviceName(i, 1)); } + deviceList.sort(); return deviceList; } @@ -488,7 +496,7 @@ int Voice::send(DataStreamBuffer& out, size_t budget) { for (auto& chunk : encodedChunks) { out.write(chunk.size()); out.writeBytes(chunk); - if ((budget -= min(budget, chunk.size())) == 0) + if (budget && (budget -= min(budget, chunk.size())) == 0) break; } @@ -499,7 +507,7 @@ int Voice::send(DataStreamBuffer& out, size_t budget) { } bool Voice::receive(SpeakerPtr speaker, std::string_view view) { - if (!speaker || view.empty()) + if (!m_enabled || !speaker || view.empty()) return false; try { @@ -635,9 +643,8 @@ void Voice::closeDevice() { return; m_applicationController->closeAudioInputDevice(); - if (!m_loopback) - m_clientSpeaker->playing = false; - + m_clientSpeaker->playing = false; + m_clientSpeaker->decibelLevel = -96.0f; m_deviceOpen = false; } @@ -684,9 +691,6 @@ void Voice::thread() { samples[i] *= m_inputVolume; } - if (!m_loopback) - m_clientSpeaker->decibelLevel = getAudioLoudness(samples.data(), samples.size()); - if (int encodedSize = opus_encode(m_encoder.get(), samples.data(), VOICE_FRAME_SIZE, (unsigned char*)encoded.ptr(), encoded.size())) { if (encodedSize == 1) continue; diff --git a/source/frontend/StarVoice.hpp b/source/frontend/StarVoice.hpp index 4500aa6..95046af 100644 --- a/source/frontend/StarVoice.hpp +++ b/source/frontend/StarVoice.hpp @@ -141,7 +141,7 @@ public: void setDeviceName(Maybe device); StringList availableDevices(); - int send(DataStreamBuffer& out, size_t budget); + int send(DataStreamBuffer& out, size_t budget = 0); bool receive(SpeakerPtr speaker, std::string_view view); // Must be called every frame with input state, expires after 1s. diff --git a/source/frontend/StarVoiceLuaBindings.cpp b/source/frontend/StarVoiceLuaBindings.cpp index 1b62aef..934b294 100644 --- a/source/frontend/StarVoiceLuaBindings.cpp +++ b/source/frontend/StarVoiceLuaBindings.cpp @@ -6,9 +6,11 @@ namespace Star { typedef Voice::SpeakerId SpeakerId; -LuaCallbacks LuaBindings::makeVoiceCallbacks(Voice* voice) { +LuaCallbacks LuaBindings::makeVoiceCallbacks() { LuaCallbacks callbacks; + auto voice = Voice::singletonPtr(); + callbacks.registerCallbackWithSignature("devices", bind(&Voice::availableDevices, voice)); callbacks.registerCallback( "getSettings", [voice]() -> Json { return voice->saveJson(); }); callbacks.registerCallback("mergeSettings", [voice](Json const& settings) { voice->loadJson(settings); }); @@ -21,7 +23,13 @@ LuaCallbacks LuaBindings::makeVoiceCallbacks(Voice* voice) { callbacks.registerCallback("speakerPosition", [voice](SpeakerId speakerId) { return voice->speaker(speakerId)->position; }); - callbacks.registerCallback("speaker", [voice](SpeakerId speakerId) { return voice->speaker(speakerId)->toJson(); }); + callbacks.registerCallback("speaker", [voice](Maybe speakerId) { + if (speakerId) + return voice->speaker(*speakerId)->toJson(); + else + return voice->localSpeaker()->toJson(); + }); + callbacks.registerCallback("speakers", [voice](Maybe onlyPlaying) -> List { List list; diff --git a/source/frontend/StarVoiceLuaBindings.hpp b/source/frontend/StarVoiceLuaBindings.hpp index 8c83e54..5d670f3 100644 --- a/source/frontend/StarVoiceLuaBindings.hpp +++ b/source/frontend/StarVoiceLuaBindings.hpp @@ -8,7 +8,7 @@ namespace Star { STAR_CLASS(Voice); namespace LuaBindings { - LuaCallbacks makeVoiceCallbacks(Voice* voice); + LuaCallbacks makeVoiceCallbacks(); } } diff --git a/source/frontend/StarVoiceSettingsMenu.cpp b/source/frontend/StarVoiceSettingsMenu.cpp new file mode 100644 index 0000000..c815dee --- /dev/null +++ b/source/frontend/StarVoiceSettingsMenu.cpp @@ -0,0 +1,23 @@ +#include "StarVoiceSettingsMenu.hpp" +#include "StarVoiceLuaBindings.hpp" + +namespace Star { + +VoiceSettingsMenu::VoiceSettingsMenu(Json const& config) : BaseScriptPane(config) { + m_script.setLuaRoot(make_shared()); + m_script.addCallbacks("voice", LuaBindings::makeVoiceCallbacks()); +} + +void VoiceSettingsMenu::show() { + BaseScriptPane::show(); +} + +void VoiceSettingsMenu::displayed() { + BaseScriptPane::displayed(); +} + +void VoiceSettingsMenu::dismissed() { + BaseScriptPane::dismissed(); +} + +} \ No newline at end of file diff --git a/source/frontend/StarVoiceSettingsMenu.hpp b/source/frontend/StarVoiceSettingsMenu.hpp new file mode 100644 index 0000000..1f9e58d --- /dev/null +++ b/source/frontend/StarVoiceSettingsMenu.hpp @@ -0,0 +1,24 @@ +#ifndef STAR_VOICE_SETTINGS_MENU_HPP +#define STAR_VOICE_SETTINGS_MENU_HPP + +#include "StarBaseScriptPane.hpp" + +namespace Star { + +STAR_CLASS(VoiceSettingsMenu); + +class VoiceSettingsMenu : public BaseScriptPane { +public: + VoiceSettingsMenu(Json const& config); + + virtual void show() override; + void displayed() override; + void dismissed() override; + +private: + +}; + +} + +#endif diff --git a/source/windowing/StarPane.cpp b/source/windowing/StarPane.cpp index a2d32ad..04739cb 100644 --- a/source/windowing/StarPane.cpp +++ b/source/windowing/StarPane.cpp @@ -404,6 +404,10 @@ LuaCallbacks Pane::makePaneCallbacks() { return this->removeChild(widgetName); }); + callbacks.registerCallback("scale", []() -> int { + return GuiContext::singleton().interfaceScale(); + }); + return callbacks; }