/* * Created on Jun 26, 2007 Copyright (C) 2001-2007, Anthony Harrison * anh23@pitt.edu (jactr.org) This library is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser General * Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more * details. You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.modules.pm.aural; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.Future; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunktype.IChunkType; import org.jactr.core.concurrent.ExecutorServices; import org.jactr.core.model.IModel; import org.jactr.core.model.event.IModelListener; import org.jactr.core.model.event.ModelEvent; import org.jactr.core.model.event.ModelListenerAdaptor; import org.jactr.core.module.IllegalModuleStateException; import org.jactr.core.module.declarative.IDeclarativeModule; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.utils.parameter.IParameterized; import org.jactr.core.utils.parameter.ParameterHandler; import org.jactr.modules.pm.AbstractPerceptualModule; import org.jactr.modules.pm.aural.audicon.IAudicon; import org.jactr.modules.pm.aural.buffer.IAuralActivationBuffer; import org.jactr.modules.pm.aural.buffer.IAuralLocationBuffer; import org.jactr.modules.pm.aural.delegate.AuralClearDelegate; import org.jactr.modules.pm.aural.delegate.AuralEncodingDelegate; import org.jactr.modules.pm.aural.delegate.AuralScanDelegate; /** * @author developer */ public abstract class AbstractAuralModule extends AbstractPerceptualModule implements IAuralModule, IParameterized { static public final String ENABLE_BUFFER_STUFF_PARAM = "EnableBufferStuff"; static public final String AURAL_DECAY_TIME_PARAM = "AuralDecayTime"; static public final String ENCODING_TIME_EQUATION_PARAM = "AuralEncodingTimeEquationClass"; /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(AbstractAuralModule.class); private IModelListener _modelListener; private IAuralActivationBuffer _auralBuffer; private IAuralLocationBuffer _auralLocationBuffer; private IAudicon _audicon; private Map<String, String> _deferredParameters; private IChunkType _soundChunkType; private IChunkType _audioEventChunkType; private IChunkType _clearChunkType; private IChunk _lowestChunk; private IChunk _highestChunk; private IChunk _externalChunk; private IChunk _internalChunk; private AuralEncodingDelegate _encodingDelegate; private AuralScanDelegate _scanDelegate; private AuralClearDelegate _clearDelegate; private TreeMap<IChunkType, Double> _recodeTimes; private boolean _isBufferStuffEnabled = false; private double _decayTime = 3; private IAuralEncodingTimeEquation _encodingTime = new IAuralEncodingTimeEquation() { public double computeEncodingTime( IChunk soundChunk) { return getRecodeTime(soundChunk .getSymbolicChunk() .getChunkType()); } }; /** * @param name */ public AbstractAuralModule() { super("aural"); _deferredParameters = new TreeMap<String, String>(); _recodeTimes = new TreeMap<IChunkType, Double>(); } abstract protected IAudicon createAudicon(); abstract protected IAuralLocationBuffer createAuralLocationBuffer(); abstract protected IAuralActivationBuffer createAuralBuffer( IAuralLocationBuffer locationBuffer); @Override protected Collection<IActivationBuffer> createBuffers() { _auralLocationBuffer = createAuralLocationBuffer(); _auralBuffer = createAuralBuffer(_auralLocationBuffer); ArrayList<IActivationBuffer> rtn = new ArrayList<IActivationBuffer>(); rtn.add(_auralLocationBuffer); rtn.add(_auralBuffer); return rtn; } /** * @see org.jactr.core.module.AbstractModule#initialize() */ @Override public void initialize() { _modelListener = new ModelListenerAdaptor() { double _lastStuffAttempt = -1; @Override public void cycleStarted(ModelEvent me) { if (isBufferStuffEnabled()) { double lastTime = getAudicon().getLastChangeTime(); if (lastTime > _lastStuffAttempt) { if (LOGGER.isDebugEnabled()) LOGGER.debug("attempting to stuff"); getAuralLocationBuffer().checkForBufferStuff(); _lastStuffAttempt = me.getSimulationTime(); } else if (LOGGER.isDebugEnabled()) LOGGER.debug("audicon has not changed since " + _lastStuffAttempt + " now: " + me.getSimulationTime()); } } }; IModel model = getModel(); model.addListener(_modelListener, ExecutorServices.INLINE_EXECUTOR); IDeclarativeModule decM = model.getDeclarativeModule(); if (decM == null) throw new IllegalModuleStateException( "declarative module is not available"); _soundChunkType = getNamedChunkType(SOUND_CHUNK_TYPE); _audioEventChunkType = getNamedChunkType(AUDIO_EVENT_CHUNK_TYPE); _clearChunkType = getNamedChunkType(CLEAR_CHUNK_TYPE); _highestChunk = getNamedChunk(HIGHEST_CHUNK); _lowestChunk = getNamedChunk(LOWEST_CHUNK); _internalChunk = getNamedChunk(INTERNAL_CHUNK); _externalChunk = getNamedChunk(EXTERNAL_CHUNK); setRecodeTime(getNamedChunkType(TONE_CHUNK_TYPE), 0.5); setRecodeTime(getNamedChunkType(DIGIT_CHUNK_TYPE), 0.285); setRecodeTime(getNamedChunkType(SPEECH_CHUNK_TYPE), 1); setRecodeTime(getNamedChunkType(WORD_CHUNK_TYPE), 0.3); _audicon = createAudicon(); _encodingDelegate = new AuralEncodingDelegate(this, model .getProceduralModule().getDefaultProductionFiringTime(), getNamedChunk("error")); _scanDelegate = new AuralScanDelegate(this, model.getProceduralModule() .getDefaultProductionFiringTime(), getNamedChunk("error")); _clearDelegate = new AuralClearDelegate(this, model.getProceduralModule() .getDefaultProductionFiringTime(), getNamedChunk("error")); super.initialize(); } @Override public void dispose() { _audicon.dispose(); _audicon = null; getModel().removeListener(_modelListener); super.dispose(); } public boolean isBufferStuffEnabled() { return _isBufferStuffEnabled; } /** * @see org.jactr.modules.pm.aural.IAuralModule#getAudicon() */ public IAudicon getAudicon() { return _audicon; } /** * @see org.jactr.modules.pm.aural.IAuralModule#getAuralBuffer() */ public IAuralActivationBuffer getAuralBuffer() { return _auralBuffer; } /** * @see org.jactr.modules.pm.aural.IAuralModule#getAuralLocationBuffer() */ public IAuralLocationBuffer getAuralLocationBuffer() { return _auralLocationBuffer; } /** * @see org.jactr.modules.pm.aural.IAuralModule#reset() */ public void reset() { if (_clearDelegate == null) throw new IllegalModuleStateException( "Cannot reset aural module until connected to common reality"); _clearDelegate.process(null, ACTRRuntime.getRuntime().getClock(getModel()).getTime(), (Object[]) null); // _auralBuffer.clear(); // _auralLocationBuffer.clear(); // _audicon.clear(); } public IAuralEncodingTimeEquation getEncodingTimeEquation() { return _encodingTime; } public IChunkType getClearChunkType() { return _clearChunkType; } public IChunkType getSoundChunkType() { return _soundChunkType; } public IChunkType getAudioEventChunkType() { return _audioEventChunkType; } public IChunk getLowestChunk() { return _lowestChunk; } public IChunk getHighestChunk() { return _highestChunk; } public IChunk getInternalChunk() { return _internalChunk; } public IChunk getExternalChunk() { return _externalChunk; } public double getRecodeTime(IChunkType chunkType) { Double rtn = _recodeTimes.get(chunkType); if (rtn != null) return rtn; if (LOGGER.isWarnEnabled()) LOGGER.warn("No recode time is defined for " + chunkType); return 0.5; } public void setRecodeTime(IChunkType chunkType, double time) { _recodeTimes.put(chunkType, time); } /** * @see org.jactr.modules.pm.aural.IAuralModule#getAuralDecayTime() */ public double getAuralDecayTime() { return _decayTime; } /** * @see org.jactr.modules.pm.aural.IAuralModule#setAuralDecayTime(double) */ public void setAuralDecayTime(double time) { _decayTime = time; } /** * apply the deferred parameters to the audicon */ protected void applyParameters() { if (_audicon instanceof IParameterized) for (Map.Entry<String, String> entry : _deferredParameters.entrySet()) ((IParameterized) _audicon).setParameter(entry.getKey(), entry .getValue()); } public Collection<String> getSetableParameters() { TreeSet<String> params = new TreeSet<String>(super.getSetableParameters()); params.add(AURAL_DECAY_TIME_PARAM); params.add(ENABLE_BUFFER_STUFF_PARAM); params.add(ENCODING_TIME_EQUATION_PARAM); return params; } public void setParameter(String key, String value) { if (AURAL_DECAY_TIME_PARAM.equalsIgnoreCase(key)) setAuralDecayTime(ParameterHandler.numberInstance().coerce(value) .doubleValue()); else if (ENABLE_BUFFER_STUFF_PARAM.equalsIgnoreCase(key)) _isBufferStuffEnabled = ParameterHandler.booleanInstance().coerce(value) .booleanValue(); else if (ENCODING_TIME_EQUATION_PARAM.equalsIgnoreCase(key)) try { Class clazz = getClass().getClassLoader().loadClass(value); _encodingTime = (IAuralEncodingTimeEquation) clazz.newInstance(); } catch (Exception e) { LOGGER.error("Could not load class " + value + " using default encoding time equation ", e); _encodingTime = new IAuralEncodingTimeEquation() { public double computeEncodingTime(IChunk soundChunk) { return getRecodeTime(soundChunk.getSymbolicChunk().getChunkType()); } }; } else super.setParameter(key, value); } public String getParameter(String key) { if(AURAL_DECAY_TIME_PARAM.equalsIgnoreCase(key)) return ""+getAuralDecayTime(); else if(ENABLE_BUFFER_STUFF_PARAM.equalsIgnoreCase(key)) return ""+isBufferStuffEnabled(); else if(ENCODING_TIME_EQUATION_PARAM.equalsIgnoreCase(key)) return ""+_encodingTime.getClass().getName(); else return super.getParameter(key); } /** * scan the audicon for an audio-event that matches the provided pattern * * @param request * @return */ public Future<IChunk> scanAuditoryField(ChunkTypeRequest request, double requestTime, boolean isStuffRequest) { if (_scanDelegate == null) throw new IllegalModuleStateException( "Can not scan until connected to common reality"); return _scanDelegate.process(request, requestTime, isStuffRequest); } /** * encode the sound represented by audioEvent * * @param audioEvent * @return */ public Future<IChunk> encodeAuditoryChunkAt(IChunk audioEvent, double requestTime) { if (_encodingDelegate == null) throw new IllegalModuleStateException( "Can not encode until connected to common reality"); return _encodingDelegate.process(null, requestTime, audioEvent); } }