Reworked audio
This commit is contained in:
		| @@ -18,7 +18,7 @@ | |||||||
| package ru.windcorp.progressia.client; | package ru.windcorp.progressia.client; | ||||||
|  |  | ||||||
| import ru.windcorp.progressia.ProgressiaLauncher; | import ru.windcorp.progressia.ProgressiaLauncher; | ||||||
| import ru.windcorp.progressia.client.audio.backend.ALTest; | import ru.windcorp.progressia.client.audio.ALTest; | ||||||
|  |  | ||||||
| public class ProgressiaClientMain { | public class ProgressiaClientMain { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio; | ||||||
|  |  | ||||||
|  | public class ALTest { | ||||||
|  | 	static private void initializeAL() { | ||||||
|  | 		AudioManager.initAL(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	static void loadALData() { | ||||||
|  | 		AudioManager.loadSound("assets/sounds/sample_stereo.ogg", | ||||||
|  | 				"Progressia", "SampleStereo", | ||||||
|  | 				AudioFormat.STEREO); | ||||||
|  | 		Music music  = new Music("Progressia", "SampleStereo"); | ||||||
|  | 		music.play(false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	static void killALData() { | ||||||
|  | 		//TODO implement the method or its analogue | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static void execute() { | ||||||
|  | 		initializeAL(); | ||||||
|  | 		loadALData(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio; | ||||||
|  |  | ||||||
|  | public enum AudioFormat { | ||||||
|  |     MONO, STEREO | ||||||
|  | } | ||||||
| @@ -0,0 +1,142 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio; | ||||||
|  |  | ||||||
|  | import org.lwjgl.openal.AL; | ||||||
|  | import org.lwjgl.openal.ALC; | ||||||
|  | import org.lwjgl.openal.ALCCapabilities; | ||||||
|  | import org.lwjgl.openal.ALCapabilities; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.AudioReader; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.Listener; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.SoundType; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.Speaker; | ||||||
|  |  | ||||||
|  | import static org.lwjgl.openal.AL11.*; | ||||||
|  | import static org.lwjgl.openal.ALC10.*; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class AudioManager { | ||||||
|  | 	private static long device; | ||||||
|  | 	private static ALCCapabilities deviceCapabilities; | ||||||
|  | 	private static ALCapabilities alCapabilities; | ||||||
|  |  | ||||||
|  | 	private static final int SOUNDS_NUM = 64; | ||||||
|  | 	private static int lastSoundIndex = 0; | ||||||
|  |  | ||||||
|  | 	private static List<Speaker> soundSpeakers = new ArrayList<>(SOUNDS_NUM); | ||||||
|  | 	private static Speaker musicSpeaker; | ||||||
|  | 	private static ArrayList<SoundType> soundsBuffer = new ArrayList<>(); | ||||||
|  | 	 | ||||||
|  | 	public static void initAL() { | ||||||
|  | 		String defaultDeviceName = alcGetString( | ||||||
|  | 				0, | ||||||
|  | 				ALC_DEFAULT_DEVICE_SPECIFIER | ||||||
|  | 		); | ||||||
|  |  | ||||||
|  | 		device = alcOpenDevice(defaultDeviceName); | ||||||
|  |  | ||||||
|  | 		int[] attributes = new int[1]; | ||||||
|  | 		long context = alcCreateContext(device, attributes); | ||||||
|  | 		alcMakeContextCurrent(context); | ||||||
|  |  | ||||||
|  | 		deviceCapabilities = ALC.createCapabilities(device); | ||||||
|  | 		alCapabilities = AL.createCapabilities(deviceCapabilities); | ||||||
|  |  | ||||||
|  | 		checkALError(); | ||||||
|  | 		createBuffers(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static void update() { | ||||||
|  | 		// Position of the listener | ||||||
|  | 		Listener.getInstance().update(); | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private static Speaker getLastSpeaker() { | ||||||
|  | 		Speaker speaker; | ||||||
|  | 		do { | ||||||
|  | 			lastSoundIndex++; | ||||||
|  | 			if (lastSoundIndex >= SOUNDS_NUM) { | ||||||
|  | 				lastSoundIndex = 0; | ||||||
|  | 			} | ||||||
|  | 			speaker = soundSpeakers.get(lastSoundIndex); | ||||||
|  | 		} while (speaker.getState() | ||||||
|  | 				.equals(Speaker.State.PLAYING_LOOP)); | ||||||
|  | 		return speaker; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private static SoundType findSoundType(String soundID) throws  Exception { | ||||||
|  | 		for (SoundType s : soundsBuffer) { | ||||||
|  | 			if (s.getId().equals(soundID)) { | ||||||
|  | 				return s; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		throw new Exception("ERROR: The selected sound is not loaded or" + | ||||||
|  | 							" not exists"); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static Speaker initSpeaker(String soundID) { | ||||||
|  | 		Speaker speaker = getLastSpeaker(); | ||||||
|  | 		try { | ||||||
|  | 			findSoundType(soundID).initSpeaker(speaker); | ||||||
|  | 		} catch (Exception ex) | ||||||
|  | 		{ | ||||||
|  | 			throw new RuntimeException(); | ||||||
|  | 		} | ||||||
|  | 		return speaker; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static Speaker initMusicSpeaker(String soundID) { | ||||||
|  | 		try { | ||||||
|  | 			findSoundType(soundID).initSpeaker(musicSpeaker); | ||||||
|  | 		} catch (Exception ex) | ||||||
|  | 		{ | ||||||
|  | 			throw new RuntimeException(); | ||||||
|  | 		} | ||||||
|  | 		return musicSpeaker; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static void checkALError() { | ||||||
|  | 		int errorCode = alGetError(); | ||||||
|  | 		if (alGetError() != AL_NO_ERROR) { | ||||||
|  | 			throw new RuntimeException(String.valueOf(errorCode)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static void loadSound(String path, String namespace, String name, | ||||||
|  | 								 AudioFormat format) { | ||||||
|  | 		if (format == AudioFormat.MONO) { | ||||||
|  | 			soundsBuffer.add(AudioReader.readAsMono(path, namespace, name)); | ||||||
|  | 		} else | ||||||
|  | 		{ | ||||||
|  | 			soundsBuffer.add(AudioReader.readAsStereo(path, namespace, name)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static void closeAL() { | ||||||
|  | 		//clearSounds(); | ||||||
|  | 		//TODO replace alDeleteSources(SOURCES); | ||||||
|  | 		for (Speaker s : soundSpeakers) { | ||||||
|  | 			alDeleteBuffers(s.getAudioData()); | ||||||
|  | 		} | ||||||
|  | 		alcCloseDevice(device); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static ALCapabilities getALCapabilities() { | ||||||
|  | 		return alCapabilities; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static ALCCapabilities getDeviceCapabilities() { | ||||||
|  | 		return deviceCapabilities; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static void createBuffers() | ||||||
|  | 	{ | ||||||
|  | 		for (int i = 0; i < SOUNDS_NUM; ++i) { | ||||||
|  | 			soundSpeakers.add(new Speaker()); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		musicSpeaker = new Speaker(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | } | ||||||
| @@ -1,48 +0,0 @@ | |||||||
| package ru.windcorp.progressia.client.audio; |  | ||||||
|  |  | ||||||
| import org.lwjgl.BufferUtils; |  | ||||||
| import ru.windcorp.progressia.common.resource.*; |  | ||||||
|  |  | ||||||
| import java.nio.IntBuffer; |  | ||||||
| import java.nio.ShortBuffer; |  | ||||||
|  |  | ||||||
| import static org.lwjgl.stb.STBVorbis.*; |  | ||||||
| import static org.lwjgl.openal.AL10.*; |  | ||||||
|  |  | ||||||
| public class AudioReader { |  | ||||||
| 	 |  | ||||||
| 	private AudioReader() {}; |  | ||||||
| 	 |  | ||||||
| 	// TODO fix converting from mono-stereo |  | ||||||
| 	 |  | ||||||
| 	private static SoundType readAsSpecified(String audioName, int format) { |  | ||||||
| 		IntBuffer channelBuffer = BufferUtils.createIntBuffer(1); |  | ||||||
| 		IntBuffer rateBuffer = BufferUtils.createIntBuffer(1); |  | ||||||
| 		 |  | ||||||
| 		Resource res = ResourceManager.getResource(audioName); |  | ||||||
| 		 |  | ||||||
| 		ShortBuffer rawAudio = decodeVorbis(res, channelBuffer, rateBuffer); |  | ||||||
| 		 |  | ||||||
| 		return new SoundType(rawAudio, format, rateBuffer.get(0)); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static SoundType readAsMono(String audioName) { |  | ||||||
| 		return readAsSpecified(audioName, AL_FORMAT_MONO16); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static SoundType readAsStereo(String audioName) { |  | ||||||
| 		return readAsSpecified(audioName, AL_FORMAT_STEREO16); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	private static ShortBuffer decodeVorbis( |  | ||||||
| 		Resource dataToDecode, |  | ||||||
| 		IntBuffer channelsBuffer, |  | ||||||
| 		IntBuffer rateBuffer |  | ||||||
| 	) { |  | ||||||
| 		return stb_vorbis_decode_memory( |  | ||||||
| 			dataToDecode.readAsBytes(), |  | ||||||
| 			channelsBuffer, |  | ||||||
| 			rateBuffer |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										76
									
								
								src/main/java/ru/windcorp/progressia/client/audio/Music.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/main/java/ru/windcorp/progressia/client/audio/Music.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.Speaker; | ||||||
|  | import ru.windcorp.progressia.common.util.Namespaced; | ||||||
|  |  | ||||||
|  | public class Music | ||||||
|  |         extends Namespaced { | ||||||
|  |     private Vec3 position = new Vec3(); | ||||||
|  |     private Vec3 velocity = new Vec3(); | ||||||
|  |     private float pitch = 1.0f; | ||||||
|  |     private float gain = 1.0f; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public Music(String namespace, | ||||||
|  |                        String name) | ||||||
|  |     { | ||||||
|  |         super(namespace, name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Music(String namespace, | ||||||
|  |                        String name, | ||||||
|  |                        Vec3 position, | ||||||
|  |                        Vec3 velocity, | ||||||
|  |                        float pitch, | ||||||
|  |                        float gain) | ||||||
|  |     { | ||||||
|  |         this(namespace, name); | ||||||
|  |         this.position = position; | ||||||
|  |         this.velocity = velocity; | ||||||
|  |         this.pitch = pitch; | ||||||
|  |         this.gain = gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void play(boolean loop) | ||||||
|  |     { | ||||||
|  |         Speaker speaker = AudioManager.initMusicSpeaker(this.getId()); | ||||||
|  |         speaker.setGain(gain); | ||||||
|  |         speaker.setPitch(pitch); | ||||||
|  |         speaker.setPosition(position); | ||||||
|  |         speaker.setVelocity(velocity); | ||||||
|  |  | ||||||
|  |         if (loop) { | ||||||
|  |             speaker.playLoop(); | ||||||
|  |         } else { | ||||||
|  |             speaker.play(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //TODO implement | ||||||
|  |     public void stop() {} | ||||||
|  |  | ||||||
|  |     public void setGain(float gain) { | ||||||
|  |         this.gain = gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setPitch(float pitch) { this.pitch = pitch; } | ||||||
|  |  | ||||||
|  |     public void setVelocity(Vec3 velocity) { | ||||||
|  |         this.velocity = velocity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Vec3 getPosition() { return position; } | ||||||
|  |  | ||||||
|  |     public float getGain() { | ||||||
|  |         return gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Vec3 getVelocity() { | ||||||
|  |         return velocity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public float getPitch() { | ||||||
|  |         return pitch; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,142 +0,0 @@ | |||||||
| package ru.windcorp.progressia.client.audio; |  | ||||||
|  |  | ||||||
| import org.lwjgl.BufferUtils; |  | ||||||
|  |  | ||||||
| import java.nio.FloatBuffer; |  | ||||||
|  |  | ||||||
| import static org.lwjgl.openal.AL11.*; |  | ||||||
|  |  | ||||||
| public class Sound { |  | ||||||
| 	 |  | ||||||
| 	// Buffers |  | ||||||
| 	private int audio; |  | ||||||
| 	private int source; |  | ||||||
| 	 |  | ||||||
| 	// Characteristics |  | ||||||
| 	private FloatBuffer position = (FloatBuffer) BufferUtils.createFloatBuffer( |  | ||||||
| 		3 |  | ||||||
| 	).put(new float[] { |  | ||||||
| 		0.0f, 0.0f, 0.0f |  | ||||||
| 	}).rewind(); |  | ||||||
| 	 |  | ||||||
| 	private FloatBuffer velocity = (FloatBuffer) BufferUtils.createFloatBuffer( |  | ||||||
| 		3 |  | ||||||
| 	).put(new float[] { |  | ||||||
| 		0.0f, 0.0f, 0.0f |  | ||||||
| 	}).rewind(); |  | ||||||
| 	 |  | ||||||
| 	private float pitch = 1.0f; |  | ||||||
| 	private float gain = 1.0f; |  | ||||||
| 	 |  | ||||||
| 	public Sound() {} |  | ||||||
| 	 |  | ||||||
| 	public Sound(int audio, int source) { |  | ||||||
| 		setAudio(audio); |  | ||||||
| 		setSource(source); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Sound( |  | ||||||
| 		int audio, |  | ||||||
| 		FloatBuffer position, |  | ||||||
| 		FloatBuffer velocity, |  | ||||||
| 		float pitch, |  | ||||||
| 		float gain |  | ||||||
| 	) { |  | ||||||
| 		setAudio(audio); |  | ||||||
| 		setPosition(position); |  | ||||||
| 		setVelocity(velocity); |  | ||||||
| 		setPitch(pitch); |  | ||||||
| 		setGain(gain); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Sound( |  | ||||||
| 		FloatBuffer position, |  | ||||||
| 		FloatBuffer velocity, |  | ||||||
| 		float pitch, |  | ||||||
| 		float gain |  | ||||||
| 	) { |  | ||||||
| 		setPosition(position); |  | ||||||
| 		setVelocity(velocity); |  | ||||||
| 		setPitch(pitch); |  | ||||||
| 		setGain(gain); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void playOnce() { |  | ||||||
| 		alSourcePlay(source); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void playLoop() { |  | ||||||
| 		alSourcei(source, AL_LOOPING, AL_TRUE); |  | ||||||
| 		playOnce(); |  | ||||||
| 		alSourcei(source, AL_LOOPING, AL_FALSE); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void stop() { |  | ||||||
| 		alSourceStop(source); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void pause() { |  | ||||||
| 		alSourcePause(source); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public boolean isPlaying() { |  | ||||||
| 		final int state = alGetSourcei(source, AL_SOURCE_STATE); |  | ||||||
| 		return state == AL_PLAYING; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public int getAudio() { |  | ||||||
| 		return audio; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setAudio(int audio) { |  | ||||||
| 		this.audio = audio; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setSource(int source) { |  | ||||||
| 		this.source = source; |  | ||||||
| 		alSourcei(this.source, AL_BUFFER, audio); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public int getSource() { |  | ||||||
| 		return source; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	// OTHER |  | ||||||
| 	 |  | ||||||
| 	public void setPosition(FloatBuffer position) { |  | ||||||
| 		this.position = position; |  | ||||||
| 		alSourcefv(source, AL_POSITION, position); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public FloatBuffer getPosition() { |  | ||||||
| 		return position; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setVelocity(FloatBuffer velocity) { |  | ||||||
| 		alSourcefv(source, AL_VELOCITY, velocity); |  | ||||||
| 		this.velocity = velocity; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public FloatBuffer getVelocity() { |  | ||||||
| 		return velocity; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setPitch(float pitch) { |  | ||||||
| 		alSourcef(source, AL_PITCH, pitch); |  | ||||||
| 		this.pitch = pitch; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public float getPitch() { |  | ||||||
| 		return pitch; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setGain(float gain) { |  | ||||||
| 		alSourcef(source, AL_GAIN, gain); |  | ||||||
| 		this.gain = gain; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public float getGain() { |  | ||||||
| 		return gain; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,83 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.Speaker; | ||||||
|  | import ru.windcorp.progressia.common.util.Namespaced; | ||||||
|  |  | ||||||
|  | public class SoundEffect | ||||||
|  |         extends Namespaced { | ||||||
|  |  | ||||||
|  |     private Vec3 position = new Vec3(); | ||||||
|  |     private Vec3 velocity = new Vec3(); | ||||||
|  |     private float pitch = 1.0f; | ||||||
|  |     private float gain = 1.0f; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public SoundEffect(String namespace, | ||||||
|  |                        String name) | ||||||
|  |     { | ||||||
|  |         super(namespace, name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public SoundEffect(String namespace, | ||||||
|  |                        String name, | ||||||
|  |                        Vec3 position, | ||||||
|  |                        Vec3 velocity, | ||||||
|  |                        float pitch, | ||||||
|  |                        float gain) | ||||||
|  |     { | ||||||
|  |         this(namespace, name); | ||||||
|  |         this.position = position; | ||||||
|  |         this.velocity = velocity; | ||||||
|  |         this.pitch = pitch; | ||||||
|  |         this.gain = gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void play(boolean loop) | ||||||
|  |     { | ||||||
|  |         Speaker speaker = AudioManager.initSpeaker(this.getId()); | ||||||
|  |         speaker.setGain(gain); | ||||||
|  |         speaker.setPitch(pitch); | ||||||
|  |         speaker.setPosition(position); | ||||||
|  |         speaker.setVelocity(velocity); | ||||||
|  |  | ||||||
|  |         if (loop) { | ||||||
|  |             speaker.playLoop(); | ||||||
|  |         } else { | ||||||
|  |             speaker.play(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //TODO implement | ||||||
|  |     public void stop() {} | ||||||
|  |  | ||||||
|  |     public void setGain(float gain) { | ||||||
|  |         this.gain = gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setPitch(float pitch) { this.pitch = pitch; } | ||||||
|  |  | ||||||
|  |     public void setPosition(Vec3 position) { | ||||||
|  |         this.position = position; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setVelocity(Vec3 velocity) { | ||||||
|  |         this.velocity = velocity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Vec3 getPosition() { | ||||||
|  |         return position; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public float getGain() { | ||||||
|  |         return gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Vec3 getVelocity() { | ||||||
|  |         return velocity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public float getPitch() { | ||||||
|  |         return pitch; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,107 +0,0 @@ | |||||||
| package ru.windcorp.progressia.client.audio; |  | ||||||
|  |  | ||||||
| import org.lwjgl.openal.AL; |  | ||||||
| import org.lwjgl.openal.ALC; |  | ||||||
| import org.lwjgl.openal.ALCCapabilities; |  | ||||||
| import org.lwjgl.openal.ALCapabilities; |  | ||||||
|  |  | ||||||
| import static org.lwjgl.openal.AL11.*; |  | ||||||
| import static org.lwjgl.openal.ALC10.*; |  | ||||||
|  |  | ||||||
| import java.util.concurrent.ArrayBlockingQueue; |  | ||||||
|  |  | ||||||
| public class SoundManager { |  | ||||||
| 	 |  | ||||||
| 	private static final int SOURCES_NUM = 64; |  | ||||||
| 	private static int lastSourceIndex = -1; |  | ||||||
| 	private static final int[] SOURCES = new int[SOURCES_NUM]; |  | ||||||
| 	 |  | ||||||
| 	private static final ArrayBlockingQueue<Sound> SOUNDS = |  | ||||||
| 		new ArrayBlockingQueue<>(SOURCES_NUM); |  | ||||||
| 	 |  | ||||||
| 	private static long device; |  | ||||||
| 	 |  | ||||||
| 	private static ALCCapabilities deviceCapabilities; |  | ||||||
| 	private static ALCapabilities alCapabilities; |  | ||||||
| 	 |  | ||||||
| 	public static void initAL() { |  | ||||||
| 		String defaultDeviceName = alcGetString( |  | ||||||
| 			0, |  | ||||||
| 			ALC_DEFAULT_DEVICE_SPECIFIER |  | ||||||
| 		); |  | ||||||
| 		 |  | ||||||
| 		device = alcOpenDevice(defaultDeviceName); |  | ||||||
| 		 |  | ||||||
| 		int[] attributes = new int[1]; |  | ||||||
| 		long context = alcCreateContext(device, attributes); |  | ||||||
| 		alcMakeContextCurrent(context); |  | ||||||
| 		 |  | ||||||
| 		deviceCapabilities = ALC.createCapabilities(device); |  | ||||||
| 		alCapabilities = AL.createCapabilities(deviceCapabilities); |  | ||||||
| 		 |  | ||||||
| 		checkALError(); |  | ||||||
| 		 |  | ||||||
| 		alGenSources(SOURCES); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static void update() { |  | ||||||
| 		// Position of the listener |  | ||||||
| 		Listener.getInstance().update(); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	private static void addSound(Sound sound) { |  | ||||||
| 		if (!SOUNDS.offer(sound)) { |  | ||||||
| 			Sound polled = SOUNDS.poll(); |  | ||||||
| 			assert polled != null; |  | ||||||
| 			polled.stop(); |  | ||||||
| 			if (!SOUNDS.offer(sound)) { |  | ||||||
| 				throw new RuntimeException(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	private static int getNextSource() { |  | ||||||
| 		if (++lastSourceIndex > SOURCES_NUM) |  | ||||||
| 			lastSourceIndex = 0; |  | ||||||
| 		return SOURCES[lastSourceIndex]; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static Sound createSound(SoundType soundType) { |  | ||||||
| 		Sound sound = soundType.genSoundSource(getNextSource()); |  | ||||||
| 		addSound(sound); |  | ||||||
| 		return sound; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static void clearSounds() { |  | ||||||
| 		Sound polled = SOUNDS.poll(); |  | ||||||
| 		while (polled != null) { |  | ||||||
| 			polled.stop(); |  | ||||||
| 			polled = SOUNDS.poll(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static void checkALError() { |  | ||||||
| 		int errorCode = alGetError(); |  | ||||||
| 		if (alGetError() != AL_NO_ERROR) { |  | ||||||
| 			throw new RuntimeException(String.valueOf(errorCode)); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static void closeAL() { |  | ||||||
| 		clearSounds(); |  | ||||||
| 		alDeleteSources(SOURCES); |  | ||||||
| 		for (Sound s : SOUNDS) { |  | ||||||
| 			alDeleteBuffers(s.getAudio()); |  | ||||||
| 		} |  | ||||||
| 		alcCloseDevice(device); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static ALCapabilities getALCapabilities() { |  | ||||||
| 		return alCapabilities; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static ALCCapabilities getDeviceCapabilities() { |  | ||||||
| 		return deviceCapabilities; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| } |  | ||||||
| @@ -1,51 +0,0 @@ | |||||||
| package ru.windcorp.progressia.client.audio; |  | ||||||
|  |  | ||||||
| import java.nio.ShortBuffer; |  | ||||||
| import static org.lwjgl.openal.AL11.*; |  | ||||||
|  |  | ||||||
| public class SoundType { |  | ||||||
| 	 |  | ||||||
| 	private ShortBuffer rawAudio; |  | ||||||
| 	private int sampleRate; |  | ||||||
| 	private int format; |  | ||||||
| 	 |  | ||||||
| 	public SoundType(ShortBuffer rawAudio, int format, int sampleRate) { |  | ||||||
| 		this.rawAudio = rawAudio; |  | ||||||
| 		this.sampleRate = sampleRate; |  | ||||||
| 		this.format = format; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static int genEmptyAudio() { |  | ||||||
| 		return alGenBuffers(); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static int genEmptySource() { |  | ||||||
| 		return alGenSources(); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public int genAudio() { |  | ||||||
| 		int audio = alGenBuffers(); |  | ||||||
| 		alBufferData(audio, format, rawAudio, sampleRate); |  | ||||||
| 		return audio; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Sound genSoundSource() { |  | ||||||
| 		return new Sound(genAudio(), alGenSources()); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Sound genSoundSource(int source) { |  | ||||||
| 		if (!alIsSource(source)) |  | ||||||
| 			throw new RuntimeException(); |  | ||||||
| 		 |  | ||||||
| 		return new Sound(genAudio(), source); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Sound genSoundSource(int source, int audio) { |  | ||||||
| 		if (!alIsBuffer(audio) || !alIsSource(source)) |  | ||||||
| 			throw new RuntimeException(); |  | ||||||
| 		 |  | ||||||
| 		alBufferData(audio, format, rawAudio, sampleRate); |  | ||||||
| 		return new Sound(audio, alGenSources()); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| package ru.windcorp.progressia.client.audio.backend; |  | ||||||
|  |  | ||||||
| import ru.windcorp.progressia.client.audio.AudioReader; |  | ||||||
| import ru.windcorp.progressia.client.audio.Sound; |  | ||||||
| import ru.windcorp.progressia.client.audio.SoundManager; |  | ||||||
|  |  | ||||||
| public class ALTest { |  | ||||||
| 	static private void initializeAL() { |  | ||||||
| 		SoundManager.initAL(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static void loadALData() { |  | ||||||
| 		Sound music = SoundManager.createSound(AudioReader.readAsMono("assets/sounds/sample_mono.ogg")); |  | ||||||
| 		music.playOnce(); |  | ||||||
| 		/*music = SoundManager.createSound(AudioReader.readAsStereo("assets/sounds/sample_mono.ogg")); |  | ||||||
| 		music.playOnce();*/ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static void killALData() { |  | ||||||
| 		//TODO implement the method or its analogue |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public static void execute() { |  | ||||||
| 		initializeAL(); |  | ||||||
| 		loadALData(); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,54 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio.backend; | ||||||
|  |  | ||||||
|  | import org.lwjgl.BufferUtils; | ||||||
|  | import ru.windcorp.progressia.client.audio.backend.SoundType; | ||||||
|  | import ru.windcorp.progressia.common.resource.*; | ||||||
|  |  | ||||||
|  | import java.nio.IntBuffer; | ||||||
|  | import java.nio.ShortBuffer; | ||||||
|  |  | ||||||
|  | import static org.lwjgl.stb.STBVorbis.*; | ||||||
|  | import static org.lwjgl.openal.AL10.*; | ||||||
|  |  | ||||||
|  | public class AudioReader { | ||||||
|  | 	 | ||||||
|  | 	private AudioReader() {} | ||||||
|  | 	 | ||||||
|  | 	// TODO fix converting from mono-stereo | ||||||
|  | 	// TODO change audio naming from full path to just name | ||||||
|  | 	private static SoundType readAsSpecified(String path, String audioNamespace, | ||||||
|  | 											 String audioName, int format) { | ||||||
|  | 		IntBuffer channelBuffer = BufferUtils.createIntBuffer(1); | ||||||
|  | 		IntBuffer rateBuffer = BufferUtils.createIntBuffer(1); | ||||||
|  | 		 | ||||||
|  | 		Resource res = ResourceManager.getResource(path); | ||||||
|  | 		 | ||||||
|  | 		ShortBuffer rawAudio = decodeVorbis(res, channelBuffer, rateBuffer); | ||||||
|  | 		 | ||||||
|  | 		return new SoundType(audioNamespace ,audioName, rawAudio, format, | ||||||
|  | 				rateBuffer.get(0)); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static SoundType readAsMono(String path, String audioNamespace, | ||||||
|  | 									   String audioName) { | ||||||
|  | 		return readAsSpecified(path, audioNamespace, | ||||||
|  | 				audioName, AL_FORMAT_MONO16); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static SoundType readAsStereo(String path,String audioNamespace, | ||||||
|  | 										 String audioName) { | ||||||
|  | 		return readAsSpecified(path, audioNamespace, audioName, AL_FORMAT_STEREO16); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private static ShortBuffer decodeVorbis( | ||||||
|  | 		Resource dataToDecode, | ||||||
|  | 		IntBuffer channelsBuffer, | ||||||
|  | 		IntBuffer rateBuffer | ||||||
|  | 	) { | ||||||
|  | 		return stb_vorbis_decode_memory( | ||||||
|  | 			dataToDecode.readAsBytes(), | ||||||
|  | 			channelsBuffer, | ||||||
|  | 			rateBuffer | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package ru.windcorp.progressia.client.audio; | package ru.windcorp.progressia.client.audio.backend; | ||||||
| 
 | 
 | ||||||
| import glm.vec._3.Vec3; | import glm.vec._3.Vec3; | ||||||
| import ru.windcorp.progressia.client.Client; | import ru.windcorp.progressia.client.Client; | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio.backend; | ||||||
|  |  | ||||||
|  | import ru.windcorp.progressia.common.util.Namespaced; | ||||||
|  |  | ||||||
|  | import java.nio.ShortBuffer; | ||||||
|  | import static org.lwjgl.openal.AL11.*; | ||||||
|  |  | ||||||
|  | public class SoundType extends Namespaced { | ||||||
|  | 	 | ||||||
|  | 	private ShortBuffer rawAudio; | ||||||
|  | 	private int sampleRate; | ||||||
|  | 	private int format; | ||||||
|  | 	private int audioBuffer; | ||||||
|  | 	 | ||||||
|  | 	public SoundType(String namespace, String name, ShortBuffer rawAudio, | ||||||
|  | 					 int format, int sampleRate) { | ||||||
|  | 		super(namespace, name); | ||||||
|  | 		this.rawAudio = rawAudio; | ||||||
|  | 		this.sampleRate = sampleRate; | ||||||
|  | 		this.format = format; | ||||||
|  | 		createAudioBuffer(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private void createAudioBuffer() { | ||||||
|  | 		this.audioBuffer = alGenBuffers(); | ||||||
|  | 		alBufferData(audioBuffer, format, rawAudio, sampleRate); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	//TODO What is this (Eugene Smirnov) | ||||||
|  | 	private Speaker createSound(int source, int audio) { | ||||||
|  | 		if (!alIsBuffer(audio) || !alIsSource(source)) | ||||||
|  | 			throw new RuntimeException(); | ||||||
|  | 		 | ||||||
|  | 		alBufferData(audio, format, rawAudio, sampleRate); | ||||||
|  | 		return new Speaker(audio); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public void initSpeaker(Speaker speaker) { | ||||||
|  | 		speaker.setAudioData(audioBuffer); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -0,0 +1,147 @@ | |||||||
|  | package ru.windcorp.progressia.client.audio.backend; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import static org.lwjgl.openal.AL11.*; | ||||||
|  |  | ||||||
|  | public class Speaker { | ||||||
|  |  | ||||||
|  |     public enum State { | ||||||
|  |         NOT_PLAYING, | ||||||
|  |         PLAYING, | ||||||
|  |         PLAYING_LOOP | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Buffers | ||||||
|  |     private int audioData; | ||||||
|  |     private int sourceData; | ||||||
|  |  | ||||||
|  |     // Characteristics | ||||||
|  |     private Vec3 position = new Vec3(); | ||||||
|  |     private Vec3 velocity = new Vec3(); | ||||||
|  |     private float pitch = 1.0f; | ||||||
|  |     private float gain = 1.0f; | ||||||
|  |     private State state = State.NOT_PLAYING; | ||||||
|  |  | ||||||
|  |     public Speaker() { | ||||||
|  |         sourceData = alGenSources(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Speaker(int audioData) { | ||||||
|  |         this(); | ||||||
|  |         setAudioData(audioData); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Speaker( | ||||||
|  |             int audioData, | ||||||
|  |             Vec3 position, | ||||||
|  |             Vec3 velocity, | ||||||
|  |             float pitch, | ||||||
|  |             float gain | ||||||
|  |     ) { | ||||||
|  |         setAudioData(audioData); | ||||||
|  |         setPosition(position); | ||||||
|  |         setVelocity(velocity); | ||||||
|  |         setPitch(pitch); | ||||||
|  |         setGain(gain); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Speaker( | ||||||
|  |             Vec3 position, | ||||||
|  |             Vec3 velocity, | ||||||
|  |             float pitch, | ||||||
|  |             float gain | ||||||
|  |     ) { | ||||||
|  |         setPosition(position); | ||||||
|  |         setVelocity(velocity); | ||||||
|  |         setPitch(pitch); | ||||||
|  |         setGain(gain); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void play() { | ||||||
|  |         alSourcePlay(sourceData); | ||||||
|  |         state = State.PLAYING; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void playLoop() { | ||||||
|  |         alSourcei(sourceData, AL_LOOPING, AL_TRUE); | ||||||
|  |         alSourcePlay(sourceData); | ||||||
|  |         state = State.PLAYING_LOOP; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void stop() { | ||||||
|  |         alSourceStop(sourceData); | ||||||
|  |         if (state == State.PLAYING_LOOP) { | ||||||
|  |             alSourcei(sourceData, AL_LOOPING, AL_FALSE); | ||||||
|  |         } | ||||||
|  |         state = State.NOT_PLAYING; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void pause() { | ||||||
|  |         alSourcePause(sourceData); | ||||||
|  |         state = State.NOT_PLAYING; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean isPlaying() { | ||||||
|  |         final int speakerState = alGetSourcei(sourceData, AL_SOURCE_STATE); | ||||||
|  |         if (speakerState == AL_PLAYING) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             state = State.NOT_PLAYING; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // GETTERS & SETTERS | ||||||
|  |  | ||||||
|  |     public int getAudioData() { | ||||||
|  |         return audioData; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setAudioData(int audioData) { | ||||||
|  |         this.audioData = audioData; | ||||||
|  |         alSourcei(this.sourceData, AL_BUFFER, audioData); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setPosition(Vec3 position) { | ||||||
|  |         this.position = position; | ||||||
|  |         alSource3f(sourceData, AL_POSITION, position.x, position.y, position.z); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Vec3 getPosition() { | ||||||
|  |         return position; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setVelocity(Vec3 velocity) { | ||||||
|  |         alSource3f(sourceData, AL_VELOCITY, velocity.x, velocity.y, velocity.z); | ||||||
|  |         this.velocity = velocity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Vec3 getVelocity() { | ||||||
|  |         return velocity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setPitch(float pitch) { | ||||||
|  |         alSourcef(sourceData, AL_PITCH, pitch); | ||||||
|  |         this.pitch = pitch; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public float getPitch() { | ||||||
|  |         return pitch; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setGain(float gain) { | ||||||
|  |         alSourcef(sourceData, AL_GAIN, gain); | ||||||
|  |         this.gain = gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public float getGain() { | ||||||
|  |         return gain; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public State getState() | ||||||
|  |     { | ||||||
|  |         return state; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -20,7 +20,7 @@ package ru.windcorp.progressia.client.graphics.backend; | |||||||
| import static org.lwjgl.glfw.GLFW.*; | import static org.lwjgl.glfw.GLFW.*; | ||||||
| import static org.lwjgl.opengl.GL11.*; | import static org.lwjgl.opengl.GL11.*; | ||||||
|  |  | ||||||
| import ru.windcorp.progressia.client.audio.SoundManager; | import ru.windcorp.progressia.client.audio.AudioManager; | ||||||
| import ru.windcorp.progressia.client.graphics.GUI; | import ru.windcorp.progressia.client.graphics.GUI; | ||||||
|  |  | ||||||
| class RenderThread extends Thread { | class RenderThread extends Thread { | ||||||
| @@ -61,7 +61,7 @@ class RenderThread extends Thread { | |||||||
|  |  | ||||||
| 	private void doRender() { | 	private void doRender() { | ||||||
| 		GUI.render(); | 		GUI.render(); | ||||||
| 		SoundManager.update(); | 		AudioManager.update(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private void waitForFrame() { | 	private void waitForFrame() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user