Skip to content

Add plugin: TickSync Music#11369

Open
theOranguzang wants to merge 6 commits intorunelite:masterfrom
theOranguzang:feature/ticksync-music
Open

Add plugin: TickSync Music#11369
theOranguzang wants to merge 6 commits intorunelite:masterfrom
theOranguzang:feature/ticksync-music

Conversation

@theOranguzang
Copy link
Copy Markdown

Tempo-syncs OSRS music to the 600ms game tick grid so beats land on tick boundaries. Reads MIDI from the local game cache, quantizes tempo to tick-aligned BPMs (25, 50, 100, 200), and plays back synced to GameTick events with drift correction.
MIDI-only — zero external dependencies beyond RuneLite's cache library.

@runelite-github-app
Copy link
Copy Markdown

runelite-github-app bot commented Apr 4, 2026

@theOranguzang theOranguzang requested a review from a team as a code owner April 4, 2026 18:11
@runelite-github-app
Copy link
Copy Markdown

Feedback:

  • Use of javax.sound.midi.MidiSystem is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MetaMessage.getType is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.getChannels is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MetaMessage.setMessage is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MetaMessage.getData is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequence.<init> is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.start is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.unloadAllInstruments is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Receiver.send is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Receiver.close is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.ShortMessage.<init> is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.InvalidMidiDataException.getMessage is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequence.getDivisionType is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiSystem.getSoundbank is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.ShortMessage.getChannel is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.setSequence is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.getReceiver is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.isOpen is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiUnavailableException is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Track.add is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MetaMessage is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.InvalidMidiDataException is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.ShortMessage.getCommand is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.loadAllInstruments is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.isOpen is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequence.getResolution is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.close is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiChannel is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.getDefaultSoundbank is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiEvent.getMessage is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.stop is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Soundbank.getInstruments is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Soundbank is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.getTransmitter is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.setMicrosecondPosition is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Track is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiChannel.programChange is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Track.size is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequence.createTrack is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiChannel.controlChange is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MetaMessage.<init> is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequence is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Track.get is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiSystem.getSequence is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.open is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiMessage is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.getMicrosecondLength is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.open is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiEvent is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiEvent.<init> is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.setLoopCount is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.setTempoInBPM is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Transmitter.setReceiver is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.ShortMessage is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Receiver is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Synthesizer.close is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.isRunning is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.ShortMessage.getData1 is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Transmitter is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiEvent.getTick is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiSystem.getSynthesizer is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequence.getTracks is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.ShortMessage.getData2 is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.MidiSystem.getSequencer is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead
  • Use of javax.sound.midi.Sequencer.getMicrosecondPosition is not allowed: Use of javax.sound, use net.runelite.client.audio.AudioPlayer instead

A new scan will be automatically performed on the next push to the PR branch.


Internal use only: Reviewer details Maintainer details

@theOranguzang
Copy link
Copy Markdown
Author

Hi — thanks for the automated review. I understand the general preference for net.runelite.client.audio.AudioPlayer, but this plugin fundamentally cannot work through that API. Here's why:

TickSync Music is a MIDI sequencer plugin — it reads MIDI data from the OSRS game cache, rewrites tempo meta-events to tick-aligned BPMs (25/50/100/200), and plays the rewritten sequences through a Java Synthesizer loaded with the OSRS soundfont. It requires:

  • javax.sound.midi.Sequencer — real-time tempo control (setTempoInBPM), microsecond-precision seeking (setMicrosecondPosition) for drift correction synced to GameTick events
  • javax.sound.midi.Synthesizer — loading a custom .sf2 soundfont (the OSRS instrument bank), per-channel program changes and control changes
  • javax.sound.midi.Sequence / Track / MidiEvent — parsing and rewriting MIDI tempo meta-events and remapping MIDI channels (channel 9 melodic workaround for Gervill's GM drum-lock)
  • javax.sound.midi.Receiver — intercepting and remapping CC messages between Sequencer and Synthesizer (bank select fix for OSRS soundfont patches)

AudioPlayer is a fire-and-forget clip player (play(file, gain)). It doesn't expose any MIDI sequencing, tempo manipulation, soundfont loading, or real-time synchronization — which is the entire purpose of this plugin.

Is there an exception or allowlist process for plugins that require low-level MIDI control? Happy to discuss the architecture further if that would help with review. The full technical docs are in the repo at docs/BLUEPRINT.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants