Update gitignore

This commit is contained in:
Vulpovile
2025-04-09 21:55:29 -07:00
parent 0648a0178c
commit 4786cb7bd7
12 changed files with 769 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
package com.flaremicro.audio;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Random;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
import com.flaremicro.util.Util;
public class AgnosticAudioSystem implements GameAudioSystem {
private Collection<AudioFinishedListener> listeners = new ArrayList<AudioFinishedListener>();
private HashMap<String, AudioSystemThread> threads = new HashMap<String, AudioSystemThread>();
private Random random = new Random();
public void init() {
}
public void destroy() {
}
public void addAudioFinishedListener(AudioFinishedListener listener) {
this.listeners.add(listener);
}
public void removeAudioFinishedListener(AudioFinishedListener listener) {
this.listeners.remove(listener);
}
public void removeAudioFinishedListeners() {
this.listeners.clear();
}
public String play(URL res) {
if (res.getPath().contains("."))
{
String key = String.valueOf(random.nextLong());
String ext = res.getPath().substring(res.getPath().lastIndexOf(".") + 1).toLowerCase();
AudioSystemThread audioSystemThread = null;
if (ext.equalsIgnoreCase("mp3"))
audioSystemThread = new AgnosticMp3AudioSystemThread(res, key, this, false);
if (ext.equalsIgnoreCase("wav"))
audioSystemThread = new AgnosticWavAudioSystemThread(res, key, this, false);
if (audioSystemThread != null)
{
synchronized (threads)
{
threads.put(key, audioSystemThread);
}
new Thread(audioSystemThread).start();
return key;
}
}
return null;
}
public void playbg(URL res) {
stopbg();
if (res.getPath().contains("."))
{
String ext = res.getPath().substring(res.getPath().lastIndexOf(".") + 1).toLowerCase();
AudioSystemThread audioSystemThread = null;
if (ext.equalsIgnoreCase("mp3"))
audioSystemThread = new AgnosticMp3AudioSystemThread(res, "bg", this, true);
if (ext.equalsIgnoreCase("wav"))
audioSystemThread = new AgnosticWavAudioSystemThread(res, "bg", this, true);
if (audioSystemThread != null)
{
synchronized (threads)
{
threads.put("bg", audioSystemThread);
}
new Thread(audioSystemThread).start();
}
}
}
public void stopbg() {
if (threads.containsKey("bg"))
threads.get("bg").stopMusic();
stopped("bg", threads.get("bg"));
}
public void stopped(String threadName, AudioSystemThread audioSystemThread) {
synchronized (threads)
{
if (audioSystemThread != null && audioSystemThread == threads.get(threadName))
{
threads.remove(threadName);
}
}
for (AudioFinishedListener listener : listeners)
{
listener.audioFinished(threadName, audioSystemThread.resource);
}
}
public void setVolume(float percent, String key) {
AudioSystemThread ast = threads.get(key);
if (ast != null)
{
ast.setVolume(percent);
}
}
public void stop(String currentKey) {
AudioSystemThread ast = threads.get(currentKey);
if (ast != null)
{
ast.stopMusic();
}
}
}
abstract class AudioSystemThread implements Runnable {
protected final boolean looping;
protected final AgnosticAudioSystem system;
protected final URL resource;
protected final String threadName;
public AudioSystemThread(URL resource, String threadName, AgnosticAudioSystem system, boolean looping) {
this.resource = resource;
this.looping = looping;
this.threadName = threadName;
this.system = system;
}
public abstract void setVolume(float percent);
public abstract void stopMusic();
public final void onStop() {
this.system.stopped(threadName, this);
}
}
class AgnosticMp3AudioSystemThread extends AudioSystemThread {
private boolean running = true;
private Player player = null;
private static final int FRAME_BUFFER = 2;
private float volume = 1F;
public AgnosticMp3AudioSystemThread(URL resource, String threadName, AgnosticAudioSystem system, boolean looping) {
super(resource, threadName, system, looping);
}
public void run() {
do
{
try
{
player = new Player(new BufferedInputStream(resource.openStream()));
player.setVolume(volume);
while (running && player.play(FRAME_BUFFER));
}
catch (JavaLayerException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
while (running && looping);
super.onStop();
}
public void stopMusic() {
this.running = false;
if (player != null)
player.close();
}
@Override
public void setVolume(float percent) {
volume = percent;
if (player != null)
{
player.setVolume(volume);
}
}
}
class AgnosticWavAudioSystemThread extends AudioSystemThread {
private boolean running = true;
private SourceDataLine sourceLine = null;
private float volume = 1.0F;
public AgnosticWavAudioSystemThread(URL resource, String threadName, AgnosticAudioSystem system, boolean looping) {
super(resource, threadName, system, looping);
}
public void run() {
AudioInputStream audioStream = null;
do
{
try
{
audioStream = AudioSystem.getAudioInputStream(resource);
AudioFormat audioFormat = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
sourceLine = (SourceDataLine) AudioSystem.getLine(info);
sourceLine.open();
sourceLine.start();
FloatControl volume = (FloatControl) sourceLine.getControl(FloatControl.Type.MASTER_GAIN);
volume.setValue(this.volume);
int nBytesRead = 0;
byte[] abData = new byte[4096];
while (nBytesRead != -1 && running)
{
try
{
nBytesRead = audioStream.read(abData, 0, abData.length);
}
catch (IOException e)
{
e.printStackTrace();
}
if (nBytesRead >= 0)
{
@SuppressWarnings("unused")
int nBytesWritten = sourceLine.write(abData, 0, nBytesRead);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
Util.cleanClose(audioStream);
if (sourceLine != null)
{
sourceLine.drain();
sourceLine.close();
}
}
}
while (running && looping);
onStop();
}
public void stopMusic() {
this.running = false;
if (sourceLine != null)
{
sourceLine.drain();
sourceLine.close();
}
}
@Override
public void setVolume(float percent) {
if (sourceLine != null)
{
this.volume = percent;
FloatControl volume = (FloatControl) sourceLine.getControl(FloatControl.Type.MASTER_GAIN);
volume.setValue(percent);
}
}
}

View File

@@ -0,0 +1,7 @@
package com.flaremicro.audio;
import java.net.URL;
public interface AudioFinishedListener {
public void audioFinished(String key, URL resource);
}

View File

@@ -0,0 +1,83 @@
package com.flaremicro.audio;
import java.net.URL;
import java.util.Random;
public class AudioPlaylist implements AudioFinishedListener{
private final AgnosticAudioSystem aas;
public AudioPlaylist(AgnosticAudioSystem aas)
{
this.aas = aas;
}
String currentKey = null;
int currentPlaylistIndex = 0;
URL[] playlist = null;
float currVol = 1F;
private boolean stopped = true;
public void setPlaylist(URL[] url)
{
this.playlist = url;
this.currentPlaylistIndex = 0;
}
public void shuffle(){
Random rand = new Random();
for (int i = 0; i < playlist.length; i++) {
int randomIndexToSwap = rand.nextInt(playlist.length);
URL temp = playlist[randomIndexToSwap];
playlist[randomIndexToSwap] = playlist[i];
playlist[i] = temp;
}
}
public void start()
{
stopped = false;
currentPlaylistIndex = 0;
aas.addAudioFinishedListener(this);
if(currentKey == null && playlist != null && playlist.length > 0)
{
currentKey = aas.play(playlist[0]);
aas.setVolume(currVol, currentKey);
}
}
public void next()
{
if(playlist != null && playlist.length > 0)
{
currentPlaylistIndex = (currentPlaylistIndex + 1 ) % playlist.length;
currentKey = aas.play(playlist[currentPlaylistIndex]);
aas.setVolume(currVol, currentKey);
}
}
public void audioFinished(String key, URL resource) {
if(!stopped && key == currentKey)
{
next();
}
}
public void setVolume(float volume) {
currVol = volume;
aas.setVolume(currVol, currentKey);
}
public void stop() {
stopped = true;
aas.stop(currentKey);
}
public float getVolume() {
return currVol;
}
}

View File

@@ -0,0 +1,16 @@
package com.flaremicro.audio;
import java.net.URL;
public interface GameAudioSystem {
public void init();
public void destroy();
public void playbg(URL res);
public String play(URL res);
public void stopbg();
}

View File

@@ -0,0 +1,130 @@
package com.flaremicro.visualforecast;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import com.flaremicro.audio.AgnosticAudioSystem;
import com.flaremicro.audio.AudioFinishedListener;
import com.flaremicro.audio.AudioPlaylist;
public class AudioManager implements AudioFinishedListener {
private PropertyManager propertyManager;
private AgnosticAudioSystem aas;
private AudioPlaylist audioPlaylist;
private File playlistDirectory = new File("./Music");
HashSet<String> playingKeys = new HashSet<String>();
public AudioManager(PropertyManager propertyManager) {
this.propertyManager = propertyManager;
this.aas = new AgnosticAudioSystem();
this.aas.addAudioFinishedListener(this);
this.audioPlaylist = new AudioPlaylist(aas);
updatePlaylist();
}
private void updatePlaylist() {
playlistDirectory = propertyManager.getFile("music-dir", playlistDirectory);
if (playlistDirectory == null || !playlistDirectory.isDirectory())
return;
File[] files = playlistDirectory.listFiles();
URL[] urls = new URL[files.length];
for (int i = 0; i < files.length; i++)
{
try
{
urls[i] = files[i].toURI().toURL();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
}
audioPlaylist.setPlaylist(urls);
audioPlaylist.shuffle();
}
@Override
public void audioFinished(String key, URL resource) {
playingKeys.remove(key);
if (playingKeys.size() == 0)
rampVolume(1F);
}
public void startPlaylist() {
if (playlistDirectory == null || !playlistDirectory.isDirectory())
return;
audioPlaylist.start();
}
public void stopPlaylist() {
audioPlaylist.stop();
}
public void end() {
audioPlaylist.stop();
aas.destroy();
}
public void playAnnouncement(File file) {
try
{
URL url = file.toURI().toURL();
rampVolume(-10F);
playingKeys.add(aas.play(url));
}
catch (MalformedURLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Thread currThread = null;
private float volume = 1F;
//***horrible***
private void rampVolume(float newVolume) {
this.volume = newVolume;
if (currThread != null)
return;
currThread = new Thread(new Runnable() {
@Override
public void run() {
try
{
if (volume < audioPlaylist.getVolume())
{
while (audioPlaylist.getVolume() > volume)
{
audioPlaylist.setVolume(audioPlaylist.getVolume() - 0.1F);
Thread.sleep(10L);
}
}
else if (volume > audioPlaylist.getVolume())
{
while (audioPlaylist.getVolume() < volume)
{
audioPlaylist.setVolume(audioPlaylist.getVolume() + 0.1F);
Thread.sleep(10L);
}
}
}
catch (InterruptedException ex)
{
}
audioPlaylist.setVolume(volume);
currThread = null;
}
});
currThread.start();
}
}

View File

@@ -0,0 +1,232 @@
package com.flaremicro.visualforecast.displays.impl;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import com.flaremicro.visualforecast.RenderPanel;
import com.flaremicro.visualforecast.api.ForecastProvider;
import com.flaremicro.visualforecast.displays.Display;
import com.flaremicro.visualforecast.forecast.ForecastDetails;
import com.flaremicro.visualforecast.forecast.TownForecast;
import com.flaremicro.visualforecast.graphics.DrawingUtil;
import com.flaremicro.visualforecast.graphics.FontManager;
import static com.flaremicro.visualforecast.graphics.RenderConstants.*;
public class ThirtySixHourForecastDisplay implements Display {
private int townIndex = 0;
private TownForecast[] townForecasts;
private String[] lines = new String[0];
private Font font;
private int stringOffset = 0;
private int ticksBeforeChange = 0;
public ThirtySixHourForecastDisplay() {
font = FontManager.getInstance().getOrCreateFont(Font.TRUETYPE_FONT, this.getClass().getResource("/Star4000.ttf")).deriveFont(30F);
/*
int w2 = g2d.getFontMetrics().stringWidth("EXTREME WEATHER ADVISORY");
drawOutlinedString(g2d, (W >> 1) - (w2 >> 1), TOPBAR_HEIGHT + 48, "EXTREME WEATHER ADVISORY", Color.RED, Color.BLACK, 2);
g2d.setFont(font.deriveFont(30F));
for (int i = 0; i < testString.length; i++)
{
drawOutlinedString(g2d, 90, TOPBAR_HEIGHT + 78 + 25 * i, testString[i], Color.WHITE, Color.BLACK, 1);
}
*/
}
@Override
public void tick(RenderPanel renderer, long ticks, int iconTicks) {
ticksBeforeChange--;
if (ticksBeforeChange <= 0)
{
stringOffset += 9;
if (stringOffset < lines.length)
{
ticksBeforeChange = 200;
renderer.requestExclusiveBoundedRepaint();
}
else
{
if(!setNextTown(renderer))
{
renderer.nextDisplay();
}
else
{
ticksBeforeChange = 200;
renderer.requestExclusiveBoundedRepaint();
}
}
}
}
@Override
public void initDisplay(RenderPanel renderer, ForecastProvider forecastProvider, long ticks, int iconTicks) {
ForecastDetails details = forecastProvider != null ? forecastProvider.getForecast() : null;
renderer.setCurrentForecast("36 Hour Detailed Forecast");
townIndex = -1;
if(details == null || details.getTownForecast() == null || details.getTownForecast().length <= 0)
{
renderer.nextDisplay();
return;
}
else
{
townForecasts = details.getTownForecast();
if(!setNextTown(renderer))
{
renderer.nextDisplay();
return;
}
}
}
public boolean setNextTown(RenderPanel renderer)
{
townIndex++;
if(townIndex >= townForecasts.length)
return false;
TownForecast town = townForecasts[townIndex];
if(!town.isDisplaySupported(getDisplayName()) || town.getDetailedForecast() == null || town.getDetailedForecast().length <= 0)
{
return setNextTown(renderer);
}
ticksBeforeChange = 200;
stringOffset = 0;
StringBuilder forecastBuilder = new StringBuilder();
for(int i = 0; i < town.getDetailedForecast().length; i++)
{
if(i > 0)
forecastBuilder.append("\n\n");
forecastBuilder.append(town.getDetailedForecast()[i].title + ":\n");
forecastBuilder.append(town.getDetailedForecast()[i].status);
}
ArrayList<String> linesList = new ArrayList<String>();
renderer.setCurrentTown(town.getTownName());
BufferedImage disposableImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = disposableImage.createGraphics();
FontMetrics f = g.getFontMetrics(font);
String[] lines = forecastBuilder.toString().split("\n");
for (int i = 0; i < lines.length; i++)
{
splitText(lines[i], W - 150 - STROKE_WIDTH, f, linesList);
//if(words[i])
}
g.dispose();
disposableImage.flush();
redrawRegionlost(renderer);
this.lines = linesList.toArray(new String[0]);
return true;
}
private void splitText(String text, int maxWidth, FontMetrics fontMetrics, ArrayList<String> lineList) {
text = text.trim();
//this.text.add(text);
if (fontMetrics.stringWidth(text) <= maxWidth)
lineList.add(text);
else
{
while (text.length() > 0)
{
int idx = binSearch(text, fontMetrics, maxWidth, 1, text.length());
if (idx <= 0)
break;
String texPart = text.substring(0, idx).trim();
if (fontMetrics.stringWidth(text.substring(0, Math.min(text.length(), idx + 1))) > maxWidth && texPart.contains(" "))
{
idx = texPart.lastIndexOf(" ");
texPart = texPart.substring(0, idx);
}
lineList.add(texPart);
text = text.substring(idx).trim();
}
}
}
private static int binSearch(String string, FontMetrics fontMetrics, int key, int low, int high) {
int index = 0;
while (low <= high)
{
int mid = low + ((high - low) / 2);
int midmetric = fontMetrics.stringWidth(string.substring(0, mid));
if (midmetric < key)
{
low = mid + 1;
index = mid;
}
else if (midmetric > key)
{
high = mid - 1;
}
else if (midmetric == key)
{
index = mid;
break;
}
}
return index;
}
@Override
public void drawDisplay(RenderPanel renderer, Graphics2D g2d, long ticks, int iconTicks) {
}
@Override
public void drawBoundLimitedDisplay(RenderPanel renderer, Graphics2D g2d, Rectangle bounds, long ticks, int iconTicks) {
DrawingUtil.drawGradientRect(g2d, 60, TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20, BG_BLUE.darker(), BG_BLUE.brighter());
g2d.setFont(font);
FontMetrics fontMetrics = g2d.getFontMetrics();
g2d.setColor(BG_BLUE.darker());
g2d.drawRect(60 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET, W - 120 - STROKE_WIDTH, MAINBAR_HEIGHT - STROKE_WIDTH);
for (int i = 0; i < Math.min(9, lines.length - this.stringOffset); i++)
{
if (lines[i + stringOffset].startsWith("*") && lines[i].endsWith("*"))
{
String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1);
DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2);
DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2);
DrawingUtil.drawOutlinedString(g2d, W - 95 - STROKE_WIDTH, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, "*", Color.WHITE, Color.BLACK, 2);
}
else if (lines[i + stringOffset].startsWith("[") && lines[i].endsWith("]"))
{
String line = lines[i + stringOffset].substring(1, lines[i + stringOffset].length() - 1);
DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET + (W - 150 - STROKE_WIDTH) / 2 - fontMetrics.stringWidth(line) / 2, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, line, Color.WHITE, Color.BLACK, 2);
}
else DrawingUtil.drawOutlinedString(g2d, 60 + 20 + STROKE_OFFSET, TOPBAR_HEIGHT + STROKE_OFFSET + 40 + i * 30, lines[i + stringOffset], Color.WHITE, Color.BLACK, 2);
}
}
@Override
public void redrawRegionlost(RenderPanel renderer) {
renderer.addRedrawBound(TOPBAR_HEIGHT, W - 120, MAINBAR_HEIGHT, 20);
}
@Override
public void notifyForecastProviderUpdate(RenderPanel renderer, ForecastProvider forecastProvider) {
}
@Override
public String getDisplayName() {
return "36-hour";
}
}

View File

@@ -0,0 +1,12 @@
package com.flaremicro.visualforecast.forecast;
public class DetailedForecast {
public final String title;
public final String status;
public DetailedForecast(String title, String status)
{
this.title = title;
this.status = status;
}
}