/*
* 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.Executor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonreality.identifier.IIdentifier;
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.event.ACTREventDispatcher;
import org.jactr.core.event.IParameterEvent;
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.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.buffer.IAuralActivationBuffer;
import org.jactr.modules.pm.aural.buffer.IAuralLocationBuffer;
import org.jactr.modules.pm.aural.event.AuralModuleEvent;
import org.jactr.modules.pm.aural.event.IAuralModuleListener;
import org.jactr.modules.pm.aural.memory.IAuralMemory;
import org.jactr.modules.pm.common.event.IPerceptualMemoryModuleEvent;
import org.jactr.modules.pm.common.memory.IActivePerceptListener;
import org.jactr.modules.pm.common.memory.IPerceptualEncoder;
import org.jactr.modules.pm.common.memory.IPerceptualMemory;
import org.jactr.modules.pm.common.memory.map.IFINSTFeatureMap;
/**
* abstract implementation of the aural module that takes care of most of the
* details. It handles parameter, FINST management, and buffer stuffing. It is
* up to the extenders to deal with the actual attending, search, buffer and
* memory creation.
*
* @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 IAuralMemory _auralMemory;
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 TreeMap<IChunkType, Double> _recodeTimes;
private ACTREventDispatcher<IAuralModule, IAuralModuleListener> _listener;
private boolean _isBufferStuffEnabled = false;
private double _decayTime = 3;
private IAuralEncodingTimeEquation _encodingTime = new DefaultAuralEncodingTimeEquation();
/**
* @param name
*/
public AbstractAuralModule()
{
super("aural");
_deferredParameters = new TreeMap<String, String>();
_recodeTimes = new TreeMap<IChunkType, Double>();
_listener = new ACTREventDispatcher<IAuralModule, IAuralModuleListener>();
}
abstract protected IAuralMemory createAuralMemory();
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;
}
public void addListener(IAuralModuleListener listener, Executor executor)
{
_listener.addListener(listener, executor);
}
public void removeListener(IAuralModuleListener listener)
{
_listener.removeListener(listener);
}
public boolean hasListeners()
{
return _listener.hasListeners();
}
public void dispatch(AuralModuleEvent event)
{
if (_listener.hasListeners()) _listener.fire(event);
}
/**
* @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 = getAuralMemory().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);
_auralMemory = createAuralMemory();
applyParameters();
/*
* we also add a listener to ourselves to keep track of the finsts..
*/
IAuralModuleListener listener = new IAuralModuleListener() {
public void moduleReset(IPerceptualMemoryModuleEvent event)
{
// TODO Auto-generated method stub
}
public void perceptAttended(IPerceptualMemoryModuleEvent event)
{
/*
* flag it as attended
*/
IChunk auralChunk = event.getChunk();
IIdentifier identifier = (IIdentifier) auralChunk
.getMetaData(IPerceptualEncoder.COMMONREALITY_IDENTIFIER_META_KEY);
if (identifier != null)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("assigning finst for " + auralChunk);
getAuralMemory().getFINSTFeatureMap().flagAsAttended(identifier,
auralChunk, getAuralMemory().getFINSTSpan());
}
else if (LOGGER.isDebugEnabled())
LOGGER.debug("could not find identifier for " + auralChunk);
}
public void perceptIndexFound(IPerceptualMemoryModuleEvent event)
{
// TODO Auto-generated method stub
}
public void parameterChanged(IParameterEvent pe)
{
}
};
addListener(listener, ExecutorServices.INLINE_EXECUTOR);
/*
* flag as new..
*/
IActivePerceptListener finstListener = new IActivePerceptListener() {
public void newPercept(IIdentifier identifier, IChunk chunk)
{
// this is quick enough that we can process it in line
// and it may be needed in the search right now..
IFINSTFeatureMap finstMap = getAuralMemory().getFINSTFeatureMap();
if (finstMap != null && !finstMap.isAttended(identifier))
finstMap.flagAsNew(identifier, chunk, getAuralMemory()
.getNewFINSTOnsetDuration());
}
public void reencoded(IIdentifier identifier, IChunk oldChunk,
IChunk newChunk)
{
// noop
}
public void removed(IIdentifier identifier, IChunk chunk)
{
// noop
}
public void updated(IIdentifier identifier, IChunk chunk)
{
// noop
}
};
_auralMemory.addListener(finstListener, ExecutorServices.INLINE_EXECUTOR);
super.initialize();
}
@Override
protected void connectToCommonReality()
{
super.connectToCommonReality();
/*
* attach the visual memory
*/
_auralMemory.attach(ACTRRuntime.getRuntime().getConnector()
.getAgent(getModel()));
}
@Override
protected void disconnectFromCommonReality()
{
super.disconnectFromCommonReality();
_auralMemory.detach();
}
@Override
public void dispose()
{
getModel().removeListener(_modelListener);
super.dispose();
}
public boolean isBufferStuffEnabled()
{
return _isBufferStuffEnabled;
}
public IAuralMemory getAuralMemory()
{
return _auralMemory;
}
public IPerceptualMemory getPerceptualMemory()
{
return _auralMemory;
}
/**
* @see org.jactr.modules.pm.aural.IAuralModule#getAuralActivationBuffer()
*/
public IAuralActivationBuffer getAuralActivationBuffer()
{
return _auralBuffer;
}
/**
* @see org.jactr.modules.pm.aural.IAuralModule#getAuralLocationBuffer()
*/
public IAuralLocationBuffer getAuralLocationBuffer()
{
return _auralLocationBuffer;
}
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 (_auralMemory instanceof IParameterized)
for (Map.Entry<String, String> entry : _deferredParameters.entrySet())
((IParameterized) _auralMemory).setParameter(entry.getKey(), entry
.getValue());
}
@Override
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);
params.add(IPerceptualMemory.FINST_DURATION_TIME_PARAM);
params.add(IPerceptualMemory.NEW_FINST_ONSET_DURATION_TIME_PARAM);
params.add(IPerceptualMemory.NUMBER_OF_FINSTS_PARAM);
return params;
}
@Override
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,
IAuralModule auralModule)
{
return getRecodeTime(soundChunk.getSymbolicChunk().getChunkType());
}
};
}
else if (STRICT_SYNCHRONIZATION_PARAM.equalsIgnoreCase(key))
super.setParameter(key, value);
else
{
_deferredParameters.put(key, value);
if (_auralMemory != null) _auralMemory.setParameter(key, value);
}
}
@Override
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 if (STRICT_SYNCHRONIZATION_PARAM.equalsIgnoreCase(key))
return super.getParameter(key);
else
return _deferredParameters.get(key);
}
}