Merge pull request #1 from OLEGSHA/audio

This commit is contained in:
OLEGSHA 2020-11-22 17:25:46 +03:00 committed by GitHub
commit e3315f62a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 656 additions and 6 deletions

View File

@ -17,17 +17,13 @@
*******************************************************************************/ *******************************************************************************/
package ru.windcorp.progressia.client; package ru.windcorp.progressia.client;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ru.windcorp.progressia.ProgressiaLauncher; import ru.windcorp.progressia.ProgressiaLauncher;
import ru.windcorp.progressia.test.ALTest;
public class ProgressiaClientMain { public class ProgressiaClientMain {
private static final Logger logger = LogManager.getLogger(ProgressiaClientMain.class.getName());
public static void main(String[] args) { public static void main(String[] args) {
logger.info("App started!"); ALTest.execute();
ProgressiaLauncher.launch(args, new ClientProxy()); ProgressiaLauncher.launch(args, new ClientProxy());
} }

View File

@ -0,0 +1,5 @@
package ru.windcorp.progressia.client.audio;
public enum AudioFormat {
MONO, STEREO
}

View File

@ -0,0 +1,143 @@
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 id, AudioFormat format) {
if (format == AudioFormat.MONO) {
soundsBuffer.add(AudioReader.readAsMono(path, id));
} else
{
soundsBuffer.add(AudioReader.readAsStereo(path, id));
}
}
public static void closeAL() {
for (Speaker s : soundSpeakers) {
alDeleteBuffers(s.getAudioData());
alDeleteBuffers(s.getSourceData());
}
alDeleteBuffers(musicSpeaker.getAudioData());
alDeleteBuffers(musicSpeaker.getSourceData());
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();
}
}

View File

@ -0,0 +1,70 @@
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.namespaces.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 id)
{
super(id);
}
public Music(String id,
Vec3 position,
Vec3 velocity,
float pitch,
float gain)
{
this(id);
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();
}
}
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;
}
}

View File

@ -0,0 +1,78 @@
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.namespaces.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 id)
{
super(id);
}
public SoundEffect(String id,
Vec3 position,
Vec3 velocity,
float pitch,
float gain)
{
this(id);
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();
}
}
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;
}
}

View File

@ -0,0 +1,49 @@
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
private static SoundType readAsSpecified(String path, String id, 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(id, rawAudio, format,
rateBuffer.get(0));
}
public static SoundType readAsMono(String path, String id) {
return readAsSpecified(path, id, AL_FORMAT_MONO16);
}
public static SoundType readAsStereo(String path,String id) {
return readAsSpecified(path, id, AL_FORMAT_STEREO16);
}
private static ShortBuffer decodeVorbis(
Resource dataToDecode,
IntBuffer channelsBuffer,
IntBuffer rateBuffer
) {
return stb_vorbis_decode_memory(
dataToDecode.readAsBytes(),
channelsBuffer,
rateBuffer
);
}
}

View File

@ -0,0 +1,80 @@
package ru.windcorp.progressia.client.audio.backend;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.world.Camera;
import static org.lwjgl.openal.AL10.*;
public class Listener {
private static final Listener INSTANCE = new Listener();
private Listener() {}
public static Listener getInstance() {
return INSTANCE;
}
// Params
private final Vec3 position = new Vec3();
private final Vec3 velocity = new Vec3();
private final Vec3 oriAt = new Vec3();
private final Vec3 oriUp = new Vec3();
private boolean isInWorld = false;
public void update() {
Client client = ClientState.getInstance();
Camera camera = client == null ? null : client.getCamera();
boolean wasInWorld = isInWorld;
isInWorld = client != null && camera.getAnchor() != null;
if (isInWorld) {
if (wasInWorld) {
velocity.set(camera.getLastAnchorPosition()).sub(position).div(
(float) GraphicsInterface.getFrameLength()
);
} else {
// If !wasInWorld, previous position is nonsence. Assume 0.
velocity.set(0);
}
position.set(camera.getLastAnchorPosition());
oriAt.set(camera.getLastAnchorLookingAt());
oriUp.set(camera.getLastAnchorUp());
} else if (wasInWorld) { // Do not reset if we weren't in world
resetParams();
}
/*
* Only apply if there is a chance that params changed.
* This can only happen if we are in world now (isInWorld) or we just
* left world (wasInWorld, then we need to reset).
*/
if (isInWorld || wasInWorld) {
applyParams();
}
}
private void resetParams() {
position.set(0);
velocity.set(0);
oriAt.set(0);
oriUp.set(0);
}
private void applyParams() {
alListener3f(AL_POSITION, position.x, position.y, position.z);
alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z);
alListenerfv(AL_ORIENTATION, new float[] {
oriAt.x, oriAt.y, oriAt.z, oriUp.x, oriUp.y, oriUp.z
});
}
}

View File

@ -0,0 +1,41 @@
package ru.windcorp.progressia.client.audio.backend;
import ru.windcorp.progressia.common.util.namespaces.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 id, ShortBuffer rawAudio,
int format, int sampleRate) {
super(id);
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);
}
}

View File

@ -0,0 +1,151 @@
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 int getSourceData() {
return sourceData;
}
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;
}
}

View File

@ -20,6 +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.AudioManager;
import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.GUI;
class RenderThread extends Thread { class RenderThread extends Thread {
@ -60,6 +61,7 @@ class RenderThread extends Thread {
private void doRender() { private void doRender() {
GUI.render(); GUI.render();
AudioManager.update();
} }
private void waitForFrame() { private void waitForFrame() {

View File

@ -0,0 +1,30 @@
package ru.windcorp.progressia.test;
import ru.windcorp.progressia.client.audio.AudioFormat;
import ru.windcorp.progressia.client.audio.AudioManager;
import ru.windcorp.progressia.client.audio.Music;
public class ALTest {
static private void initializeAL() {
AudioManager.initAL();
}
static void loadALData() {
AudioManager.loadSound("assets/sounds/sample_stereo.ogg",
"Progressia:SampleStereo",
AudioFormat.STEREO);
AudioManager.loadSound("assets/sounds/block_destroy_clap.ogg",
"Progressia:BlockDestroy",
AudioFormat.MONO);
Music music = new Music("Progressia:SampleStereo");
music.setGain(0.5f);
//music.play(false);
}
public static void execute() {
initializeAL();
Thread shutdownHook = new Thread(AudioManager::closeAL, "AL Shutdown Hook");
Runtime.getRuntime().addShutdownHook(shutdownHook);
loadALData();
}
}

View File

@ -9,6 +9,7 @@ import org.lwjgl.glfw.GLFW;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.audio.SoundEffect;
import ru.windcorp.progressia.client.comms.controls.*; import ru.windcorp.progressia.client.comms.controls.*;
import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher; import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
@ -213,6 +214,10 @@ public class TestContent {
private static void onBlockBreakTrigger(ControlData control) { private static void onBlockBreakTrigger(ControlData control) {
((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock()); ((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
SoundEffect sfx = new SoundEffect("Progressia:BlockDestroy");
sfx.setPosition(getSelection().getPoint());
sfx.setPitch((float) (Math.random() + 1 * 0.5));
sfx.play(false);
} }
private static void onBlockBreakReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) { private static void onBlockBreakReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {

Binary file not shown.

Binary file not shown.