Package de.maramuse.soundcomp.process

Source Code of de.maramuse.soundcomp.process.TimerMap$EventObject

package de.maramuse.soundcomp.process;

/*
* Copyright 2010 Jan Schmidt-Reinisch
*
* SoundComp - a sound processing library
*
* 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; in version 2.1
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/**
* This class keeps track of global timing. It is advanced by the sample advancer and
* allows any process element or set of these to register themselves for certain events at certain time stamps.
* When the time stamp is reached, the event is sent back to each affected process element.
* TODO This class lacks its C++ pendant.
*/
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import de.maramuse.soundcomp.util.NotImplementedException;

public class TimerMap implements Stateful {

  /**
   * An interface for events with an intrinsic timestamp.
   * Note that this is for simplifying test case purposes only. In "real life"
   * scenarios, precalculated timing rarely makes sense due to the possibility
   * to change tempo at any point in time as well as to react on external events.
   * Therefore marked as @deprecated
   */
  public static interface TimedEvent extends Event {
  /**
   * Returns the timestamp of the event
   *
   * @return the timestamp of the event
   */
  public double getTimestamp();

  }

  /**
   * An interface to mark all Events that may be sent from the global timer Typical Events may be "note on", "note off",
   * etc. Typical use would be constructing a state machine of multiple events, for providing input streams to envelope
   * generators etc.
   */
  public static interface Event {
  /**
   * Each Implementation has the knowledge how its Element wants to get notified
   *
   * @param processElement
   *          The process element that has to get notified
   */
  public void notifyElement(ProcessElement processElement);

  };

  /**
   * The container for the data stored for one event/element-set
   */
  private class EventObject {
  private Set<ProcessElement> receivers;
  private Event event;

  /** constructor for a 1-element-receiver set */
  private EventObject(Event event, ProcessElement receiver) {
    this.event=event;
    this.receivers=new HashSet<ProcessElement>();
    this.receivers.add(receiver);
  }

  /** constructor for a real receiver set */
  private EventObject(Event event, Set<ProcessElement> receivers) {
    this.event=event;
    this.receivers=receivers;
  }

  /**
   * notifies the process element that the event has occurred The event has the knowledge how to accomplish that
   */
  public void notifyReceiver() {
    if(event!=null){
    for(ProcessElement receiver:receivers)
      event.notifyElement(receiver);
    }
  }
  }

  Time time=new Time();
  Double nextTimeStamp;
  public TreeMap<Double, HashSet<EventObject>> eventsByTime=new TreeMap<Double, HashSet<EventObject>>();

  public HashMap<Event, Double> eventsByEvent=new HashMap<Event, Double>();

  /**
   * add a future event and its receiver at a certain timestamp; past events will silently be ignored.
   *
   * @param timestamp
   *          the timestamp at which the event shall get triggered
   * @param event
   *          the event that shall get triggered
   * @param element
   *          the element that is to notify of the event
   */
  public void addEvent(Double timestamp, Event event, ProcessElement element) {
  if(timestamp<time.getValue(StandardParameters.OUT.i))
    return; // past elements will not get added
  eventsByEvent.put(event, timestamp);
  HashSet<EventObject> events=eventsByTime.get(timestamp);
  if(events==null){
    events=new HashSet<EventObject>();
    eventsByTime.put(timestamp, events);
  }
  events.add(new EventObject(event, element));
  if(nextTimeStamp==null||timestamp<nextTimeStamp)
    nextTimeStamp=timestamp;
  }

  /**
   * add a future event and its receiver at a certain timestamp; past events will silently be ignored.
   *
   * @param timestamp
   *          the timestamp at which the event shall get triggered
   * @param event
   *          the event that shall get triggered
   * @param elements
   *          the set of elements that are to notify of the event
   */
  public void addEvent(Double timestamp, Event event, Set<ProcessElement> elements) {
  if(timestamp<time.getValue(StandardParameters.OUT.i))
    return; // past elements will not get added
  eventsByEvent.put(event, timestamp);
  HashSet<EventObject> events=eventsByTime.get(timestamp);
  if(events==null){
    events=new HashSet<EventObject>();
    eventsByTime.put(timestamp, events);
  }
  events.add(new EventObject(event, elements));
  if(nextTimeStamp==null||timestamp<nextTimeStamp)
    nextTimeStamp=timestamp;
  }

  /**
   * remove a single event that has earlier been added, independent of its timestamp (for the sake of completeness.
   * might rarely be needed)
   *
   * @param event
   *          the event to remove. None of its elements will be notified of reaching the timestamp
   */
  public void removeEvent(Event event) {
  Double timestamp=eventsByEvent.get(event);
  if(timestamp!=null){
    eventsByEvent.remove(event);
    HashSet<EventObject> events=eventsByTime.get(timestamp);
    if(events!=null)
    for(EventObject o:events)
      if(o.event==event){
      events.remove(o);
      break; // only one hit possible, so leave iterator, else would get ConcurrentModificationException
      }
  }
  }

  /**
   * remove all contained information, so you get a "virgin" TimerMap also clears the internal timer, so future events
   * have no relation to the past
   */
  public void clear() {
  eventsByEvent.clear();
  eventsByTime.clear();
  nextTimeStamp=null;
  time.reset();
  }

  @Override
  public void advanceOutput() {
  time.advanceOutput();
  }

  @Override
  public void advanceState() {
  boolean repeat=false;
  Double now=time.getValue(StandardParameters.OUT.i);
  // notify and remove all past events
  do{
    if(nextTimeStamp==null){
    if(!eventsByTime.isEmpty())
      nextTimeStamp=eventsByTime.firstKey();
    }
    repeat=false;
    if(nextTimeStamp!=null&&nextTimeStamp<now){
    Set<EventObject> eventObjects=eventsByTime.get(nextTimeStamp);
    for(EventObject obj:eventObjects){
      obj.notifyReceiver();
      eventsByEvent.remove(obj.event);
    }
    eventsByTime.remove(nextTimeStamp);
    nextTimeStamp=null;
    repeat=true;
    }
  }while(repeat);
  time.advanceState();
  }

  @Override
  public String getAbstractName() {
  return "$$$GlobalTimer$$$";
  }

  @Override
  public String getInstanceName() {
  return "$$$GlobalTimer$$$Instance";
  }

  @Override
  public void setAbstractName(String abstractName) {
  throw new NotImplementedException();
  }

  @Override
  public void setInstanceName(String instanceName) {
  throw new NotImplementedException();
  }

  @Override
  public long getNativeSpace() {
  // TODO This class has no native pendant yet, and does not implement ProcessElement.
  // When implemented, it must just return the current time at the only output
  return 0;
  }

  public double currentTime() {
  return time.getValue(StandardParameters.OUT.i);
  }
}
TOP

Related Classes of de.maramuse.soundcomp.process.TimerMap$EventObject

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.