initial commit

This commit is contained in:
Sen 2025-03-11 00:23:54 +01:00 committed by Sen
parent 3c9ee26b06
commit 22186c33b9
1458 changed files with 282792 additions and 0 deletions

View file

@ -0,0 +1,267 @@
package game.audio;
import java.util.Arrays;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import game.Log;
import game.collect.Lists;
public class AudioInterface implements Runnable {
private static class Channel {
short[] buffer;
int pos;
int level;
int type;
boolean loop;
boolean run;
boolean load;
}
private static class Command {
Object buffer;
Cmd command;
short address;
int param;
}
private static enum Cmd {
STOP,
QUERY,
VOLUME,
PLAY,
BREAK,
LEVEL
};
private final int blocksize;
private final int devbufsize;
private final byte[] buffer;
private final Thread thread;
private final List<Command> cmds = Lists.newArrayList();
private final short[] volumes = new short[Volume.values().length];
// private final int[] mix = new int[Volume.values().length];
// private final int[] attn = new int[Volume.values().length];
private final Channel[] channels = new Channel[32];
private SourceDataLine line;
private int smppos;
private long peak = 32767L;
private boolean opened;
private boolean waiting;
private void push(short data) {
for(int z = 0; z < 2 * 2; z++) {
this.buffer[this.smppos * 2 * 2 + z] = (byte)((data >> ((z & 1) * 8)) & 0xff);
}
this.smppos++;
if(this.smppos == this.blocksize) {
this.write(this.buffer, this.blocksize);
this.smppos = 0;
}
}
private void write(byte[] data, int samples) {
if(this.line == null)
return;
int rc;
int avail;
int channels = 2;
int offset = 0;
samples *= 2 * 2;
int block = this.blocksize * 2 * 2;
while(samples > 0) {
block = samples < block ? samples : block;
avail = this.line.available();
rc = this.line.write(data, offset, avail < block ? avail : block);
// this.line.drain();
if(rc > 0) {
offset += rc;
samples -= rc;
}
}
}
private short generate() {
long mix = 0;
long attn = 0; // (32767 * 100) / 95);
for(int z = 0; z < this.channels.length; z++) {
Channel ch = this.channels[z];
if(ch.run) {
attn += (long)ch.level * (long)this.volumes[ch.type]; // (this.attn[ch.type] * 95) / 100;
mix += (long)ch.buffer[ch.pos] * (long)ch.level * (((long)this.volumes[ch.type] * (long)this.volumes[0]) / 32767);
if(++ch.pos >= ch.buffer.length) {
ch.pos = 0;
if(!ch.loop)
ch.run = ch.load = false;
}
}
}
// for(int z = 1; z < this.volumes.length; z++) {
// if(this.attn[z] > 0)
// this.mix[0] += (short)(int)(((this.mix[z] * 32767 / this.attn[z]) * (int)this.volumes[z]) / 32767);
// }
mix /= 32767L * 32767L;
long amp = Math.abs(mix);
if(amp > 32767L)
this.peak = Math.max(this.peak, amp);
else if(this.peak > 32767L)
this.peak = Math.max(this.peak - 3L, 32767L);
return (short)((mix * 32767L) / this.peak);
// return attn == 0L ? 0 : (short)(mix / Math.min(attn, 32767L * 32767L * 2L));
}
private void dispatch(Object buf, Cmd op, short addr, int param) {
switch(op) {
case VOLUME:
this.volumes[addr] = (short)param;
break;
case PLAY:
if(buf != null) {
this.channels[addr].buffer = (short[])buf;
this.channels[addr].pos = 0;
this.channels[addr].type = param & 0xff;
this.channels[addr].run = this.channels[addr].load = true;
this.channels[addr].loop = (param & 0x100) != 0;
}
break;
case BREAK:
this.channels[addr].run = this.channels[addr].load = false;
break;
case LEVEL:
this.channels[addr].level = param;
break;
case QUERY:
this.waiting = false;
break;
}
}
public void run() {
AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, 48000, 16, 2, 2 * 2, 48000, false);
Info info = new Info(SourceDataLine.class, format, this.devbufsize);
try {
this.line = (SourceDataLine)AudioSystem.getLine(info);
this.line.open(format, this.devbufsize);
}
catch(LineUnavailableException e) {
this.line = null;
Log.SOUND.error(e, "Konnte Audiogerät nicht öffnen");
}
if(this.line != null)
this.line.start();
this.opened = this.line != null;
long sampletime = 1000000000L / 48000L; // * (long)this.blocksize;
boolean running = true;
long elapsed = 0;
while(running) {
synchronized(this.cmds) {
while(!this.cmds.isEmpty()) {
Command cmd = this.cmds.remove(0);
if(cmd.command == Cmd.STOP)
running = false;
else
this.dispatch(cmd.buffer, cmd.command, cmd.address, cmd.param);
}
}
elapsed += 1000L;
while((elapsed >= sampletime)) {
this.push(this.generate());
elapsed -= sampletime;
}
}
if(this.smppos != 0)
this.write(this.buffer, this.smppos);
if(this.line != null) {
this.line.drain();
this.line.stop();
this.line.close();
}
this.line = null;
this.waiting = false;
}
private void instruct(Object buf, Cmd op, int addr, int param) {
if(this.thread == null)
return;
Command cmd = new Command();
cmd.command = op;
cmd.address = (short)addr;
cmd.param = param;
cmd.buffer = buf;
synchronized(this.cmds) {
this.cmds.add(cmd);
}
}
public AudioInterface(int frames, int bufsize, boolean disabled) {
this.blocksize = frames;
this.devbufsize = bufsize;
this.buffer = new byte[this.blocksize * 2 * 2];
this.thread = disabled ? null : new Thread(this, "Audio Thread");
for(int z = 0; z < this.channels.length; z++) {
this.channels[z] = new Channel();
}
Arrays.fill(this.volumes, (short)32767);
}
public boolean start() {
if(this.thread == null)
return false;
this.thread.start();
this.waiting = true;
this.instruct(null, Cmd.QUERY, 0, 0);
while(this.waiting) {
try {
Thread.sleep(1L);
}
catch(InterruptedException e) {
}
}
return this.opened;
}
public boolean end() {
if(this.thread == null)
return false;
this.waiting = true;
this.instruct(null, Cmd.STOP, 0, 0);
while(this.waiting) {
try {
Thread.sleep(1L);
}
catch(InterruptedException e) {
}
}
return this.opened;
}
public void alSetVolume(Volume channel, short volume) {
this.instruct(null, Cmd.VOLUME, channel.ordinal(), volume);
}
public void alSourcePlay(int source, short[] buffer, Volume channel, boolean loop) {
this.channels[source].load = true;
this.instruct(buffer, Cmd.PLAY, source, channel.ordinal() | (loop ? 0x100 : 0));
}
public void alSourceStop(int source) {
this.channels[source].load = false;
this.instruct(null, Cmd.BREAK, source, 0);
}
public boolean alPlaying(int source) {
return this.channels[source].load;
}
public void alGain(int source, float value) {
this.instruct(null, Cmd.LEVEL, source, (int)(value * 32767.0f));
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
package game.audio;
import game.init.SoundEvent;
public abstract class MovingSound extends Sound
{
protected boolean donePlaying = false;
protected MovingSound(SoundEvent event)
{
super(event);
}
public boolean isDonePlaying()
{
return this.donePlaying;
}
public abstract void update();
}

View file

@ -0,0 +1,48 @@
package game.audio;
import game.ExtMath;
import game.entity.item.EntityCart;
import game.init.SoundEvent;
public class MovingSoundMinecart extends MovingSound
{
private final EntityCart minecart;
private float distance = 0.0F;
public MovingSoundMinecart(EntityCart minecartIn)
{
super(SoundEvent.CART);
this.minecart = minecartIn;
this.repeat = true;
// this.repeatDelay = 0;
}
/**
* Like the old updateEntity(), except more generic.
*/
public void update()
{
if (this.minecart.dead)
{
this.donePlaying = true;
}
else
{
this.xPosF = (float)this.minecart.posX;
this.yPosF = (float)this.minecart.posY;
this.zPosF = (float)this.minecart.posZ;
float f = ExtMath.sqrtd(this.minecart.motionX * this.minecart.motionX + this.minecart.motionZ * this.minecart.motionZ);
if ((double)f >= 0.01D)
{
this.distance = ExtMath.clampf(this.distance + 0.0025F, 0.0F, 1.0F);
this.volume = 0.0F + ExtMath.clampf(f, 0.0F, 0.5F) * 0.7F;
}
else
{
this.distance = 0.0F;
this.volume = 0.0F;
}
}
}
}

View file

@ -0,0 +1,46 @@
package game.audio;
import game.ExtMath;
import game.entity.item.EntityCart;
import game.entity.npc.EntityNPC;
import game.init.SoundEvent;
public class MovingSoundMinecartRiding extends MovingSound
{
private final EntityNPC player;
private final EntityCart minecart;
public MovingSoundMinecartRiding(EntityNPC playerRiding, EntityCart minecart)
{
super(SoundEvent.CART_INSIDE);
this.player = playerRiding;
this.minecart = minecart;
this.attenuationType = false;
this.repeat = true;
// this.repeatDelay = 0;
}
/**
* Like the old updateEntity(), except more generic.
*/
public void update()
{
if (!this.minecart.dead && this.player.isRiding() && this.player.vehicle == this.minecart)
{
float f = ExtMath.sqrtd(this.minecart.motionX * this.minecart.motionX + this.minecart.motionZ * this.minecart.motionZ);
if ((double)f >= 0.01D)
{
this.volume = 0.0F + ExtMath.clampf(f, 0.0F, 1.0F) * 0.75F;
}
else
{
this.volume = 0.0F;
}
}
else
{
this.donePlaying = true;
}
}
}

View file

@ -0,0 +1,33 @@
package game.audio;
import game.init.SoundEvent;
public class PositionedSound extends Sound
{
// public PositionedSound(SoundEvent event, float pitch)
// {
// this(event, 0.25F, pitch, false, false, 0.0F, 0.0F, 0.0F);
// }
public PositionedSound(SoundEvent event)
{
this(event, 1.0F, false, 0.0F, 0.0F, 0.0F);
}
public PositionedSound(SoundEvent event, float volume, float xPosition, float yPosition, float zPosition)
{
this(event, volume, true, xPosition, yPosition, zPosition);
}
private PositionedSound(SoundEvent event, float volume, boolean attenuationType, float xPosition, float yPosition, float zPosition)
{
super(event);
this.volume = volume;
// this.pitch = pitch;
this.xPosF = xPosition;
this.yPosF = yPosition;
this.zPosF = zPosition;
this.repeat = false;
this.attenuationType = attenuationType;
}
}

66
java/src/game/audio/Sound.java Executable file
View file

@ -0,0 +1,66 @@
package game.audio;
import game.init.SoundEvent;
public abstract class Sound
{
protected final SoundEvent event;
protected Volume type = Volume.SFX;
protected float volume = 1.0F;
// protected float pitch = 1.0F;
protected float xPosF;
protected float yPosF;
protected float zPosF;
protected boolean repeat = false;
protected boolean attenuationType = true;
protected Sound(SoundEvent event)
{
this.event = event;
}
public SoundEvent getSoundLocation()
{
return this.event;
}
public boolean canRepeat()
{
return this.repeat;
}
public float getVolume()
{
return this.volume;
}
// public float getPitch()
// {
// return this.pitch;
// }
public float getXPosF()
{
return this.xPosF;
}
public float getYPosF()
{
return this.yPosF;
}
public float getZPosF()
{
return this.zPosF;
}
public boolean getAttenuationType()
{
return this.attenuationType;
}
public Volume getChannelType()
{
return this.type;
}
}

View file

@ -0,0 +1,246 @@
package game.audio;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import game.ExtMath;
import game.Game;
import game.collect.BiMap;
import game.collect.HashBiMap;
import game.collect.Lists;
import game.collect.Maps;
import game.entity.npc.EntityNPC;
public class SoundManager {
private class Source {
private final boolean attModel;
private final float distOrRoll;
// private final short[] buffer;
private final int source;
private float positionX;
private float positionY;
private float positionZ;
private float volume;
public Source(short[] buffer, Volume type, boolean toLoop, String sourcename, float x, float y, float z, boolean attModel,
float distOrRoll, float volume) {
this.positionX = x;
this.positionY = y;
this.positionZ = z;
this.attModel = attModel;
this.distOrRoll = distOrRoll;
// this.buffer = buffer;
this.volume = volume;
this.source = SoundManager.this.getNextChannel(sourcename);
updateGain();
SoundManager.this.gm.getAudioInterface().alSourcePlay(this.source, buffer, type, toLoop);
}
public void updateGain() {
float gain;
if(attModel) {
double dX = positionX - SoundManager.this.listenerX;
double dY = positionY - SoundManager.this.listenerY;
double dZ = positionZ - SoundManager.this.listenerZ;
float dist = (float)Math.sqrt(dX * dX + dY * dY + dZ * dZ);
if(dist <= 0) {
gain = 1.0f;
}
else if(dist >= distOrRoll) {
gain = 0.0f;
}
else {
gain = 1.0f - (dist / distOrRoll);
}
if(gain > 1.0f)
gain = 1.0f;
if(gain < 0.0f)
gain = 0.0f;
}
else {
gain = 1.0f;
}
SoundManager.this.gm.getAudioInterface().alGain(source, (gain * volume));
}
}
private final Map<String, Sound> playingSounds = HashBiMap.<String, Sound>create();
private final Map<Sound, String> invPlayingSounds = ((BiMap)this.playingSounds).inverse();
private final List<MovingSound> tickableSounds = Lists.<MovingSound>newArrayList();
private final Map<String, Integer> playingSoundsStopTime = Maps.<String, Integer>newHashMap();
private final Map<String, Source> sources = new HashMap<String, Source>();
private final String[] channels = new String[28];
private final Game gm;
private float listenerX;
private float listenerY;
private float listenerZ;
private int nextChannel = 0;
private int playTime = 0;
private int eventId = 0;
public SoundManager(Game gm) {
this.gm = gm;
}
public void unload()
{
this.stopSounds();
for(int x = 0; x < this.channels.length; x++) {
channels[x] = null;
}
this.sources.clear();
this.nextChannel = 0;
this.eventId = 0;
}
public void stopSounds()
{
for (String s : this.playingSounds.keySet())
{
Source src = sources.get(s);
if(src != null)
this.gm.getAudioInterface().alSourceStop(src.source);
sources.remove(s);
}
this.playingSounds.clear();
this.tickableSounds.clear();
this.playingSoundsStopTime.clear();
}
public void update()
{
++this.playTime;
for (MovingSound itickablesound : this.tickableSounds)
{
itickablesound.update();
if (itickablesound.isDonePlaying())
{
String s = (String)this.invPlayingSounds.get(itickablesound);
if (s != null)
{
Source src = sources.get(s);
if(src != null)
this.gm.getAudioInterface().alSourceStop(src.source);
sources.remove(s);
}
}
else
{
String s = (String)this.invPlayingSounds.get(itickablesound);
Source source = sources.get(s);
if(source != null) {
source.volume = ExtMath.clampf(itickablesound.getVolume(), 0.0f, 1.0f);
source.positionX = itickablesound.getXPosF();
source.positionY = itickablesound.getYPosF();
source.positionZ = itickablesound.getZPosF();
source.updateGain();
}
}
}
Iterator<Entry<String, Sound>> iterator = this.playingSounds.entrySet().iterator();
while (iterator.hasNext())
{
Entry<String, Sound> entry = (Entry)iterator.next();
String s1 = (String)entry.getKey();
Sound isound = (Sound)entry.getValue();
Source source = this.sources.get(s1);
if (source == null || !this.gm.getAudioInterface().alPlaying(source.source))
{
int i = ((Integer)this.playingSoundsStopTime.get(s1)).intValue();
if (i <= this.playTime)
{
iterator.remove();
this.sources.remove(s1);
this.playingSoundsStopTime.remove(s1);
if (isound instanceof MovingSound)
{
this.tickableSounds.remove(isound);
}
}
}
}
}
public void playSound(Sound sound)
{
float volume = sound.getVolume();
float attn = 16.0F;
if (volume > 1.0F)
attn *= volume;
volume = ExtMath.clampf(volume, 0.0f, 1.0f);
if (volume > 0.0F)
{
String s = String.format("snd-%016x", this.eventId++);
this.sources.put(s, new Source(sound.getSoundLocation().getBuffer(), sound.getChannelType(), sound.canRepeat(), s, sound.getXPosF(), sound.getYPosF(), sound.getZPosF(), sound.getAttenuationType(), attn, volume));
this.playingSoundsStopTime.put(s, this.playTime + 20);
this.playingSounds.put(s, sound);
if (sound instanceof MovingSound)
this.tickableSounds.add((MovingSound)sound);
}
}
public void setListener(EntityNPC player, float partial)
{
double x = player.prevX + (player.posX - player.prevX) * (double)partial;
double y = player.prevY + (player.posY - player.prevY) * (double)partial + (double)player.getEyeHeight();
double z = player.prevZ + (player.posZ - player.prevZ) * (double)partial;
this.listenerX = (float)x;
this.listenerY = (float)y;
this.listenerZ = (float)z;
for(Source source : this.sources.values()) {
source.updateGain();
}
}
private int getNextChannel(String sourcename) {
int x;
int next;
String name;
next = nextChannel;
int n = next;
Source src;
for(x = 0; x < channels.length; x++) {
name = this.channels[n];
src = name == null ? null : sources.get(name);
if(src == null || !this.gm.getAudioInterface().alPlaying(src.source)) {
nextChannel = n + 1;
if(nextChannel >= channels.length)
nextChannel = 0;
this.channels[n] = sourcename;
if(src != null)
this.sources.remove(name);
return n;
}
n++;
if(n >= channels.length)
n = 0;
}
n = next;
name = this.channels[n];
src = name == null ? null : sources.get(name);
nextChannel = n + 1;
if(nextChannel >= channels.length)
nextChannel = 0;
this.channels[n] = sourcename;
if(src != null)
this.sources.remove(name);
return n;
}
}

View file

@ -0,0 +1,124 @@
package game.audio;
import game.init.SoundEvent;
public class SoundType
{
public static final SoundType SLIME = new SoundType(SoundEvent.SLIME_BIG, SoundEvent.SLIME_SMALL, 1.0F, 1.0F);
// {
// public Sounds getBreakSound()
// {
// return "mob.slime.big";
// }
// public Sounds getPlaceSound()
// {
// return "mob.slime.big";
// }
// public Sounds getStepSound()
// {
// return "mob.slime.small";
// }
// };
public static final SoundType ANVIL = new SoundType(SoundEvent.STONE, SoundEvent.ANVIL_LAND, SoundEvent.STONE, 0.3F, 1.0F);
// {
// public Sounds getBreakSound()
// {
// return "dig.stone";
// }
// public Sounds getPlaceSound()
// {
// return "random.anvil_land";
// }
// public Sounds getStepSound() // fix ...
// {
// return "dig.stone";
// }
// };
public static final SoundType LADDER = new SoundType(SoundEvent.WOOD, null, 1.0F, 1.0F);
// {
// public Sounds getBreakSound()
// {
// return "dig.wood";
// }
// public Sounds getStepSound()
// {
// return null;
// }
// };
public static final SoundType SNOW = new SoundType(SoundEvent.SNOW, 1.0F, 1.0F);
public static final SoundType SAND = new SoundType(SoundEvent.SAND, 1.0F, 1.0F);
public static final SoundType CLOTH = new SoundType(SoundEvent.CLOTH, 1.0F, 1.0F);
public static final SoundType GLASS = new SoundType(SoundEvent.GLASS, SoundEvent.STONE, SoundEvent.STONE, 1.0F, 1.0F);
// {
// public Sounds getBreakSound()
// {
// return "dig.glass";
// }
// public Sounds getPlaceSound()
// {
// return "dig.stone";
// }
// };
public static final SoundType METAL = new SoundType(SoundEvent.STONE, 1.0F, 1.5F);
public static final SoundType PISTON = new SoundType(SoundEvent.STONE, 1.0F, 1.0F);
public static final SoundType GRASS = new SoundType(SoundEvent.GRASS /* , null */ , 1.0F, 1.0F);
// {
// public Sounds getStepSound()
// {
// return null;
// }
// };
public static final SoundType GRAVEL = new SoundType(SoundEvent.GRAVEL, 1.0F, 1.0F);
public static final SoundType WOOD = new SoundType(SoundEvent.WOOD, 1.0F, 1.0F);
public static final SoundType STONE = new SoundType(SoundEvent.STONE, 1.0F, 1.0F);
private final SoundEvent breakSound;
private final SoundEvent placeSound;
private final SoundEvent stepSound;
private final float volume;
// private final float frequency;
private SoundType(SoundEvent breakSound, SoundEvent placeSound, SoundEvent stepSound, float volume, float frequency)
{
this.breakSound = breakSound;
this.placeSound = placeSound;
this.stepSound = stepSound;
this.volume = volume;
// this.frequency = frequency;
}
private SoundType(SoundEvent breakPlaceSound, SoundEvent stepSound, float volume, float frequency)
{
this(breakPlaceSound, breakPlaceSound, stepSound, volume, frequency);
}
private SoundType(SoundEvent sound, float volume, float frequency)
{
this(sound, sound, sound, volume, frequency);
}
public SoundEvent getBreakSound()
{
return this.breakSound;
}
public SoundEvent getPlaceSound()
{
return this.placeSound;
}
public SoundEvent getStepSound()
{
return this.stepSound;
}
public float getVolume()
{
return this.volume;
}
public float getFrequency()
{
return 1.0f; // this.frequency;
}
}

View file

@ -0,0 +1,86 @@
package game.audio;
import game.CVar;
import game.CVarCategory;
import game.Game;
import game.Slider;
import game.color.TextColor;
public enum Volume implements CVar {
MASTER("master", "Gesamt"),
MUSIC("music", "Musik"),
SFX("sfx", "Geräusche"),
GUI("gui", "Oberfläche");
public final String id;
public final String name;
private int value = 100;
private Volume(String id, String name) {
this.id = id;
this.name = name;
}
public String getDisplay() {
return this.name;
}
public String getCVarName() {
return "snd_volume_" + this.id;
}
public String getType() {
return TextColor.DGREEN + "volume";
}
public CVarCategory getCategory() {
return CVarCategory.SOUND;
}
public boolean parse(String str) {
int value;
try {
value = Integer.parseInt(str);
}
catch(NumberFormatException e) {
return false;
}
if(value < 0 || value > 100)
return false;
this.value = value;
this.apply();
return true;
}
public String format() {
return "" + this.value;
}
public String getDefault() {
return "100";
}
public void setDefault() {
this.value = 100;
this.apply();
}
public String getValues() {
return "0..100";
}
public void apply() {
if(Game.getGame().getAudioInterface() != null)
Game.getGame().getAudioInterface().alSetVolume(this, (short)(((float)this.value) / 100.0f * 32767.0f));
}
public Slider selector(int x, int y, int w, int h) {
return new Slider(x, y, w, h, 0, 0, 100, 100, this.value, new Slider.Callback() {
public void use(Slider elem, int value) {
Volume.this.value = value;
Volume.this.apply();
}
}, this.name, "%");
}
}