package NET.worlds.scape; import NET.worlds.console.Main; import NET.worlds.console.MainCallback; import NET.worlds.core.IniFile; import NET.worlds.network.CacheFile; import NET.worlds.network.URL; import java.io.IOException; import java.util.Vector; public class Sound extends Action implements BGLoaded, StateContext { static int debugLevel = IniFile.gamma().getIniInt("sounddebug", 0); private String cachedName; private URL soundURL; protected CacheFile cachedFile = null; protected float soundVolume = 1.0F; protected float stopDistance = 1000.0F; private int repeat = 1; private boolean continuous = false; private boolean attenuateOn = true; private boolean panningOn = true; protected SuperRoot activeSeq; SoundPlayer player; AudibilityFilter aFilter = null; private int maxHopcount = 1; private boolean doorMovementFlag = false; private static boolean soundOn = true; boolean playAfterLoading = true; protected int state = 0; protected static final int SYNC = 1; protected static final int ASYNC = 2; protected int backgroundState = 1; private static SoundCallback closer; static Vector cachedEntries; private static Vector pendingEntries; private boolean mainRan; private boolean opened; private static Object classCookie = new Object(); static { debugOut(1, "SOUND DEBUGGING LEVEL = " + debugLevel); } private static void debugOut(int n, String s) { if (debugLevel > n) { System.out.println(s); } } public Sound(URL soundURL) { Transform.countClass(this, 1); debugOut(9, "Sound " + soundURL); this.setURL(soundURL); } public Sound() { Transform.countClass(this, 1); debugOut(9, "Sound "); this.setURL(null); } public final void setURL(URL url) { if (this.activeSeq != null) { this.closePlayer(); } this.soundURL = url; } public final URL getURL() { return this.soundURL; } public final void setSoundVolume(float vol) { this.soundVolume = vol; } public final float getSoundVolume() { return this.soundVolume; } public final void setStopDistance(float i) { this.stopDistance = i; } public final float getStopDistance() { return this.stopDistance; } public final void setRepeat(int i) { this.repeat = i; } public final int getRepeat() { return this.repeat; } public final void setContinuous(boolean c) { this.continuous = c; } public final boolean isContinuous() { return this.continuous; } public final void setAttenuate(boolean b) { this.attenuateOn = b; } public final boolean getAttenuate() { return this.attenuateOn; } public final void setPanning(boolean b) { this.panningOn = b; } public final boolean getPanning() { return this.panningOn; } public final int getState() { return this.player.getState(); } public final int getHopcount() { return this.maxHopcount; } public final void setHopcount(int i) { this.maxHopcount = i; if (this.aFilter != null) { this.aFilter.setHopcount(this.maxHopcount); } } public boolean getDoorMovementFlag() { return this.doorMovementFlag; } public void setDoorMovementFlag(boolean b) { this.doorMovementFlag = b; if (this.aFilter != null) { this.startAudibilityFilter(); } } private void startAudibilityFilter() { debugOut(9, "startAudibilityFilter " + this.getURL()); WObject owner = (WObject)this.getOwner(); if (owner != null) { if (this.doorMovementFlag) { this.aFilter = new DoorBasedFilter(this, owner, this.maxHopcount, this.stopDistance); } else { this.aFilter = new AudibilityFilter(this, owner, this.maxHopcount); } this.aFilter.moveEmitter(); } } public static void turnSoundOn() { soundOn = true; } public static void turnSoundOff() { soundOn = false; } public void setPlayAfterLoading(boolean b) { this.playAfterLoading = b; } protected boolean isRealAudio(URL s) { return s.endsWith(".ram") || s.endsWith(".ra") || s.endsWith(".rm"); } @Override public void changeState(int s) { debugOut(9, "changeState to " + s); this.state = s; } protected Object doState(Object obj) { return SoundState.instance().doState(this, obj); } private boolean isBackgroundProcessing() { if (this.state != 0) { debugOut(9, "backgroundProcessing " + this.state); } return this.state != 0; } private void startSound() { this.backgroundState = 1; if (this.aFilter == null) { this.startAudibilityFilter(); } if (this.aFilter != null) { this.aFilter.setEmitterOn(true); } if (this.state == 0) { this.state = 1; } this.doState(this.soundURL); } @Override public Object asyncBackgroundLoad(String localName, URL remoteName) { this.backgroundState = 2; return this.doState(URL.make(localName)); } @Override public boolean syncBackgroundLoad(Object obj, URL remoteURL) { this.backgroundState = 1; this.doState(obj); return false; } @Override public Room getBackgroundLoadRoom() { WObject owner = (WObject)this.getOwner(); return owner == null ? null : owner.getRoom(); } protected void ensureClosure() { if (closer == null) { closer = new SoundCallback(); Main.register(closer); } if (cachedEntries == null) { cachedEntries = new Vector(); } cachedEntries.addElement(this.cachedFile); } protected void unlockCache() { if (this.cachedFile != null) { debugOut(6, "unlocking " + this.soundURL); this.cachedFile.finalize(); assert cachedEntries != null; assert cachedEntries.indexOf(this.cachedFile, 0) >= 0; cachedEntries.removeElement(this.cachedFile); this.cachedFile = null; } } synchronized boolean openPlayer(URL url) { assert this.player == null; this.cachedName = url.unalias(); debugOut(4, "openPlayer on:: " + this.cachedName); if (url.endsWith(".wav")) { this.player = new WavSoundPlayer(this); } else if (url.endsWith(".mid")) { this.player = new MCISoundPlayer(this); } else { this.player = new WMPSoundPlayer(this); } if (Main.isMainThread()) { this.openInMainThread(); } else { this.mainRan = false; Main.register(new MainCallback() { @Override public void mainCallback() { Sound.this.openInMainThread(); Main.unregister(this); } }); while (!this.mainRan) { try { this.wait(); } catch (InterruptedException var3) { } } } return this.opened; } public synchronized void openInMainThread() { this.opened = this.player.open(this.soundVolume, this.stopDistance, this.attenuateOn, this.panningOn); if (!this.opened) { this.closePlayer(); } this.mainRan = true; this.notify(); } void closePlayer() { if (this.player != null) { this.player.stop(); this.unlockCache(); if (SoundResource.instance().syncUnlock()) { this.player.close(); } this.player = null; } debugOut(2, " closePlayer"); } @Override public Persister trigger(Event e, Persister seqID) { debugOut(9, "trigger in: seqID " + seqID + " activeSeq " + this.activeSeq); if (this.isBackgroundProcessing()) { return this.activeSeq; } else { if (seqID == null) { if (this.soundURL == null || this.getOwner() == null) { return null; } if (this.activeSeq != null) { this.closePlayer(); } this.activeSeq = this; Persister var3 = this.activeSeq; debugOut(6, "triggering new sound " + var3); this.startSound(); } else if (seqID != this.activeSeq) { this.unlockCache(); if (!this.isContinuous()) { this.aFilter.setEmitterOn(false); } return null; } if (this.player != null && this.player.getState() != 0 && !this.isContinuous()) { this.aFilter.setEmitterOn(false); } if ((this.isBackgroundProcessing() || this.updateSound() && this.player.getState() == 0) && this.getOwner() != null) { return this.activeSeq; } else { this.activeSeq = null; this.closePlayer(); return null; } } } boolean updateSound() { debugOut(9, "Sound::updateSound:: in"); if (this.player == null) { debugOut(6, "update returning false because player is null"); return false; } else if (!SoundResource.instance().isOK()) { debugOut(2, "update registering not OK"); return false; } else if (!this.aFilter.isAudible()) { debugOut(4, "update stopping sound because isAudible false"); return false; } else { Point3Temp pilot = Point3Temp.make(); Point3Temp up = Point3Temp.make(); Point3Temp forward = Point3Temp.make(); Point3Temp obj = Point3Temp.make(); this.aFilter.getListenerPosition(pilot, up, forward); this.aFilter.getEmitterPosition(obj); if (!this.player.position(pilot, obj, forward, up)) { debugOut(6, "update returning false because position false"); return false; } else { float vol = -1.0F; if (soundOn) { vol = this.aFilter.getEmitterVolume(); } if (!this.player.setVolume(vol * this.soundVolume)) { debugOut(6, "update returning false because volume false"); return false; } else { return true; } } } } @Override public void postRestore(int version) { super.postRestore(version); this.startAudibilityFilter(); } @Override public Object properties(int index, int offset, int mode, Object value) throws NoSuchPropertyException { Object ret = null; switch (index - offset) { case 0: if (mode == 0) { ret = URLPropertyEditor.make(new Property(this, index, "Sound File URL").allowSetNull(), "asf;wav;mid;ram;ra;rm;mp3"); } else if (mode == 1) { ret = this.soundURL; } else if (mode == 2) { this.soundURL = (URL)value; } break; case 1: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Max Volume")); } else if (mode == 1) { ret = new Float(this.soundVolume); } else if (mode == 2) { this.soundVolume = (Float)value; } break; case 2: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Stop Distance")); } else if (mode == 1) { ret = new Float(this.stopDistance); } else if (mode == 2) { this.stopDistance = (Float)value; } break; case 3: if (mode == 0) { ret = IntegerPropertyEditor.make(new Property(this, index, "Times to Repeat")); } else if (mode == 1) { ret = new Integer(this.repeat); } else if (mode == 2) { this.repeat = (Integer)value; } break; case 4: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Attenuate With Distance"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.attenuateOn); } else if (mode == 2) { this.attenuateOn = (Boolean)value; } break; case 5: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Do panning"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.panningOn); } else if (mode == 2) { this.panningOn = (Boolean)value; } break; case 6: if (mode == 0) { ret = IntegerPropertyEditor.make(new Property(this, index, "Room hops till sound dies")); } else if (mode == 1) { ret = new Integer(this.maxHopcount); } else if (mode == 2) { this.setHopcount((Integer)value); } break; case 7: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Door based audibility"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.getDoorMovementFlag()); } else if (mode == 2) { this.setDoorMovementFlag((Boolean)value); } break; case 8: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Play after loading"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.playAfterLoading); } else if (mode == 2) { this.setPlayAfterLoading((Boolean)value); } break; case 9: if (mode == 0) { ret = BooleanPropertyEditor.make(new Property(this, index, "Loop Infinite"), "No", "Yes"); } else if (mode == 1) { ret = new Boolean(this.continuous); } else if (mode == 2) { this.setContinuous((Boolean)value); } break; default: ret = super.properties(index, offset + 10, mode, value); } return ret; } @Override public void saveState(Saver s) throws IOException { s.saveVersion(7, classCookie); s.saveBoolean(this.playAfterLoading); s.saveBoolean(this.doorMovementFlag); s.saveBoolean(this.panningOn); s.saveBoolean(this.attenuateOn); s.saveInt(this.maxHopcount); super.saveState(s); URL.save(s, this.soundURL); s.saveFloat(this.soundVolume); s.saveFloat(this.stopDistance); s.saveInt(this.repeat); s.saveBoolean(this.continuous); } @Override public void restoreState(Restorer r) throws IOException, TooNewException { int vers = r.restoreVersion(classCookie); switch (vers) { case 0: this.setURL(URL.restore(r)); break; case 1: this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); if (this.repeat < 0) { this.setContinuous(true); } break; case 2: super.restoreState(r); this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); if (this.repeat < 0) { this.setContinuous(true); } break; case 3: this.setHopcount(r.restoreInt()); super.restoreState(r); this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); if (this.repeat < 0) { this.setContinuous(true); } break; case 4: this.setDoorMovementFlag(r.restoreBoolean()); this.setPanning(r.restoreBoolean()); this.setAttenuate(r.restoreBoolean()); this.setHopcount(r.restoreInt()); super.restoreState(r); this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); if (this.repeat < 0) { this.setContinuous(true); } break; case 5: this.setPlayAfterLoading(r.restoreBoolean()); this.setDoorMovementFlag(r.restoreBoolean()); this.setPanning(r.restoreBoolean()); this.setAttenuate(r.restoreBoolean()); this.setHopcount(r.restoreInt()); super.restoreState(r); this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); if (this.repeat < 0) { this.setContinuous(true); } break; case 6: this.setPlayAfterLoading(r.restoreBoolean()); this.setDoorMovementFlag(r.restoreBoolean()); this.setPanning(r.restoreBoolean()); this.setAttenuate(r.restoreBoolean()); this.setHopcount(r.restoreInt()); super.restoreState(r); this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); if (this.repeat < 0) { this.setContinuous(true); } break; case 7: this.setPlayAfterLoading(r.restoreBoolean()); this.setDoorMovementFlag(r.restoreBoolean()); this.setPanning(r.restoreBoolean()); this.setAttenuate(r.restoreBoolean()); this.setHopcount(r.restoreInt()); super.restoreState(r); this.setURL(URL.restore(r)); this.soundVolume = r.restoreFloat(); this.stopDistance = r.restoreFloat(); this.repeat = r.restoreInt(); this.setContinuous(r.restoreBoolean()); break; default: throw new TooNewException(); } if (vers < 6 && this.continuous) { this.setContinuous(false); } } }