/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven; import java.util.*; import java.io.*; import haven.Audio.CS; public class ActAudio extends GLState.Abstract { public static final GLState.Slot<ActAudio> slot = new GLState.Slot<ActAudio>(GLState.Slot.Type.SYS, ActAudio.class); private final Collection<CS> clips = new ArrayList<CS>(); private final Collection<CS> current = new ArrayList<CS>(); private final Map<Global, Global> global = new HashMap<Global, Global>(); public void prep(Buffer st) { st.put(slot, this); } public interface Global { public boolean cycle(ActAudio list); } public static class PosClip implements Rendered { private final Audio.DataClip clip; public PosClip(Audio.DataClip clip) { this.clip = clip; } public void draw(GOut g) { g.apply(); ActAudio list = g.st.cur(slot); if(list != null) { Coord3f pos = g.st.mv.mul4(Coord3f.o); double pd = Math.sqrt((pos.x * pos.x) + (pos.y * pos.y)); this.clip.vol = Math.min(1.0, 50.0 / pd); list.add(clip); } } public boolean setup(RenderList rl) { return(true); } } public static class Ambience implements Rendered { public final Resource res; public final double bvol; private Glob glob = null; public Ambience(Resource res, double bvol) { if(res.layer(Resource.audio, "amb") == null) { /* This check is mostly just to make sure the resource * is loaded and doesn't throw Loading exception in * the setup routine. */ throw(new RuntimeException("No ambient clip found in " + res)); } this.res = res; this.bvol = bvol; } public Ambience(Resource res) { this(res, res.layer(Resource.audio, "amb").bvol); } public static class Glob implements Global { public final Resource res; private final Audio.DataClip clip; private int n; private double vacc; private double lastupd = System.currentTimeMillis() / 1000.0; public Glob(Resource res) { this.res = res; final Resource.Audio clip = res.layer(Resource.audio, "amb"); if(clip == null) throw(new RuntimeException("No ambient clip found in " + res)); this.clip = new Audio.DataClip(new RepeatStream(new RepeatStream.Repeater() { public InputStream cons() { return(clip.pcmstream()); } }), 0.0, 1.0); } public int hashCode() { return(res.hashCode()); } public boolean equals(Object other) { return((other instanceof Glob) && (((Glob)other).res == this.res)); } public boolean cycle(ActAudio list) { double now = System.currentTimeMillis() / 1000.0; double td = Math.max(now - lastupd, 0.0); if(vacc < clip.vol) clip.vol = Math.max(clip.vol - (td * 0.5), 0.0); else if(vacc > clip.vol) clip.vol = Math.min(clip.vol + (td * 0.5), 1.0); if((n == 0) && (clip.vol < 0.005)) return(true); vacc = 0.0; n = 0; lastupd = now; list.add(clip); return(false); } public void add(double vol) { vacc += vol; n++; } } public void draw(GOut g) { g.apply(); if(glob == null) { ActAudio list = g.st.cur(slot); if(list == null) return; glob = list.intern(new Glob(res)); } Coord3f pos = g.st.mv.mul4(Coord3f.o); double pd = Math.sqrt((pos.x * pos.x) + (pos.y * pos.y)); double svol = Math.min(1.0, 50.0 / pd); glob.add(svol * bvol); } public boolean setup(RenderList rl) { return(true); } } public void add(CS clip) { clips.add(clip); } @SuppressWarnings("unchecked") public <T extends Global> T intern(T glob) { T ret = (T)global.get(glob); if(ret == null) global.put(glob, ret = glob); return(ret); } public void cycle() { for(Iterator<Global> i = global.keySet().iterator(); i.hasNext();) { Global glob = i.next(); if(glob.cycle(this)) i.remove(); } for(CS clip : current) { if(!clips.contains(clip)) Audio.stop(clip); } for(CS clip : clips) { if(!current.contains(clip)) Audio.play(clip); } current.clear(); current.addAll(clips); clips.clear(); } public void clear() { for(CS clip : current) Audio.stop(clip); } }