{"version":1,"id":"mini-game","groups":[{"id":"mini-game","name":"Mini Game","parameters":[{"parameter_name":"BarkMeter","label":"Bark Meter","min":0,"max":100,"default_value":0,"script":"-- Bark Meter\n-- Collects energy from short bark-like mic bursts, then drains when quiet.\n-- Uses adaptive threshold + onset detection so steady noise counts less.\n--\n-- Configurable:\n-- @inspect min=0 max=1 step=0.01\nlocal min_threshold = 0.48\n-- @inspect min=0 max=1 step=0.01\nlocal noise_margin = 0.24\n-- @inspect min=0 max=1 step=0.01\nlocal voice_peak_ratio = 0.72\n-- @inspect min=0 max=1 step=0.01\nlocal rise_threshold = 0.18\n-- @inspect min=0 max=1 step=0.01\nlocal band_rise_threshold = 0.13\n-- @inspect min=0 max=100 step=1\nlocal bark_bonus = 7.0\n-- @inspect min=0 max=100 step=1\nlocal bark_fill = 5.0\n-- @inspect min=0 max=200 step=1\nlocal sustain_fill_speed = 8.0\n-- @inspect min=0 max=300 step=1\nlocal decay_speed = 85.0\n-- @inspect min=0 max=2 step=0.01\nlocal refractory_seconds = 0.20\n\nlocal meter = prev()\nlocal mic = src.MicPeak\nif src.MicVolume > mic then\n    mic = src.MicVolume\nend\n\nlocal prev_mic = src_prev.MicPeak\nif src_prev.MicVolume > prev_mic then\n    prev_mic = src_prev.MicVolume\nend\n\nlocal noise = load(\"noise_floor\", 0.05)\nlocal noise_rate = 0.25\nif mic < noise then\n    noise_rate = 1.5\nend\nlocal noise_step = noise_rate * dt()\nif noise_step > 1.0 then\n    noise_step = 1.0\nend\nnoise = noise + (mic - noise) * noise_step\nsave(\"noise_floor\", noise)\n\nlocal voice_peak = load(\"voice_peak\", min_threshold)\nif mic > voice_peak then\n    voice_peak = voice_peak + (mic - voice_peak) * 0.25\nelse\n    voice_peak = voice_peak - dt() * 0.08\nend\nif voice_peak < min_threshold then\n    voice_peak = min_threshold\nend\nif voice_peak > 1.0 then\n    voice_peak = 1.0\nend\nsave(\"voice_peak\", voice_peak)\n\nlocal threshold = noise + noise_margin\nlocal voice_threshold = voice_peak * voice_peak_ratio\nif threshold < voice_threshold then\n    threshold = voice_threshold\nend\nif threshold < min_threshold then\n    threshold = min_threshold\nend\nif threshold > 0.95 then\n    threshold = 0.95\nend\n\nlocal strength = 0.0\nif mic > threshold then\n    strength = (mic - threshold) / (1.0 - threshold)\n    strength = strength * strength\nend\n\nlocal rise = mic - prev_mic\nlocal band_rise = src.MicMid - src_prev.MicMid\nif src.MicTreble - src_prev.MicTreble > band_rise then\n    band_rise = src.MicTreble - src_prev.MicTreble\nend\nif src.MicBass - src_prev.MicBass > band_rise then\n    band_rise = src.MicBass - src_prev.MicBass\nend\n\nlocal now = time()\nlocal ready = now >= load(\"next_bark_time\", 0.0)\nlocal bark = strength > 0.0 and ready and (rise > rise_threshold or band_rise > band_rise_threshold)\n\nif bark then\n    meter = meter + bark_bonus + strength * bark_fill\n    save(\"next_bark_time\", now + refractory_seconds)\nelseif strength > 0.0 then\n    meter = meter + strength * sustain_fill_speed * dt()\nelse\n    meter = meter - decay_speed * dt()\nend\n\nif meter > param_max() then\n    meter = param_max()\nend\nif meter < param_min() then\n    meter = param_min()\nend\nreturn meter\n"},{"parameter_name":"BlowMeter","label":"Blow Meter","min":0,"max":100,"default_value":0,"script":"-- Blow Meter\n-- Fills quickly on mouth-blown \"phew\" noise while rejecting voice-like wording.\n-- Uses generic audio shape and periodicity values instead of a task-specific detector.\n--\n-- Configurable:\n-- @inspect min=0 max=1 step=0.01\nlocal min_volume = 0.10\n-- @inspect min=0 max=1 step=0.01\nlocal noise_margin = 0.03\n-- @inspect min=0 max=1 step=0.01\nlocal min_brightness = 0.18\n-- @inspect min=0 max=1 step=0.01\nlocal min_flatness = 0.15\n-- @inspect min=0 max=1 step=0.01\nlocal min_centroid = 0.12\n-- @inspect min=0 max=1 step=0.01\nlocal min_treble = 0.15\n-- @inspect min=0 max=1 step=0.01\nlocal max_bass = 0.62\n-- @inspect min=0 max=1 step=0.01\nlocal max_periodicity = 0.58\n-- @inspect min=0 max=1 step=0.01\nlocal hard_periodicity = 0.86\n-- @inspect min=0 max=5000 step=1\nlocal fill_speed = 2000\n-- @inspect min=0 max=2000 step=1\nlocal decay_speed = 1000.0\n-- @inspect min=0 max=1 step=0.01\nlocal release_delay = 0.12\n-- @inspect min=0 max=20 step=0.1\nlocal smooth_speed = 24.0\n\nlocal function clamp(value, min_value, max_value)\n    if value < min_value then return min_value end\n    if value > max_value then return max_value end\n    return value\nend\n\nlocal volume = src.MicVolume\nif src.MicPeak > volume then\n    volume = src.MicPeak\nend\n\nlocal noise = load(\"blow_noise_floor\", 0.02)\nlocal noise_rate = 0.08\nif volume < noise then\n    noise_rate = 1.10\nend\nlocal noise_step = clamp(noise_rate * dt(), 0.0, 1.0)\nnoise = noise + (volume - noise) * noise_step\nsave(\"blow_noise_floor\", noise)\n\nlocal volume_threshold = noise + noise_margin\nif volume_threshold < min_volume then\n    volume_threshold = min_volume\nend\nif volume_threshold > 0.85 then\n    volume_threshold = 0.85\nend\n\nlocal raw_volume_score = 0.0\nif volume > volume_threshold then\n    raw_volume_score = (volume - volume_threshold) / (1.0 - volume_threshold)\nend\nlocal volume_score = math.sqrt(clamp(raw_volume_score, 0.0, 1.0))\n\nlocal brightness_score = clamp((src.MicBrightness - min_brightness) / (1.0 - min_brightness), 0.0, 1.0)\nlocal flatness_score = clamp((src.MicFlatness - min_flatness) / (1.0 - min_flatness), 0.0, 1.0)\nlocal centroid_score = clamp((src.MicCentroid - min_centroid) / (1.0 - min_centroid), 0.0, 1.0)\nlocal treble_score = clamp((src.MicTreble - min_treble) / (1.0 - min_treble), 0.0, 1.0)\nlocal air_score = brightness_score\nif flatness_score > air_score then air_score = flatness_score end\nif treble_score > air_score then air_score = treble_score end\n\nlocal treble_dominance = clamp((src.MicTreble - src.MicMid * 0.70 + 0.20) / 0.45, 0.0, 1.0)\nlocal bass_penalty = clamp((src.MicBass - max_bass) / (1.0 - max_bass), 0.0, 1.0)\nlocal mid_penalty = clamp((src.MicMid - src.MicTreble * 0.95) / 0.45, 0.0, 1.0)\nlocal periodicity_penalty = clamp((src.MicPeriodicity - max_periodicity) / (1.0 - max_periodicity), 0.0, 1.0)\nlocal onset_penalty = clamp((src.MicOnset - 0.90) / 0.10, 0.0, 1.0) * 0.15\n\nlocal shape_score = 0.10 + brightness_score * 0.20 + flatness_score * 0.35 + centroid_score * 0.15 + treble_score * 0.20\nlocal score = volume_score * shape_score * (0.40 + treble_dominance * 0.60)\nif air_score < 0.05 then\n    score = 0.0\nend\nif src.MicPeriodicity >= hard_periodicity then\n    score = 0.0\nend\nscore = score * (1.0 - bass_penalty * 0.45)\nscore = score * (1.0 - mid_penalty * 0.50)\nscore = score * (1.0 - periodicity_penalty * 0.85)\nscore = score * (1.0 - onset_penalty)\nscore = clamp(score, 0.0, 1.0)\n\nlocal smoothed = load(\"blow_score\", 0.0)\nlocal smooth_step = clamp(smooth_speed * dt(), 0.0, 1.0)\nsmoothed = smoothed + (score - smoothed) * smooth_step\nsave(\"blow_score\", smoothed)\n\nlocal now = time()\nlocal last_blow_time = load(\"blow_last_active\", 0.0)\nlocal meter = prev()\nif smoothed > 0.025 then\n    meter = meter + fill_speed * smoothed * dt()\n    save(\"blow_last_active\", now)\nelseif now - last_blow_time > release_delay then\n    meter = meter - decay_speed * dt()\nend\n\nmeter = clamp(meter, param_min(), param_max())\nreturn meter"},{"parameter_name":"OsuCircle","label":"Osu Circle","min":0,"max":100,"default_value":0,"script":"-- Osu Circle\n-- Move the mouse in circles around the screen center to fill the meter.\n-- Easier circle motion is accepted, but straight sweeps are filtered.\n-- Stop circling and it drops after a short delay.\n--\n-- Configurable:\n-- @inspect min=0 max=1 step=0.01\nlocal min_radius = 0.12\n-- @inspect min=0 max=0.2 step=0.001\nlocal min_motion = 0.008\n-- @inspect min=0 max=1 step=0.01\nlocal min_turn = 0.035\n-- @inspect min=0 max=1 step=0.01\nlocal max_radial_ratio = 0.55\n-- @inspect min=0 max=1 step=0.01\nlocal min_tangent_ratio = 0.45\n-- @inspect min=0 max=3.14 step=0.01\nlocal min_arc_before_gain = 0.75\n-- @inspect min=0 max=200 step=1\nlocal gain_speed = 55.0\n-- @inspect min=0 max=3 step=0.01\nlocal decay_delay = 0.45\n-- @inspect min=0 max=200 step=1\nlocal decay_speed = 40.0\n\nlocal meter = prev()\nlocal x = src.MouseX\nlocal y = src.MouseY\nlocal last_x = load(\"circle_last_x\", x)\nlocal last_y = load(\"circle_last_y\", y)\nlocal dx = x - last_x\nlocal dy = y - last_y\nlocal motion = math.sqrt(dx * dx + dy * dy)\nlocal radius = math.sqrt(x * x + y * y)\nlocal last_radius = math.sqrt(last_x * last_x + last_y * last_y)\n\nlocal angle = math.atan(y, x)\nlocal last_angle = load(\"circle_last_angle\", angle)\nlocal turn = angle - last_angle\nlocal pi = 3.141592653589793\nwhile turn > pi do\n    turn = turn - pi * 2.0\nend\nwhile turn < -pi do\n    turn = turn + pi * 2.0\nend\n\nlocal turn_amount = math.abs(turn)\nlocal direction = 0.0\nif turn > 0.0 then\n    direction = 1.0\nelseif turn < 0.0 then\n    direction = -1.0\nend\n\nlocal radial_delta = radius - last_radius\nlocal radial_change = math.abs(radial_delta)\nlocal tangent_motion = 0.0\nif radius > 0.0 then\n    tangent_motion = math.abs(x * dy - y * dx) / radius\nend\nlocal radial_ratio = 1.0\nlocal tangent_ratio = 0.0\nif motion > 0.0 then\n    radial_ratio = radial_change / motion\n    tangent_ratio = tangent_motion / motion\nend\n\nlocal radial_direction = 0.0\nlocal radial_flip_reset_ratio = 0.18\nif radial_delta > min_motion * 0.25 then\n    radial_direction = 1.0\nelseif radial_delta < -min_motion * 0.25 then\n    radial_direction = -1.0\nend\nlocal previous_radial_direction = load(\"circle_radial_direction\", 0.0)\nlocal radial_flip = previous_radial_direction ~= 0.0 and radial_direction ~= 0.0 and radial_direction ~= previous_radial_direction and radial_ratio > radial_flip_reset_ratio\n\nlocal previous_direction = load(\"circle_direction\", 0.0)\nlocal direction_ok = previous_direction == 0.0 or direction == 0.0 or direction == previous_direction\nlocal circle_motion = radius >= min_radius and last_radius >= min_radius and motion >= min_motion and turn_amount >= min_turn and radial_ratio <= max_radial_ratio and tangent_ratio >= min_tangent_ratio and direction_ok and not radial_flip\n\nlocal now = time()\nlocal arc = load(\"circle_arc\", 0.0)\nif circle_motion then\n    arc = arc + turn_amount\n    save(\"circle_arc\", arc)\n    save(\"circle_direction\", direction)\n    if arc >= min_arc_before_gain then\n        local turn_quality = turn_amount / 0.18\n        if turn_quality > 1.0 then\n            turn_quality = 1.0\n        end\n        local motion_quality = motion / 0.05\n        if motion_quality > 1.0 then\n            motion_quality = 1.0\n        end\n        local tangent_quality = 1.0\n        if min_tangent_ratio < 1.0 then\n            tangent_quality = (tangent_ratio - min_tangent_ratio) / (1.0 - min_tangent_ratio)\n            if tangent_quality < 0.0 then\n                tangent_quality = 0.0\n            end\n            if tangent_quality > 1.0 then\n                tangent_quality = 1.0\n            end\n        end\n        local quality = (turn_quality + motion_quality + tangent_quality) / 3.0\n        meter = meter + gain_speed * (0.35 + quality * 0.65) * dt()\n        save(\"circle_last_active\", now)\n    end\nelse\n    if motion >= min_motion then\n        arc = arc - turn_amount * 0.75\n        if arc < 0.0 then\n            arc = 0.0\n        end\n        save(\"circle_arc\", arc)\n    end\n    local last_active = load(\"circle_last_active\", 0.0)\n    if now - last_active > decay_delay then\n        meter = meter - decay_speed * dt()\n    end\n    if motion < min_motion then\n        save(\"circle_direction\", 0.0)\n        save(\"circle_radial_direction\", 0.0)\n        save(\"circle_arc\", 0.0)\n    elseif radial_ratio > radial_flip_reset_ratio then\n        save(\"circle_radial_direction\", radial_direction)\n    end\nend\n\nsave(\"circle_last_x\", x)\nsave(\"circle_last_y\", y)\nif radius >= min_radius then\n    save(\"circle_last_angle\", angle)\nend\n\nif meter > param_max() then\n    meter = param_max()\nend\nif meter < param_min() then\n    meter = param_min()\nend\nreturn meter\n"}]}],"mappings":{}}