This week I was trying to figure out how to play sounds with Java built-in sound API. After 2 days of work I got "something" working - I can easily play/loop/pause/stop MIDIs, simple sounds and streamed audio.
But now I need to change pitch of a sound for my game project. Basically, I need to use 1x-2x pitch. I made the pitch working by changing the sampling rate of a Line. But because of the Java's sampling rate 48kHz limit... I can't set the pitch for my 44.1kHz sounds to a higher value than about 1.08x.
And I thought about something. What if I would just skip some bytes, and don't change the sampling rate any higher than 48kHz (so, if I want to set it higher, I would just set it to 48kHz and skip some bytes) ? The problem is - I can't think of a way to do that properly.

My current code:
package pl.shockah.audio;
 
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Mixer;
 
public abstract class Audio {
	protected static final Mixer mixer;
 
	static {
		mixer = AudioSystem.getMixer(null);
	}
 
	public abstract void play();
	public abstract void loop();
	public abstract void pause();
	public abstract void stop();
	public abstract boolean isPlaying();
	public abstract boolean isPaused();
	public abstract boolean isStopped();
	public abstract boolean isLooping();
}
package pl.shockah.audio;
 
public abstract class AudioWave extends Audio {
	public abstract float getPitch();
	public abstract void setPitch(float pitch);
}

package pl.shockah.audio;
 
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
 
public class MIDI extends Audio implements ControllerEventListener {
	protected static int[] controllers;
 
	static {
		controllers = new int[128];
		for (int i = 0; i < controllers.length; i++) controllers[i] = i;
	}
 
	protected Sequencer sequencer;
	protected boolean paused = false, looping = false;
	protected float factor = 1;
 
	public MIDI(File file) {
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.setSequence(MidiSystem.getSequence(file));
			sequencer.addControllerEventListener(this,controllers);
		} catch (Exception e) {e.printStackTrace();}
	}
	public MIDI(URL url) {
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.setSequence(MidiSystem.getSequence(url));
		} catch (Exception e) {e.printStackTrace();}
	}
	public MIDI(InputStream is) {
		try {
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.setSequence(MidiSystem.getSequence(is));
		} catch (Exception e) {e.printStackTrace();}
	}
 
	public void play() {play(false);}
	public void loop() {play(true);}
	public void play(boolean loop) {
		if (!isPaused()) stop();
		looping = loop;
		paused = false;
		sequencer.setLoopCount(looping ? Sequencer.LOOP_CONTINUOUSLY : 0);
		sequencer.start();
	}
	public void pause() {
		paused = true;
		sequencer.stop();
	}
	public void stop() {
		paused = false;
		looping = false;
		sequencer.stop();
		sequencer.setTickPosition(0);
	}
	public boolean isPlaying() {
		return sequencer.isRunning();
	}
	public boolean isPaused() {
		return paused;
	}
	public boolean isStopped() {
		return !isPaused() && !isPlaying();
	}
	public boolean isLooping() {
		return looping;
	}
 
	public float getTempoFactor() {
		return factor;
	}
	public void setTempoFactor(float factor) {
		this.factor = factor;
		sequencer.setTempoFactor(factor);
	}
 
	public void controlChange(ShortMessage shortMsg) {
		if (shortMsg.getCommand() == ShortMessage.START) paused = false;
	}
}

package pl.shockah.audio;
 
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
 
public class Sample extends AudioWave implements LineListener {
	protected Clip clip;
	protected boolean paused = false, looping = false;
	protected float frequency, pitch = 1;
 
	public Sample(File file) {
		try {
			clip = AudioSystem.getClip(mixer.getMixerInfo());
			clip.open(AudioSystem.getAudioInputStream(file));
			frequency = clip.getFormat().getSampleRate();
			clip.addLineListener(this);
		} catch (Exception e) {e.printStackTrace();}
	}
	public Sample(URL url) {
		try {
			clip = AudioSystem.getClip(mixer.getMixerInfo());
			clip.open(AudioSystem.getAudioInputStream(url));
			frequency = clip.getFormat().getSampleRate();
			clip.addLineListener(this);
		} catch (Exception e) {e.printStackTrace();}
	}
	public Sample(InputStream is) {
		try {
			clip = AudioSystem.getClip(mixer.getMixerInfo());
			clip.open(AudioSystem.getAudioInputStream(is));
			frequency = clip.getFormat().getSampleRate();
			clip.addLineListener(this);
		} catch (Exception e) {e.printStackTrace();}
	}
	public Sample(AudioInputStream ais) {
		try {
			clip = AudioSystem.getClip(mixer.getMixerInfo());
			clip.open(ais);
			frequency = clip.getFormat().getSampleRate();
			clip.addLineListener(this);
		} catch (Exception e) {e.printStackTrace();}
	}
 
	public void play() {play(false);}
	public void loop() {play(true);}
	public void play(boolean loop) {
		looping = loop;
		if (!isPaused()) stop();
		paused = false;
		if (loop) clip.loop(Clip.LOOP_CONTINUOUSLY);
		else clip.start();
	}
	public void pause() {
		paused = true;
		clip.stop();
	}
	public void stop() {
		paused = false;
		clip.stop();
		clip.setFramePosition(0);
	}
	public boolean isPlaying() {
		return clip.isRunning();
	}
	public boolean isPaused() {
		return paused;
	}
	public boolean isStopped() {
		return !isPaused() && !isPlaying();
	}
	public boolean isLooping() {
		return looping;
	}
 
	public float getPitch() {
		return pitch;
	}
	public void setPitch(float pitch) {
		this.pitch = pitch;
		((FloatControl)clip.getControl(FloatControl.Type.SAMPLE_RATE)).setValue(frequency*pitch);
	}
 
	public void update(LineEvent event) {
		if (event.getType() == LineEvent.Type.START) paused = false;
	}
}
package pl.shockah.audio;
 
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
 
public class SampleMulti extends AudioWave implements LineListener {
	protected ArrayList<Sample> samples = new ArrayList<Sample>();
	protected AudioInputStream ais;
 
	public SampleMulti(File file) {
		try {
			ais = AudioSystem.getAudioInputStream(file);
		} catch (Exception e) {e.printStackTrace();}
	}
	public SampleMulti(URL url) {
		try {
			ais = AudioSystem.getAudioInputStream(url);
		} catch (Exception e) {e.printStackTrace();}
	}
 
	public ArrayList<Sample> getSamples() {
		return new ArrayList<Sample>(samples);
	}
 
	public void play() {
		Sample sample = new Sample(ais);
		sample.clip.addLineListener(this);
		samples.add(sample);
		sample.play();
	}
	public void loop() {
		Sample sample = new Sample(ais);
		sample.clip.addLineListener(this);
		samples.add(sample);
		sample.loop();
	}
	public void pause() {
		for (Sample sample : samples) sample.pause();
	}
	public void stop() {
		for (Sample sample : samples) {
			sample.stop();
			sample.clip.removeLineListener(this);
		}
		samples.clear();
	}
	public boolean isPlaying() {
		for (Sample sample : samples) if (sample.isPlaying()) return true;
		return false;
	}
	public boolean isPaused() {
		for (Sample sample : samples) if (!sample.isPaused()) return false;
		return true;
	}
	public boolean isStopped() {
		for (Sample sample : samples) if (!sample.isStopped()) return false;
		return true;
	}
	public boolean isLooping() {
		for (Sample sample : samples) if (sample.isLooping()) return true;
		return false;
	}
 
	public float getPitch() {
		float p = 0; int n = 0;
		for (Sample sample : samples) {p += sample.getPitch(); n++;}
		if (n == 0) return 1;
		return p/((float)n);
	}
	public void setPitch(float pitch) {
		for (Sample sample : samples) sample.setPitch(pitch);
	}
 
	public void update(LineEvent event) {
		if (event.getType() == LineEvent.Type.STOP) {
			for (int i = 0; i < samples.size(); i++) if (event.getSource() == samples.get(i)) {
				if (!samples.get(i).isPaused()) {
					samples.get(i).clip.removeLineListener(this);
					samples.remove(i);
					return;
				}
			}
		}
	}
}

package pl.shockah.audio;
 
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.SourceDataLine;
 
public class Stream extends AudioWave implements LineListener {
	protected SourceDataLine sdl;
	protected AudioInputStream ais;
	protected boolean paused = false;
	protected float frequency, pitch = 1;
 
	protected ThreadStream thread = null;
	protected File ctrFile = null;
	protected URL ctrURL = null;
 
	public Stream(File file) {
		try {
			ctrFile = file;
		} catch (Exception e) {e.printStackTrace();}
	}
	public Stream(URL url) {
		try {
			ctrURL = url;
		} catch (Exception e) {e.printStackTrace();}
	}
	public Stream(InputStream is) {
		try {
			ais = AudioSystem.getAudioInputStream(is);
			sdl = AudioSystem.getSourceDataLine(ais.getFormat(),mixer.getMixerInfo());
			sdl.open(ais.getFormat());
			sdl.addLineListener(this);
		} catch (Exception e) {e.printStackTrace();}
	}
	public Stream(AudioInputStream ais) {
		try {
			this.ais = ais;
			sdl = AudioSystem.getSourceDataLine(ais.getFormat(),mixer.getMixerInfo());
			sdl.open(ais.getFormat());
			sdl.addLineListener(this);
		} catch (Exception e) {e.printStackTrace();}
	}
 
	public void play() {play(false);}
	public void loop() {play(true);}
	public void play(boolean loop) {
		if (!isPaused()) stop();
 
		(thread = new ThreadStream(loop){
			public void run() {
				try {
					do {
						if (!isPaused()) restartStream();
						paused = false;
						sdl.start();
 
						int numRead = 0;
						byte[] buf = new byte[sdl.getBufferSize()];
						while ((numRead = ais.read(buf,0,buf.length)) >= 0 && !stopThread) {
							int offset = 0;
							while (offset < numRead) {
								offset += sdl.write(buf,offset,numRead-offset);
								if (stopThread) return;
							}
							if (stopThread) return;
						}
						if (stopThread) return;
						sdl.drain();
						if (stopThread) return;
						sdl.stop();
						sdl.flush();
					} while (isLooping() && !stopThread);
				} catch (Exception e) {e.printStackTrace();}
			}
		}).start();
	}
	public void pause() {
		if (thread == null) return;
		thread.stopThread = true;
		sdl.stop();
		paused = true;
	}
	public void stop() {
		if (thread == null) return;
		thread.stopThread = true;
		thread.looping = false;
		paused = false;
		sdl.stop();
		sdl.flush();
		thread = null;
	}
	public boolean isPlaying() {
		if (sdl == null) return false;
		return sdl.isRunning();
	}
	public boolean isPaused() {
		return paused;
	}
	public boolean isStopped() {
		return !isPaused() && !isPlaying();
	}
	public boolean isLooping() {
		if (thread == null) return false;
		return thread.looping;
	}
 
	public void update(LineEvent event) {
		if (event.getType() == LineEvent.Type.START) paused = false;
		if (event.getType() == LineEvent.Type.STOP && !isLooping() && !isPaused()) stop();
	}
 
	public float getPitch() {
		return pitch;
	}
	public void setPitch(float pitch) {
		this.pitch = pitch;
		((FloatControl)sdl.getControl(FloatControl.Type.SAMPLE_RATE)).setValue(frequency*pitch);
	}
 
	protected void restartStream() {
		try {
			if (ctrFile == null && ctrURL == null) return;
 
			if (sdl != null && sdl.isOpen()) {
				sdl.stop();
				sdl.flush();
				sdl.removeLineListener(this);
				sdl.close();
			}
 
			if (ctrFile != null) ais = AudioSystem.getAudioInputStream(ctrFile);
			else if (ctrURL != null) ais = AudioSystem.getAudioInputStream(ctrURL);
 
			sdl = AudioSystem.getSourceDataLine(ais.getFormat(),mixer.getMixerInfo());
			sdl.open(ais.getFormat());
			sdl.addLineListener(this);
 
			frequency = sdl.getFormat().getSampleRate();
			setPitch(getPitch());
		} catch (Exception e) {e.printStackTrace();}
	}
}
package pl.shockah.audio;
 
public class ThreadStream extends Thread {
	protected boolean looping;
	protected boolean stopThread = false;
 
	public ThreadStream(boolean looping) {
		this.looping = looping;
	}
}