|
| 1 | +-- put this into /var/tweak/com.r333d.eqe/lua/autorun/app/ |
| 2 | +-- load by restarting the EQE app |
| 3 | + |
| 4 | +local str_esc = require 'str_esc' |
| 5 | +local md = require 'md' |
| 6 | + |
| 7 | +local page = {} |
| 8 | +page.title = 'Crossfeed (beta)' |
| 9 | +page.icon = IMG('headphone.png', PAGE_ICON_COLOR):retain() |
| 10 | + |
| 11 | +local function ipc(s, safe) |
| 12 | + s = str_esc(s) |
| 13 | + if safe then |
| 14 | + return IPC('return eqe.raw('..s..')') |
| 15 | + else |
| 16 | + return IPC('return eqe.raw('..s..', true)') |
| 17 | + end |
| 18 | +end |
| 19 | + |
| 20 | +local thumbImg = IMG('thumb.png'):retain() |
| 21 | + |
| 22 | +local pad = 11 |
| 23 | +local function create_slider(y, width, title, m) |
| 24 | + local self = {} |
| 25 | + self.onchange = function() end |
| 26 | + self.onfinish = function() end |
| 27 | + |
| 28 | + self.label = objc.UILabel:alloc():initWithFrame{{pad,y},{width - pad*2,44}} |
| 29 | + self.label:setFont(objc.UIFont:fontWithName_size('HelveticaNeue', 16)) |
| 30 | + self.label:setTextColor(COLOR(0xffffff8d)) |
| 31 | + self.label:setBackgroundColor(objc.UIColor:clearColor()) |
| 32 | + self.label:setText(title) |
| 33 | + |
| 34 | + y = y + self.label:frame().size.height |
| 35 | + |
| 36 | + self.slider = objc.EQEOBSlider:alloc():initWithFrame{{pad,y},{width - pad*2,44}} |
| 37 | + self.slider:setThumbImage_forState(thumbImg, UIControlStateNormal) |
| 38 | + self.slider:setMinimumTrackTintColor(COLOR(0xffffff80)) |
| 39 | + self.slider:setMaximumTrackTintColor(COLOR(0xffffff50)) |
| 40 | + |
| 41 | + local target = ns.target:new() |
| 42 | + function target.onaction() |
| 43 | + self.onfinish() |
| 44 | + end |
| 45 | + self.slider:addTarget_action_forControlEvents(target.m, target.sel, bit.bor(UIControlEventTouchUpInside, UIControlEventTouchUpOutside)) |
| 46 | + |
| 47 | + local target = ns.target:new() |
| 48 | + function target.onaction() |
| 49 | + self.onchange() |
| 50 | + end |
| 51 | + self.slider:addTarget_action_forControlEvents(target.m, target.sel, bit.bor(UIControlEventValueChanged)) |
| 52 | + |
| 53 | + local target = ns.target:new() |
| 54 | + self.slider:addTarget_action_forControlEvents(target.m, target.sel, UIControlEventValueChanged) |
| 55 | + function self.updatetext() |
| 56 | + self.label:setText(title..': '..self.slider:value()) |
| 57 | + end |
| 58 | + function target.onaction() |
| 59 | + self.updatetext() |
| 60 | + end |
| 61 | + |
| 62 | + y = y + self.slider:frame().size.height |
| 63 | + |
| 64 | + m:view():addSubview(self.slider) |
| 65 | + m:view():addSubview(self.label) |
| 66 | + |
| 67 | + return self, y |
| 68 | +end |
| 69 | + |
| 70 | +local function create_button(x, y, canvaswidth, title, m) |
| 71 | + local height = 34 |
| 72 | + local width = canvaswidth*2/3 |
| 73 | + local button = ui.button:new() |
| 74 | + button.m:setFrame{{x + (canvaswidth-width)/2, y},{width,height}} |
| 75 | + button.m:layer():setCornerRadius(8) |
| 76 | + button:setFont('HelveticaNeue', 16) |
| 77 | + button:setColor(COLOR(0xff, 0xff, 0xff, 0xff*0.7)) |
| 78 | + button.m:setBackgroundColor(COLOR(0xff, 0xff, 0xff, 0xff*0.07)) |
| 79 | + button:setTitle(title) |
| 80 | + m:view():addSubview(button.m) |
| 81 | + return button, y + height |
| 82 | +end |
| 83 | + |
| 84 | +local function create_switch(y, canvaswidth, title, m) |
| 85 | + local target = ns.target:new() |
| 86 | + target.switch = objc.UISwitch:alloc():init() |
| 87 | + local s = target.switch:frame().size |
| 88 | + local x = canvaswidth - s.width - pad*3 |
| 89 | + target.switch:setFrame{{x, y},s} |
| 90 | + target.switch:setOnTintColor(COLOR(0x4bc2ffaa)) |
| 91 | + target.switch:setTintColor(COLOR(0xffffff55)) |
| 92 | + target.switch:addTarget_action_forControlEvents(target.m, target.sel, UIControlEventValueChanged) |
| 93 | + |
| 94 | + target.label = objc.UILabel:alloc():init() |
| 95 | + target.label:setFont(objc.UIFont:fontWithName_size('HelveticaNeue', 16)) |
| 96 | + target.label:setTextColor(COLOR(0xffffff8d)) |
| 97 | + target.label:setBackgroundColor(objc.UIColor:clearColor()) |
| 98 | + target.label:setText(title) |
| 99 | + target.label:sizeToFit() |
| 100 | + local switchS = s |
| 101 | + local s = target.label:frame().size |
| 102 | + target.label:setFrame{{x - s.width - pad, y + (switchS.height - s.height)/2},s} |
| 103 | + |
| 104 | + m:view():addSubview(target.label) |
| 105 | + m:view():addSubview(target.switch) |
| 106 | + return target, y + target.switch:frame().size.height |
| 107 | +end |
| 108 | + |
| 109 | +function page:init() |
| 110 | + local vc = VIEWCONTROLLER(function(m) |
| 111 | + local frame = m:view():frame() |
| 112 | + local width = frame.size.width |
| 113 | + |
| 114 | + local intensity, y = create_slider(64, width, 'Intensity', m) |
| 115 | + intensity.slider:setMinimumValue(0) |
| 116 | + intensity.slider:setMaximumValue(1) |
| 117 | + function intensity.onchange() |
| 118 | + ipc('crossfeed.intensity('..intensity.slider:value()..')') |
| 119 | + end |
| 120 | + function intensity.onfinish() |
| 121 | + intensity.onchange() |
| 122 | + ipc('crossfeed.save()') |
| 123 | + end |
| 124 | + self.intensity = intensity |
| 125 | + |
| 126 | + local delay, y = create_slider(y, width, 'Delay (ms)', m) |
| 127 | + delay.slider:setMinimumValue(0) |
| 128 | + delay.slider:setMaximumValue(4) |
| 129 | + function delay.onchange() |
| 130 | + ipc('crossfeed.delay('..delay.slider:value()..')') |
| 131 | + end |
| 132 | + function delay.onfinish() |
| 133 | + delay.onchange() |
| 134 | + ipc('crossfeed.save()') |
| 135 | + end |
| 136 | + self.delay = delay |
| 137 | + |
| 138 | + --[[ |
| 139 | + y = y + pad*2 |
| 140 | +
|
| 141 | + self.save = create_button(0, y, width/2, 'Save', m) |
| 142 | + local load, y = create_button(width/2, y, width/2, 'Load', m) |
| 143 | + self.load = load |
| 144 | +
|
| 145 | + function self.save.ontoggle() |
| 146 | + ipc('crossfeed.save()') |
| 147 | + end |
| 148 | +
|
| 149 | + function self.load.ontoggle() |
| 150 | + ipc('crossfeed.load()') |
| 151 | + self:refresh() |
| 152 | + end |
| 153 | + ]] |
| 154 | + |
| 155 | + y = y + pad*2 |
| 156 | + |
| 157 | + local enable, y = create_switch(y, width, 'Enabled', m) |
| 158 | + function enable.onaction() |
| 159 | + local on = objc.weirdbool(enable.switch:isOn()) |
| 160 | + ipc('crossfeed.enabled('..tostring(on)..')', true) |
| 161 | + end |
| 162 | + self.enable = enable |
| 163 | + |
| 164 | + local r = md.new(width - pad*2) |
| 165 | + self.helper_text = r.m |
| 166 | + self.helper_text:setFrame{{pad, y + pad},{0,0}} |
| 167 | + m:view():addSubview(self.helper_text) |
| 168 | + r:init[[ |
| 169 | +If you have no clue what this does, [here's a pretty good explanation](http://www.meier-audio.homepage.t-online.de/crossfeed.htm). |
| 170 | +
|
| 171 | +Basically, you should use this if you're using headphones, it makes it sound more "full" and "open", like speakers. |
| 172 | +
|
| 173 | +Credits to xC0nfused on [Discord](https://discord.gg/RSJWAuX) for helping make this. This code is open source.]] |
| 174 | + |
| 175 | + end) |
| 176 | + self.view = vc:view() |
| 177 | +end |
| 178 | + |
| 179 | +function page:refresh() |
| 180 | + local intensity = tonumber(ipc('return crossfeed.intensity()')) |
| 181 | + local delay = tonumber(ipc('return crossfeed.delay()')) |
| 182 | + local enabled = ipc('return crossfeed.enabled()') == 'true' |
| 183 | + |
| 184 | + self.intensity.slider:setValue(intensity) |
| 185 | + self.intensity.updatetext() |
| 186 | + self.delay.slider:setValue(delay) |
| 187 | + self.delay.updatetext() |
| 188 | + self.enable.switch:setOn(enabled) |
| 189 | +end |
| 190 | + |
| 191 | +function page:hide(hiding) |
| 192 | + if not hiding then |
| 193 | + self:refresh() |
| 194 | + end |
| 195 | + |
| 196 | +end |
| 197 | + |
| 198 | +Page.crossfeed = page |
| 199 | +ADD_NAV_PAGE(page, Page.eqe) |
0 commit comments