Jump to content
View in the app

A better way to browse. Learn more.

OSBot :: 2007 OSRS Botting

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Java Music Loader/Player class

Featured Replies

Not written by me, I slightly optimized / tweaked it.

Singleton pattern.

package audio;

import java.io.File;
import java.util.HashMap;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class MusicPlayer {
	
	private static MusicPlayer instance = new MusicPlayer();
	private HashMap<String, Clip> clips = new HashMap<String, Clip>();
	private int frameGap;
	private boolean mute;
		
	private MusicPlayer() {
	}
	
	public static MusicPlayer getInstance() {
		return instance == null ? instance = new MusicPlayer() : instance;
	}
	
	public void load(File file, String name) {
		if (clips.get(name) != null) {
			return;
		}
		Clip clip;
		try {			
			AudioInputStream rawStream = AudioSystem.getAudioInputStream(file);
			AudioFormat baseFormat = rawStream.getFormat();
			AudioFormat decodeFormat = new AudioFormat(
					AudioFormat.Encoding.PCM_SIGNED,
					baseFormat.getSampleRate(), 
					16, 
					baseFormat.getChannels(),
					baseFormat.getChannels() * 2, 
					baseFormat.getSampleRate(),
					false);
			AudioInputStream decodedStream = AudioSystem.getAudioInputStream(decodeFormat, rawStream);
			clip = AudioSystem.getClip();
			clip.open(decodedStream);
			clips.put(name, clip);
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void play(String name) {
		play(name, frameGap);
	}
	
	public void play(String name, int framePosition) {
		if (mute) {
			return;
		}
		Clip clip = clips.get(name);
		if (clip == null) {
			return;
		}
		if (clip.isRunning()) {
			clip.stop();
		}
		clip.setFramePosition(framePosition);
		while (!clip.isRunning()) {
			clip.start();
		}
	}
	
	public void stop(String name) {
		if(clips.get(name) == null) {
			return;
		}
		if(clips.get(name).isRunning()) {
			clips.get(name).stop();
		}
	}
	
	public void resume(String name) {
		if (mute) {
			return;
		}
		if (clips.get(name).isRunning()) {
			return;
		}
		clips.get(name).start();
	}
	
	public void loop(String name) {
		loop(name, frameGap, frameGap, clips.get(name).getFrameLength() - 1);
	}
	
	public void loop(String name, int framePosition) {
		loop(name, framePosition, frameGap, clips.get(name).getFrameLength() - 1);
	}
	
	public void loop(String name, int loopStartPosition, int loopEndPosition) {
		loop(name, frameGap, loopStartPosition, loopEndPosition);
	}
	
	public void loop(String name, int framePosition, int loopStartPosition, int loopEndPosition) {
		stop(name);
		if(mute) {
			return;
		}
		clips.get(name).setLoopPoints(loopStartPosition, loopEndPosition);
		clips.get(name).setFramePosition(framePosition);
		clips.get(name).loop(Clip.LOOP_CONTINUOUSLY);
	}
	
	public void setPosition(String name, int framePosition) {
		clips.get(name).setFramePosition(framePosition);
	}
	
	public int getFrameLength(String name) {
		return clips.get(name).getFrameLength();
	}

	public int getFramePosition(String name) {
		return clips.get(name).getFramePosition();
	}
	
	public void close(String name) {
		stop(name);
		clips.get(name).close();
	}
	
}

Edited by Botre

  • 1 month later...

Why did you declare clip outside of the try scope within the load method? And I'd personally use "pause" as the identifier for the stop method (nothing too different, just a bit more descriptive. "loop" also seems as if I'm looping the music player, rather than processing the song (although if that is the case, then please excuse me).

I'm a bit confused as to why you put "clip.start()" in a while loop. Why must you repeatedly call "start()"? I haven't gone beyond the basics of audio streaming, so I'm not sure if there's something I'm not understanding.

Not a fan of global state, so I probably won't use this, but I'm sure others could make great use of it. Also, not sure if you have anything against enums (not sure why you would), but they are definitely the way to go for singletons.

If you could answer my questions, I would greatly appreciate it :) I'm new to the site, and you seem smart and reputable, so I would love to pick at your brain if you wouldn't mind; I'm always looking to learn new things

  • Author

 

 

Why did you declare clip outside of the try scope within the load method? 

 

Absolutely no reason, I checked my most recent version of this method and turns out  I had already changed it:

	public void load(File file, String name) {
		if (getClips().get(name) != null) {
			return;
		}
		try {			
			AudioInputStream decodedInputStream = SoundUtilities.decodeInputStream(AudioSystem.getAudioInputStream(file));
			Clip clip = AudioSystem.getClip();
			clip.open(decodedInputStream);
			getClips().put(name, clip);
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}

The decodeInputStream method:

	public static AudioInputStream decodeInputStream(AudioInputStream input) {		
			AudioFormat rawFormat = input.getFormat();
			AudioFormat decoderFormat = new AudioFormat(
					AudioFormat.Encoding.PCM_SIGNED,
					rawFormat.getSampleRate(), 
					16, 
					rawFormat.getChannels(),
					rawFormat.getChannels() * 2, 
					rawFormat.getSampleRate(),
					false);
		return AudioSystem.getAudioInputStream(decoderFormat, input);
	}

 

 

And I'd personally use "pause" as the identifier for the stop method (nothing too different, just a bit more descriptive.

 

//TODO stop() -> pause()

//TODO proper stop() method

Thanks!

 

 

 

 "loop" also seems as if I'm looping the music player, rather than processing the song (although if that is the case, then please excuse me).

 

//TODO loop() -> loopClip() 

Thanks!

 

 

 

I'm a bit confused as to why you put "clip.start()" in a while loop. Why must you repeatedly call "start()"? I haven't gone beyond the basics of audio streaming, so I'm not sure if there's something I'm not understanding.

 

I'm afraid I don't understand the whole reasoning behind it either (looking into it though!) but I'm pretty sure it's a DataLine/IO Buffering thing. 

 

 

 

Not a fan of global state, so I probably won't use this, but I'm sure others could make great use of it. Also, not sure if you have anything against enums (not sure why you would), but they are definitely the way to go for singletons.

 

I like enums! Anywhere you would use them in here? 

 

 

If you could answer my questions, I would greatly appreciate it smile.png I'm new to the site, and you seem smart and reputable, so I would love to pick at your brain if you wouldn't mind; I'm always looking to learn new things

 

Welcome to OSB :D

And thanks for the tips, I love getting constructive feedback like this.

 

I like enums! Anywhere you would use them in here?

If lazy initialization isn't needed, you could actually replace your class for an enum. Heard it from Effective Java second edition:

public enum MusicPlayer {
        PLAYER; //could use a better name

     	private static MusicPlayer instance = new MusicPlayer();
	private HashMap<String, Clip> clips = new HashMap<String, Clip>();
	private int frameGap;
	private boolean mute;

        //...
}
Due to the nature of how enums are created, you don't need to worry about double-checked locking if you switch to a multithreaded environment. Also, serialization doesn't break the singleton design (when deserializing a class object, a new instance is constructed in memory. Enums prevent this, allowing you to simply discard the new object by overriding readResolve and returning the pre-existing instance.

Enums are basically the future of the singleton design. I'm not a huge fan of the singleton pattern, other than maybe for logging, but hopefully this will come in handy for those who use it quite often.

Welcome to OSB :D

And thanks for the tips, I love getting constructive feedback like this.

Thanks! :) I plan on staying, although I'm not much of a RuneScaper myself anymore (quit a few years ago). I've been helping friends with this scripts every now-and-then, so within time I'll hopefully be familiar enough with the API and botting aspects (like antiban) to release my own innovative scripts. Thanks for the warm welcome, much appreciated :D

Edited by fixthissite

  • Author

If lazy initialization isn't needed, you could actually replace your class for an enum. Heard it from Effective Java second edition:

 

public enum MusicPlayer {
        PLAYER; //could use a better name

     	private static MusicPlayer instance = new MusicPlayer();
	private HashMap<String, Clip> clips = new HashMap<String, Clip>();
	private int frameGap;
	private boolean mute;

        //...
}
Due to the nature of how enums are created, you don't need to worry about double-checked locking if you switch to a multithreaded environment. Also, serialization doesn't break the singleton design (when deserializing a class object, a new instance is constructed in memory. Enums prevent this, allowing you to simply discard the new object by overriding readResolve and returning the pre-existing instance.

Enums are basically the future of the singleton design. I'm not a huge fan of the singleton pattern, other than maybe for logging, but hopefully this will come in handy for those who use it quite often.

 

 

Holy guacamole.

Just did some reading on Enum Singletons, neat as f!

Holy guacamole.

Just did some reading on Enum Singletons, neat as f!

 

Glad you found that information useful smile.png

 

So, I messed with this for a bit, and I found a couple problems. Nothing huge, just a few minor things I found a bit unappealing:

 

When the clip plays, apparently it plays on a background thread. This thread is not daemon, so unless we keep our calling thread alive, the music won't play. Although threads handling sound tend to be non-daemon, since sound isn't usually the main aspect of the application, this could definitely confuse people who aren't familiar with the API (it got me, that's forsure). You should at least mention this so people who reference this in the future don't need to ask. Since we can't make the thread non-daemon (without cheap tricks), I think allowing the client to optionally block the calling thread until the song has finished would be nice, but that's no big deal really.

 

The second problem is when you play a clip one right after the other. The second and third time, it seems to cut off a bit in the beginning. A simple delay between stopping and starting the song (in the play method) fixed this for me. 200ms seemed reasonable, although I really wish Java's Sound API didn't require me to do all this /:

 

Other than that, this was a really nice quick introduction into the Java Sound API for me. Thanks for sharing!

Edited by fixthissite

Guest
This topic is now closed to further replies.

Recently Browsing 0

  • No registered users viewing this page.

Account

Navigation

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.