changeset 0:3074a84ef81e

first import
author Fiore Martin <f.martin@qmul.ac.uk>
date Wed, 26 Aug 2015 16:16:53 +0100
parents
children 629262395647
files .classpath .project .settings/org.eclipse.jdt.core.prefs libs/beads.jar libs/jhapticgui.jar libs/jl1.0.1.jar libs/mp3spi1.9.4.jar libs/tritonus_share.jar license.txt native/DawHaptics/DawHaptics.sln native/DawHaptics/DawHaptics/Commands.cpp native/DawHaptics/DawHaptics/Commands.h native/DawHaptics/DawHaptics/DawHaptics.vcxproj native/DawHaptics/DawHaptics/DawHaptics.vcxproj.filters native/DawHaptics/DawHaptics/DawHaptics.vcxproj.user native/DawHaptics/DawHaptics/Graph.cpp native/DawHaptics/DawHaptics/Graph.h native/DawHaptics/DawHaptics/HapticObjects.h native/DawHaptics/DawHaptics/HapticScene.h native/DawHaptics/DawHaptics/Message.cpp native/DawHaptics/DawHaptics/Message.h native/DawHaptics/DawHaptics/PhantomScene.cpp native/DawHaptics/DawHaptics/PhantomScene.h native/DawHaptics/DawHaptics/stdafx.cpp native/DawHaptics/DawHaptics/stdafx.h native/DawHaptics/DawHaptics/targetver.h native/DawHaptics/DawHaptics/uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.cpp native/DawHaptics/DawHaptics/uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.h native/DawHaptics/DawHaptics/utils.cpp native/DawHaptics/DawHaptics/utils.h src/uk/ac/qmul/eecs/depic/daw/AudioLoader.java src/uk/ac/qmul/eecs/depic/daw/Automation.java src/uk/ac/qmul/eecs/depic/daw/AutomationValue.java src/uk/ac/qmul/eecs/depic/daw/Chunk.java src/uk/ac/qmul/eecs/depic/daw/Clip.java src/uk/ac/qmul/eecs/depic/daw/ClipList.java src/uk/ac/qmul/eecs/depic/daw/Daw.java src/uk/ac/qmul/eecs/depic/daw/DbWave.java src/uk/ac/qmul/eecs/depic/daw/Direction.java src/uk/ac/qmul/eecs/depic/daw/Parameter.java src/uk/ac/qmul/eecs/depic/daw/ParametersControl.java src/uk/ac/qmul/eecs/depic/daw/Sample.java src/uk/ac/qmul/eecs/depic/daw/Selection.java src/uk/ac/qmul/eecs/depic/daw/Sonification.java src/uk/ac/qmul/eecs/depic/daw/Sound.java src/uk/ac/qmul/eecs/depic/daw/SoundEngineFactory.java src/uk/ac/qmul/eecs/depic/daw/SoundType.java src/uk/ac/qmul/eecs/depic/daw/SoundWave.java src/uk/ac/qmul/eecs/depic/daw/SoundWaveEditor.java src/uk/ac/qmul/eecs/depic/daw/SoundWaveEvent.java src/uk/ac/qmul/eecs/depic/daw/SoundWaveListener.java src/uk/ac/qmul/eecs/depic/daw/Wave.java src/uk/ac/qmul/eecs/depic/daw/WavePeaks.java src/uk/ac/qmul/eecs/depic/daw/beads/BeadsAutomation.java src/uk/ac/qmul/eecs/depic/daw/beads/BeadsParametersControl.java src/uk/ac/qmul/eecs/depic/daw/beads/BeadsSampleWrapper.java src/uk/ac/qmul/eecs/depic/daw/beads/BeadsSoundEngineFactory.java src/uk/ac/qmul/eecs/depic/daw/beads/BeadsSoundWave.java src/uk/ac/qmul/eecs/depic/daw/beads/BeadsTransportControl.java src/uk/ac/qmul/eecs/depic/daw/beads/GainParameter.java src/uk/ac/qmul/eecs/depic/daw/beads/GranularPlayer.java src/uk/ac/qmul/eecs/depic/daw/beads/PanParameter.java src/uk/ac/qmul/eecs/depic/daw/beads/SamplePlayer.java src/uk/ac/qmul/eecs/depic/daw/beads/ZoomUGen.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/AutomationSound.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsAutomationMapping.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsPeakLevelMapping.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsSequenceMapping.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsSonification.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/Buzz.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/PitchedUGen.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/SonificationPrefsPanel.java src/uk/ac/qmul/eecs/depic/daw/beads/sonification/error.mp3 src/uk/ac/qmul/eecs/depic/daw/beads/sonification/loop.mp3 src/uk/ac/qmul/eecs/depic/daw/beads/sonification/metallic.wav src/uk/ac/qmul/eecs/depic/daw/beads/sonification/ok.mp3 src/uk/ac/qmul/eecs/depic/daw/beads/sonification/pop.mp3 src/uk/ac/qmul/eecs/depic/daw/beads/sonification/selection_borders.wav src/uk/ac/qmul/eecs/depic/daw/beads/sonification/zero.wav src/uk/ac/qmul/eecs/depic/daw/gui/ArrangeWindow.java src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrack.java src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackInput.java src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackParameters.java src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackSonification.java src/uk/ac/qmul/eecs/depic/daw/gui/AutomationGraphActions.java src/uk/ac/qmul/eecs/depic/daw/gui/MainFrame.java src/uk/ac/qmul/eecs/depic/daw/gui/PreferencesDialog.java src/uk/ac/qmul/eecs/depic/daw/gui/PreferencesPanel.java src/uk/ac/qmul/eecs/depic/daw/gui/Rule.java src/uk/ac/qmul/eecs/depic/daw/gui/SequenceGraph.java src/uk/ac/qmul/eecs/depic/daw/gui/SequencePoint.java src/uk/ac/qmul/eecs/depic/daw/gui/actions/Actions.java src/uk/ac/qmul/eecs/depic/daw/gui/actions/EditActions.java src/uk/ac/qmul/eecs/depic/daw/gui/actions/MenuAction.java src/uk/ac/qmul/eecs/depic/daw/gui/actions/MenuAction.properties src/uk/ac/qmul/eecs/depic/daw/gui/actions/TransportControlActions.java src/uk/ac/qmul/eecs/depic/daw/gui/actions/UniqueHapticTrigger.java src/uk/ac/qmul/eecs/depic/daw/gui/actions/fwd.png src/uk/ac/qmul/eecs/depic/daw/gui/actions/loop.png src/uk/ac/qmul/eecs/depic/daw/gui/actions/play.png src/uk/ac/qmul/eecs/depic/daw/gui/actions/rew.png src/uk/ac/qmul/eecs/depic/daw/gui/actions/stop.png src/uk/ac/qmul/eecs/depic/daw/haptics/DawHapticListener.java src/uk/ac/qmul/eecs/depic/daw/haptics/DawHaptics.dll src/uk/ac/qmul/eecs/depic/daw/haptics/HapticTrigger.java src/uk/ac/qmul/eecs/depic/daw/haptics/HapticViewPort.java src/uk/ac/qmul/eecs/depic/daw/haptics/HaptificationPrefsPanel.java src/uk/ac/qmul/eecs/depic/daw/referencesound/BeadsSequenceMapping.java src/uk/ac/qmul/eecs/depic/daw/referencesound/BeadsWaveTableSequenceMapping.java src/uk/ac/qmul/eecs/depic/daw/referencesound/GridSound.java src/uk/ac/qmul/eecs/depic/daw/referencesound/PitchedUGen.java src/uk/ac/qmul/eecs/depic/patterns/MathUtils.java src/uk/ac/qmul/eecs/depic/patterns/Range.java src/uk/ac/qmul/eecs/depic/patterns/Sequence.java src/uk/ac/qmul/eecs/depic/patterns/SequenceEvent.java src/uk/ac/qmul/eecs/depic/patterns/SequenceListener.java src/uk/ac/qmul/eecs/depic/patterns/SequenceMapping.java test/uk/ac/qmul/eecs/depic/daw/test/AllTests.java test/uk/ac/qmul/eecs/depic/daw/test/AudioFileLoader.java test/uk/ac/qmul/eecs/depic/daw/test/AudioTrackTest.java test/uk/ac/qmul/eecs/depic/daw/test/ClipListTest.java test/uk/ac/qmul/eecs/depic/daw/test/ClipTest.java test/uk/ac/qmul/eecs/depic/daw/test/DummySoundWave.java test/uk/ac/qmul/eecs/depic/daw/test/MathUtilsTest.java test/uk/ac/qmul/eecs/depic/daw/test/SoundWaveChunkTest.java test/uk/ac/qmul/eecs/depic/daw/test/audio/sound1.wav test/uk/ac/qmul/eecs/depic/daw/test/audio/sound2.wav
diffstat 127 files changed, 16691 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.classpath	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="uk/ac/qmul/eecs/depic/daw/haptics/HapticWaveTrigger.java" kind="src" path="src"/>
+	<classpathentry kind="src" path="test"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+	<classpathentry kind="lib" path="libs/beads.jar"/>
+	<classpathentry kind="lib" path="libs/jl1.0.1.jar"/>
+	<classpathentry kind="lib" path="libs/mp3spi1.9.4.jar"/>
+	<classpathentry kind="lib" path="libs/tritonus_share.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="lib" path="libs/jhapticgui.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.project	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>Daw</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.settings/org.eclipse.jdt.core.prefs	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
Binary file libs/beads.jar has changed
Binary file libs/jhapticgui.jar has changed
Binary file libs/jl1.0.1.jar has changed
Binary file libs/mp3spi1.9.4.jar has changed
Binary file libs/tritonus_share.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/license.txt	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics.sln	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2013 for Windows Desktop
+VisualStudioVersion = 12.0.21005.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DawHaptics", "DawHaptics\DawHaptics.vcxproj", "{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Debug|Win32.ActiveCfg = Debug|Win32
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Debug|Win32.Build.0 = Debug|Win32
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Debug|x64.ActiveCfg = Debug|x64
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Debug|x64.Build.0 = Debug|x64
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Release|Win32.ActiveCfg = Release|Win32
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Release|Win32.Build.0 = Release|Win32
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Release|x64.ActiveCfg = Release|x64
+		{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/Commands.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,51 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "Commands.h"
+
+
+const char* jhapticgui::InCommands::DISPLAY_SEQUENCE = "display.seq";
+const char* jhapticgui::InCommands::SEQUENCE_VALUE_ADD = "seq.value.add";
+const char* jhapticgui::InCommands::SEQUENCE_VALUE_CHANGE = "seq.value.change";
+const char* jhapticgui::InCommands::SEQUENCE_VALUE_REMOVE  = "seq.value.rem";
+const char* jhapticgui::InCommands::SEQUENCE_VALUE_FIND  = "seq.value.find";
+const char* jhapticgui::InCommands::SEQUENCE_TRANSLATE  = "seq.translate";
+const char* jhapticgui::InCommands::SEQUENCE_SHIFT = "seq.shift";
+const char* jhapticgui::InCommands::SEQUENCE_BEGIN = "seq.begin";
+
+const char* jhapticgui::InCommands::DISPLAY_RENDER_VALUE = "display.value";
+const char* jhapticgui::InCommands::RENDER_VALUE  = "render_value";
+const char* jhapticgui::InCommands::DISPLAY_PEAKS = "display.peaks";
+const char* jhapticgui::InCommands::DISPLAY_NONE = "display.none";
+const char* jhapticgui::InCommands::ROTATE_Z = "rotate.z";
+const char* jhapticgui::InCommands::ROTATE_Y = "rotate.y";
+const char* jhapticgui::InCommands::ROTATE_X = "rotate.x";
+
+const char* jhapticgui::OutCommands::SCRUB_ON = "scrub.on";
+const char* jhapticgui::OutCommands::SCRUB_OFF = "scrub.off";
+const char* jhapticgui::OutCommands::SCRUB_SHIFT = "scrub.shift";
+
+const char* jhapticgui::OutCommands::ADD_SEQ_VAL = "seq.val.add";
+const char* jhapticgui::OutCommands::REM_SEQ_VAL = "seq.val.rem" ;
+const char* jhapticgui::OutCommands::MOVE_SEQ_VAL= "seq.val.move";
+const char* jhapticgui::OutCommands::PICK_SEQ_VAL = "seq.val.pick"; 
+const char* jhapticgui::OutCommands::DROP_SEQ_VAL = "seq.val.drop"; 
+
+
+const char* jhapticgui::OutCommands::LINE_TOUCHED = "line.touched";
+const char* jhapticgui::OutCommands::POINT_TOUCHED = "point.touched";
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/Commands.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,56 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+namespace jhapticgui { 
+	struct InCommands {
+			static const char* DISPLAY_SEQUENCE;
+			static const char* SEQUENCE_BEGIN;
+			static const char* SEQUENCE_VALUE_ADD;
+			static const char* SEQUENCE_VALUE_CHANGE;
+			static const char* SEQUENCE_VALUE_REMOVE;
+			static const char* SEQUENCE_VALUE_FIND;
+			static const char* SEQUENCE_TRANSLATE;
+			static const char* SEQUENCE_SHIFT;
+			static const char* DISPLAY_RENDER_VALUE;
+			static const char* RENDER_VALUE;
+			static const char* ROTATE_Z;
+			static const char* ROTATE_Y;
+			static const char* ROTATE_X;
+			static const char* DISPLAY_NONE;
+			static const char* DISPLAY_PEAKS;
+			};
+
+	struct OutCommands {
+		static const char* SCRUB_ON ;
+		static const char* SCRUB_OFF ;	
+		static const char* SCRUB_SHIFT;
+
+		static const char* ADD_SEQ_VAL;
+		static const char* REM_SEQ_VAL;
+		static const char* PICK_SEQ_VAL; 
+		static const char* MOVE_SEQ_VAL; 
+		static const char* DROP_SEQ_VAL; 
+
+		static const char* LINE_TOUCHED;
+		static const char* POINT_TOUCHED;
+	};
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/DawHaptics.vcxproj	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{63CB2DAE-8F26-45B9-A4CD-4D7335BFC501}</ProjectGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <Keyword>ManagedCProj</Keyword>
+    <RootNamespace>DawHaptics</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CLRSupport>true</CLRSupport>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CLRSupport>true</CLRSupport>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <CLRSupport>true</CLRSupport>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <CLRSupport>true</CLRSupport>
+    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>..\..\..\bin\uk\ac\qmul\eecs\depic\daw\haptics\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>..\..\..\bin\uk\ac\qmul\eecs\depic\daw\haptics\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>..\..\..\bin\uk\ac\qmul\eecs\depic\daw\haptics\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>..\..\..\bin\uk\ac\qmul\eecs\depic\daw\haptics\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Program Files\Java\jdk1.7.0_51\include;C:\Program Files\Java\jdk1.7.0_51\include\win32;C:\H3D\HAPI\include;C:\H3D\H3DUtil\include;C:\H3D\External\include;C:\H3D\External\include\pthread;C:\H3D\HAPI\OpenHapticsRenderer\include;C:\Program Files\SensAble\3DTouch\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>HAPI_vc10.lib;H3DUtil_vc10.lib;OpenHapticsRenderer_vc10.lib;pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\H3D\lib32;C:\H3D\External\lib32</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Program Files\Java\jdk1.7.0_51\include;C:\Program Files\Java\jdk1.7.0_51\include\win32;C:\H3D\HAPI\include;C:\H3D\H3DUtil\include;C:\H3D\External\include;C:\H3D\External\include\pthread;C:\H3D\HAPI\OpenHapticsRenderer\include;C:\Program Files\SensAble\3DTouch\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>HAPI_vc10.lib;H3DUtil_vc10.lib;OpenHapticsRenderer_vc10.lib;pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\H3D\lib32;C:\H3D\External\lib32</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Program Files\Java\jdk1.7.0_51\include;C:\Program Files\Java\jdk1.7.0_51\include\win32;C:\H3D\HAPI\include;C:\H3D\H3DUtil\include;C:\H3D\External\include;C:\H3D\External\include\pthread;C:\H3D\HAPI\OpenHapticsRenderer\include;C:\Program Files (x86)\Novint\HDAL_SDK_2.1.3\include;C:\Program Files\SensAble\3DTouch\include;C:\Program Files\SensAble\3DTouch\utilities\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>glut32.lib;hl.lib;hd.lib;hlu.lib;hdu.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\H3D\lib32;C:\H3D\External\lib32;C:\Program Files\SensAble\3DTouch\lib\x64;C:\Program Files\SensAble\3DTouch\utilities\lib\x64\Release</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32;C:\Program Files (x86)\Novint\HDAL_SDK_2.1.3\include;C:\Program Files\SensAble\3DTouch\include;C:\Program Files\SensAble\3DTouch\utilities\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>glut32.lib;hl.lib;hd.lib;hlu.lib;hdu.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\Program Files\SensAble\3DTouch\lib\x64;C:\Program Files\SensAble\3DTouch\utilities\lib\x64\Release</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="Commands.h" />
+    <ClInclude Include="Graph.h" />
+    <ClInclude Include="HapticObjects.h" />
+    <ClInclude Include="HapticScene.h" />
+    <ClInclude Include="Message.h" />
+    <ClInclude Include="PhantomScene.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+    <ClInclude Include="uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.h" />
+    <ClInclude Include="utils.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Commands.cpp" />
+    <ClCompile Include="Graph.cpp" />
+    <ClCompile Include="Message.cpp" />
+    <ClCompile Include="PhantomScene.cpp" />
+    <ClCompile Include="stdafx.cpp" />
+    <ClCompile Include="uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.cpp" />
+    <ClCompile Include="utils.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/DawHaptics.vcxproj.filters	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="HapticScene.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Message.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="stdafx.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="targetver.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Commands.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="PhantomScene.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="utils.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Graph.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="HapticObjects.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Message.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Commands.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="PhantomScene.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="utils.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Graph.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/DawHaptics.vcxproj.user	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/Graph.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,139 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "StdAfx.h"
+#include "Graph.h"
+
+
+namespace jhapticgui { 
+	Graph::Graph(HLdouble startX, HLdouble endX, HLdouble  y, float width, float viewPort) :
+		initialYValue(y), widthMs(width), viewPortMs(viewPort) {
+	
+	start = new HPoint(0);
+	start->setCoord(startX, y);
+
+	end = new HPoint(0);
+	end->setCoord(endX, y);
+
+}
+
+Graph::~Graph(void){
+	while(!points.empty()){
+		delete points.back();
+		points.pop_back();
+	}
+
+	delete start;
+	delete end; 
+}
+
+Graph::Graph(const Graph & aGraph) { std::cout << "WARNING COPY CONSTRUCTOR CALLED " << std::endl; }
+
+const HPoint &  Graph::addPoint(long hashCode, float x, float y){
+	/* takes into account the viewport shift when adding the point */
+	HPoint* p = new HPoint(hlGenShapes(1), x, y);
+	p->setHashCode(hashCode);
+
+	/* ordered insert into the vector. look for the position where to insert. */
+	std::vector<HPoint*>::iterator indexItr;
+	for(indexItr = points.begin();indexItr != points.end(); indexItr++){
+		if(compareX(*p, *(*indexItr)) < 0){ // move forward until the righ position is found 
+			break; 
+		}
+	}
+
+	/* insert the new point */
+	points.insert(indexItr,p);
+
+	/* find the neighbours of the new point */
+	HPoint* prev = start;
+	HPoint* next = end;
+	unsigned int pointsSize = points.size();
+	for(unsigned int i=0; i<pointsSize;i++){
+		if(points[i] == p){
+			if(i>0)
+				prev = points[i-1];
+			if(i<pointsSize-1)
+				next = points[i+1];
+			break;
+		}
+	}
+
+	return *p;
+}
+
+void Graph::changePoint(long hashCode, float x, float y){
+	for (std::vector<HPoint*>::iterator itr = points.begin(); itr != points.end(); itr++){
+		if((*itr)->getHashCode() == hashCode){
+			(*itr)->setCoord(x,y);
+			break;
+		}
+	}
+}
+
+void Graph::removePoint(long hashCode){
+	HPoint* pointToRemove = NULL;
+	/* look for the point to remove */
+	for (std::vector<HPoint*>::iterator itr = points.begin(); itr != points.end(); itr++){
+		if((*itr)->getHashCode() == hashCode){
+			pointToRemove = (*itr);
+			points.erase(itr);
+			break;
+		}
+	}
+	
+	if(pointToRemove == NULL){ // FIXME must give feedback
+		return;
+	}
+
+	delete pointToRemove;
+}
+
+int Graph::compareX(const HPoint & p1, const HPoint & p2) {
+	if( p1.getX()  < p2.getX() )
+		return -1;
+	else if(p1.getX() > p2.getX() )
+		return 1;
+	else return 0;
+}
+
+void Graph::translate( float x, float y) {
+	for (std::vector<HPoint*>::iterator itr = points.begin(); itr != points.end(); itr++) {
+		HPoint* p = (*itr);
+		/* set new coordinate for this point */
+		(*itr)->setCoord(static_cast<float>(p->getX()+x),static_cast<float>(p->getY()+y));
+	}
+}
+
+const HPoint & Graph::getStart() const {
+	return *start;
+}
+
+const HPoint & Graph::getEnd() const {
+	return *end;
+}
+
+void Graph::setStart(HDdouble startY){
+	start->setCoord(start->getX(), startY);
+}
+
+const std::vector<HPoint*> & Graph::getPoints(){
+	return points;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/Graph.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,79 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include "HapticObjects.h"
+
+namespace jhapticgui { 
+
+/**
+ * A Data structure containing all the data to represent a graph either visually or haptically. 
+ * The graph also encompasses the concept of viewport, which is the portion of the graph which is visible 
+ * or touchable when its dimension exceeds the scope of the screen and haptic device.
+ */
+class Graph{
+	float initialYValue;
+	float widthMs; 
+	float viewPortMs;
+	/* hidden copy constructor */
+	Graph(const Graph & aGraph);	
+
+	HPoint *start;
+	HPoint *end;
+public :
+	
+	std::vector<HPoint*> points;
+	
+	Graph(HLdouble startX, HLdouble endX, HLdouble  y, float width, float viewPort);
+	~Graph(void);
+
+	const HPoint &  addPoint(long hashCode, float x, float y);
+	void changePoint(long hashCode, float x, float y);
+	void removePoint(long hashCode);
+
+	const HPoint & getStart() const;
+
+	const HPoint & getEnd() const;
+
+	const std::vector<HPoint*> & getPoints();
+
+	void setStart(double startX);
+
+	void translate(float x, float y);
+
+	static int compareX(const HPoint & p1, const HPoint & p2);
+	
+	inline float getInitialYValue(){
+		return initialYValue;
+	}
+
+	inline float getWidthMs(){
+		return widthMs;
+	}
+
+	inline float getViewPortMs(){
+		return viewPortMs;
+	}
+
+};
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/HapticObjects.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,94 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+#include <GL/glut.h>
+#include <HL/hl.h>
+#include <HLU/hlu.h>
+
+class HPoint {
+	HLuint hapticID;
+	HDdouble x, y;
+	GLuint displayList;
+	int hashCode;
+
+public:
+	
+	HPoint(HLuint hID) : hapticID(hID), x(0), y(0) { }
+	HPoint(HLuint hID, HDdouble _x, HDdouble _y) : hapticID(hID), x(_x), y(_y) { }
+	//Point(HLuint id, HDdouble _x, HDdouble _y) : hapticID(id), x(_x), y(_y) {}
+
+	~HPoint(){
+		hlDeleteShapes(hapticID, 1);
+	}
+
+	inline void setHashCode(int hash){
+		hashCode = hash;
+	}
+
+	inline const int getHashCode() const{
+		return hashCode;
+	}
+
+	inline void setCoord(HDdouble _x, HDdouble _y){
+		x = _x; y = _y;
+	}
+
+	inline HDdouble getX() const {
+		return x;
+	}
+
+	const HLuint getHapticID() const {
+		return hapticID;
+	}
+
+	inline HDdouble getY() const {
+		return y;
+	}
+
+};
+
+struct HLine {
+
+	HLine() : hapticID(hlGenShapes(1)) {}
+	~HLine(){
+		hlDeleteShapes(hapticID, 1);
+	}
+
+	HLuint getID() const {
+		return hapticID;
+	}
+
+	inline void setHashCode(int hash){
+		hashCode = hash;
+	}
+
+	inline const int getHashCode() const{
+		return hashCode;
+	}
+
+	HDdouble X1;
+	HDdouble Y1;
+	HDdouble X2;
+	HDdouble Y2;
+	int hashCode;
+private :
+	HLuint hapticID;
+
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/HapticScene.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,94 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+
+#include "Message.h"
+#include <bitset>
+
+
+namespace jhapticgui {
+
+	class HapticScene {
+		
+		int mWidth;
+		int mHeight;
+
+	protected:
+		void (*send) (const Message &m);
+
+		
+		
+		double mCursorScale;
+		double mWorldScale;
+	public:
+		
+		inline int getWidth() {
+			return mWidth;
+		}
+
+		inline int getHeight() {
+			return mHeight;
+		}
+
+		void setWorldScale(double scale){
+			mWorldScale = scale;
+		}
+
+		void setCursorScale(double scale){
+			mCursorScale = scale;
+		}
+
+		HapticScene(void (*messageCallback) (const Message & m) ) : send(messageCallback) {} 
+
+		virtual ~HapticScene(void){}
+
+		/* try and initialize the haptic device, return false if the initialization fails */
+		virtual bool initHaptics(void) = 0;
+		inline virtual void setSize(int w, int h){
+			mWidth = w;
+			mHeight = h;
+		}
+
+		virtual unsigned int processMessage(const Message & m) = 0;
+
+		virtual void beginFrame(void) = 0;
+		/* drawing routines (both graphically and haptically) */
+		virtual void drawCursor(void) = 0;
+
+		virtual void updateWorkspace(void) = 0;
+
+		/*
+		  Draws one frame both in graphics and haptics.
+		 
+		  messageUpdate and callbacksUpdate return are integer codes defining what needs to be updated 
+		  in the haptic and graphic scenes. The values that must be passed to this method is returned  
+		  by precessMessage.
+		 */
+		virtual void drawScene(unsigned int messageUpdate, unsigned int callbacksUpdate) = 0;
+
+		virtual void endFrame(void) = 0;
+
+		virtual unsigned int checkCallbacks(void) = 0;
+
+		virtual void initGL(void) = 0;
+
+	};
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/Message.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,43 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "stdafx.h"
+#include "Message.h"
+
+jhapticgui::Message::Message(){
+	ID = 0;
+	std::fill(command,command+MAX_CMD_LEN,0);
+	std::fill(args,args+MAX_ARGS_LEN,0);
+}
+
+jhapticgui::Message::Message(const char* c, const char* a, long id){
+	ID = id;
+	for(int i = 0; i<MAX_CMD_LEN ;i++){
+		command[i] = c[i];
+		if(c[i] == '\0')
+			break;
+	}
+
+	for(int i = 0; i<MAX_ARGS_LEN ;i++){
+		args[i] = a[i];
+		if(a[i] == '\0')
+			break;
+	}
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/Message.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,39 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+namespace jhapticgui {
+
+	struct Message {
+		static const int MAX_CMD_LEN = 32;
+		static const int MAX_ARGS_LEN = 64;
+
+		long ID;
+		char command[MAX_CMD_LEN];
+		char args[MAX_ARGS_LEN];
+		
+		Message();
+
+		Message(const char* c, const char* a, long id);
+
+		
+
+	};
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/PhantomScene.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,1136 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "StdAfx.h"
+#include "PhantomScene.h"
+#include "Commands.h"
+
+
+#if defined(WIN32) || defined(linux)
+#include <GL/glut.h>
+#elif defined(__APPLE__)
+#include <GLUT/glut.h>
+#endif
+
+
+#if defined(WIN32)
+#include <windows.h>
+#endif
+
+#include <string.h>
+
+
+void jhapticgui::PhantomScene::drawPoint(const HPoint& p, float snappiness){
+	hlPushAttrib(HL_HINT_BIT);
+	hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 3);
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, p.getHapticID());
+	hlTouchModel(HL_CONSTRAINT);
+	hlTouchModelf(HL_SNAP_DISTANCE, snappiness);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1.0);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_DAMPING, 0);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_DYNAMIC_FRICTION, 0);
+
+	//draw the point
+	glBegin(GL_POINTS);
+	glVertex3d(p.getX(), p.getY(), 0);
+	glEnd();
+
+	// End the shape.
+	hlEndShape();
+	hlPopAttrib();
+}
+
+void jhapticgui::PhantomScene::drawLine(const HLine& l, float snappiness){
+	hlPushAttrib(HL_HINT_BIT | HL_MATERIAL_BIT);
+	hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 1);
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER,l.getID());
+	hlTouchModel(HL_CONSTRAINT);
+	hlTouchModelf(HL_SNAP_DISTANCE, (snappiness));
+	hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0.0f /*0.5f*/);
+	glLineWidth(1.0);
+
+	glBegin(GL_LINES);
+
+	glVertex3d(l.X2, l.Y2, 0);
+	glVertex3d(l.X1, l.Y1, 0);
+
+	glEnd();
+	hlEndShape();
+	hlPopAttrib();
+}
+
+void jhapticgui::PhantomScene::drawSceneGraphics() {
+	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);
+	glEnable(GL_COLOR_MATERIAL);
+
+
+
+	/* draw the vertical line unless it's display none state */
+	if (state.display == DISPLAY_VALUE_MODE){
+		//draw vertical line
+		glColor3f(1.0, 0.0, 0.0); // red
+		glLineWidth(2.0);
+		glPushAttrib(GL_ENABLE_BIT);
+		 glDisable(GL_LIGHTING);
+		 glEnable(GL_COLOR_MATERIAL);
+
+
+		 glBegin(GL_LINES);
+		 glVertex3d(hLine->X1, hLine->Y1, 0);
+		 glVertex3d(hLine->X2, hLine->Y2, 0);
+		 glEnd();
+		glPopAttrib();
+	}
+	else if (state.display == DISPLAY_GRAPH_MODE) {
+		//draw vertical line
+		glColor3f(1.0, 1.0, 0.0); // red
+		glLineWidth(2.0);
+		glPushAttrib(GL_ENABLE_BIT);
+		 glDisable(GL_LIGHTING);
+		 glEnable(GL_COLOR_MATERIAL);
+
+
+		 glBegin(GL_LINE_STRIP);
+		  glVertex3d(graph->getStart().getX(), graph->getStart().getY(), 0);
+		  const HPoint* lastPoint = nullptr;
+		  for (auto& p : graph->getPoints()){
+		     glVertex3d(p->getX(), p->getY(), 0);
+			 lastPoint = p;
+		  }
+
+		  /* the Y of the end of the automation follows the last point or the begin if no points exist */
+		  if (lastPoint == nullptr){
+			  glVertex3d(graph->getEnd().getX(), graph->getStart().getY(), 0);
+		  }
+		  else{
+			  glVertex3d(graph->getEnd().getX(), lastPoint->getY(), 0);
+		  }
+		  
+		 glEnd(); // GL_LINES
+		glPopAttrib();
+
+
+
+		glColor3f(1.0, 1.0, 1.0); // white
+		double dPointScale = 10 * mWorldScale;
+
+		for (auto& p : graph->getPoints()){
+			glPushMatrix();
+			static bool once = false;
+			if (!once){
+				once = true;
+			}
+			glTranslated(p->getX(), p->getY(), 0);
+			glScaled(dPointScale, dPointScale, dPointScale);
+			glutSolidSphere(0.5, 10, 10);
+			glPopMatrix();
+		}
+
+	}
+	else if (state.display == DISPLAY_EDIT_GRID_MODE){
+		glColor3f(1.0, 0.0, 0.0); // red
+		glLineWidth(2.0);
+		glPushAttrib(GL_ENABLE_BIT);
+		glDisable(GL_LIGHTING);
+		glEnable(GL_COLOR_MATERIAL);
+
+
+		glBegin(GL_LINES);
+		
+		//draw vertical line
+		glVertex3d(grid.verticalLine->X1, grid.verticalLine->Y1, 0);
+		glVertex3d(grid.verticalLine->X2, grid.verticalLine->Y2, 0);
+
+		/* draw grid */
+		for (auto& p : grid.lines){
+			glVertex3d(p->X1, p->Y1, 0);
+			glVertex3d(p->X2, p->Y2, 0);
+		}
+		glEnd();
+		glPopAttrib();
+
+		/* draw points on the vertical line of the grid */
+		glColor3f(1.0, 1.0, 1.0); // white
+		double dPointScale = 10 * mWorldScale;
+
+		for (auto& p : grid.points){
+			glPushMatrix();
+			static bool once = false;
+			if (!once){
+				once = true;
+			}
+			glTranslated(p->getX(), p->getY(), 0);
+			glScaled(dPointScale, dPointScale, dPointScale);
+			glutSolidSphere(0.5, 10, 10);
+			glPopMatrix();
+		}
+
+	}
+#ifdef ATTRACTION
+	if (state.display == ATTRACTION_DISPLAY){
+		glPushMatrix();
+		glTranslated(state.attractionPoint->x, state.attractionPoint->y, 0);
+		double dPointScale = 10 * mWorldScale;
+		glScaled(dPointScale, dPointScale, dPointScale);
+
+		/* draw shpere */
+		glutSolidSphere(0.5, 10, 10);
+		glPopMatrix();
+	}
+#endif 
+	glPopAttrib();
+}
+
+void jhapticgui::PhantomScene::drawCage(void){
+
+	HLenum direction = HL_FRONT;
+
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, grid.cage.left.ID);
+	hlMaterialf(direction, HL_STIFFNESS, 1);
+	hlTouchModel(HL_CONTACT);
+	hlTouchableFace(direction);
+	hlMaterialf(direction, HL_POPTHROUGH, 0);
+	hlMaterialf(direction, HL_STATIC_FRICTION, 0);
+	hlMaterialf(direction, HL_DYNAMIC_FRICTION, 0);
+	drawPlane(Axis::X, grid.cage.left.offset, 0, false);
+	hlEndShape();
+
+	direction = HL_BACK;
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, grid.cage.right.ID);
+	hlMaterialf(direction, HL_STIFFNESS, 1);
+	hlTouchModel(HL_CONTACT);
+	hlTouchableFace(direction);
+	hlMaterialf(direction, HL_POPTHROUGH, 0);
+	hlMaterialf(direction, HL_STATIC_FRICTION, 0);
+	hlMaterialf(direction, HL_DYNAMIC_FRICTION, 0);
+	drawPlane(Axis::X, grid.cage.right.offset, 0, false);
+	hlEndShape();
+
+	direction = HL_BACK;
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, grid.cage.bottom.ID);
+	hlMaterialf(direction, HL_STIFFNESS, 1);
+	hlTouchModel(HL_CONTACT);
+	hlTouchableFace(direction);
+	hlMaterialf(direction, HL_POPTHROUGH, 0);
+	hlMaterialf(direction, HL_STATIC_FRICTION, 0);
+	hlMaterialf(direction, HL_DYNAMIC_FRICTION, 0);
+	drawPlane(Axis::Y, grid.cage.bottom.offset, 0, false);
+	hlEndShape();
+
+	direction = HL_BACK;
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, grid.cage.subBottom.ID);
+	hlMaterialf(direction, HL_STIFFNESS, 1);
+	hlTouchModel(HL_CONTACT);
+	hlTouchableFace(direction);
+	hlMaterialf(direction, HL_POPTHROUGH, 0);
+	hlMaterialf(direction, HL_STATIC_FRICTION, 0);
+	hlMaterialf(direction, HL_DYNAMIC_FRICTION, 0);
+	drawPlane(Axis::Y, grid.cage.bottom.offset - 0.005, 0, false);
+	hlEndShape();
+
+	direction = HL_FRONT;
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, grid.cage.top.ID);
+	hlMaterialf(direction, HL_STIFFNESS, 1);
+	hlTouchModel(HL_CONTACT);
+	hlTouchableFace(direction);
+	hlMaterialf(direction, HL_POPTHROUGH, 0);
+	hlMaterialf(direction, HL_STATIC_FRICTION, 0);
+	hlMaterialf(direction, HL_DYNAMIC_FRICTION, 0);
+	drawPlane(Axis::Y, grid.cage.top.offset, 0, false);
+	hlEndShape();
+
+	direction = HL_FRONT;
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, grid.cage.overTop.ID);
+	hlMaterialf(direction, HL_STIFFNESS, 1);
+	hlTouchModel(HL_CONTACT);
+	hlTouchableFace(direction);
+	hlMaterialf(direction, HL_POPTHROUGH, 0);
+	hlMaterialf(direction, HL_STATIC_FRICTION, 0);
+	hlMaterialf(direction, HL_DYNAMIC_FRICTION, 0);
+	drawPlane(Axis::Y, grid.cage.top.offset - 0.005, 0, false);
+	hlEndShape();
+}
+
+void jhapticgui::PhantomScene::drawEditPlane(void){
+	const double offset = -0.005;
+	glPushAttrib(GL_ENABLE_BIT);
+	glPolygonOffset(1, 1);
+	glEnable(GL_POLYGON_OFFSET_FILL);
+
+	glBegin(GL_QUADS);
+     glVertex3f(grid.editGridLeft, grid.editGridBottom, offset);
+	 glVertex3f(grid.editGridRight, grid.editGridBottom, offset);
+	 glVertex3f(grid.editGridRight, grid.editGridTop, offset);
+	 glVertex3f(grid.editGridLeft, grid.editGridTop, offset);
+	glEnd();
+
+	glDisable(GL_POLYGON_OFFSET_FILL);
+	glPopAttrib();
+	//glEndList();
+}
+
+/*******************************************************************************
+The main routine for rendering scene haptics.
+*******************************************************************************/
+void jhapticgui::PhantomScene::drawSceneHaptics()
+{
+	/* stop the sticky ref if enough time has passed */
+	if (state.stickyRef == StickyRef::RUNNING){
+		if (clock() > state.stickyRefStart + STICKY_INTERVAL){
+			state.stickyRef = StickyRef::NONE;
+		}
+	}
+
+	hlBeginFrame();
+
+	// draw sticky wall
+	hlPushAttrib(HL_HINT_BIT);
+	hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, stickyWall.ID);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_POPTHROUGH, 0);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION, 0);
+	hlMaterialf(HL_FRONT_AND_BACK, HL_DYNAMIC_FRICTION, 0);
+	hlTouchModel(HL_CONSTRAINT);
+	hlTouchModelf(HL_SNAP_DISTANCE, HARD_SNAP);
+	hlTouchableFace(HL_FRONT_AND_BACK);
+	if (state.display == DISPLAY_EDIT_GRID_MODE) {
+		drawEditPlane();
+	}
+	else{
+		drawPlane(Axis::Z, -0.005, stickyWall.displayList);
+	}
+	hlEndShape();
+	hlPopAttrib();
+
+	/* draw the force walls */
+	hlPushAttrib(HL_HINT_BIT);
+	for (const PhantomScene::Wall & wall : {forceWallBack, forceWallFront}) {
+		hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, wall.ID);
+		hlMaterialf(HL_BACK, HL_STIFFNESS, 1);
+		hlTouchModel(HL_CONTACT);
+		hlTouchableFace(wall.offset > 0 ? HL_BACK : HL_FRONT);
+		hlMaterialf(HL_FRONT, HL_POPTHROUGH, 0);
+		hlMaterialf(HL_FRONT, HL_STATIC_FRICTION, 0);
+		hlMaterialf(HL_FRONT, HL_DYNAMIC_FRICTION, 0);
+		drawPlane(Axis::Z, wall.offset, wall.displayList);
+		hlEndShape();
+	}
+	hlPopAttrib();
+
+
+	hlPopAttrib();
+
+	/* draw the horizontal line  */
+	if (state.display == DISPLAY_VALUE_MODE){
+		drawLine(*hLine, LINE_SNAP);
+	}
+	else if (state.display == DISPLAY_GRAPH_MODE) {
+		//draw automation line 
+		hlPushAttrib(HL_HINT_BIT | HL_MATERIAL_BIT);
+		 hlHinti(HL_SHAPE_FEEDBACK_BUFFER_VERTICES, 1);
+		 hlBeginShape(HL_SHAPE_FEEDBACK_BUFFER, hLine->getID());
+		 hlTouchModel(HL_CONSTRAINT);
+		 hlTouchModelf(HL_SNAP_DISTANCE, (LINE_SNAP));
+		 hlMaterialf(HL_FRONT_AND_BACK, HL_STIFFNESS, 1);
+		 hlMaterialf(HL_FRONT_AND_BACK, HL_STATIC_FRICTION,  state.friction /*0.5f*/);
+		 glLineWidth(1.0);
+
+		 glBegin(GL_LINE_STRIP);
+
+		 glVertex3d(graph->getStart().getX(), graph->getStart().getY(), 0);
+		 const HPoint* lastPoint = nullptr;
+		 for (auto& p : graph->getPoints()){
+			 glVertex3d(p->getX(), p->getY(), 0);
+			 lastPoint = p;
+		 }
+
+		 /* the Y of the end of the automation follows the last point or the begin if no points exist */
+		 if (lastPoint == nullptr){
+			 glVertex3d(graph->getEnd().getX(), graph->getStart().getY(), 0);
+		 }
+		 else{
+			 glVertex3d(graph->getEnd().getX(), lastPoint->getY(), 0);
+		 }
+		  
+		 glEnd(); // GL_LINES_STRIP
+
+		 hlEndShape();
+		hlPopAttrib();
+
+
+		// draw points
+		for (auto& p : graph->getPoints()){
+			drawPoint(*p, 3.0f);
+		}
+		
+	}
+	else if (state.display == DISPLAY_EDIT_GRID_MODE /*&& state.gridType != FREEFORM*/){
+
+		/* draw the two vertical walls */
+		//drawLine(*(grid.bars.left), LINE_SNAP);
+		//drawLine(*(grid.bars.right), LINE_SNAP);
+		drawCage();
+		
+		drawLine(*(grid.verticalLine), SOFT_SNAP);
+
+		if (state.gridType == FREEFORM){
+			for (auto& line : grid.lines){
+				int index = line->getHashCode() - grid.HALF_SIZE;
+				drawLine(*line, 0);
+			}
+		}
+		else{
+			for (auto& line : grid.lines){
+				int index = line->getHashCode() - grid.HALF_SIZE;
+				if (state.gridType == GRID1 && index == 0){
+					drawLine(*line, state.stickyRef == StickyRef::RUNNING ? HARD_SNAP : SOFT_SNAP);
+					drawPoint(*(grid.points[line->getHashCode()]), state.stickyRef == StickyRef::RUNNING ? HARD_SNAP : SOFT_SNAP);
+				}
+				else if (state.gridType == GRID3 && (index == 0 || index == 7 || index == -7)){
+					drawLine(*line, state.stickyRef == StickyRef::RUNNING ? HARD_SNAP : SOFT_SNAP);
+					drawPoint(*(grid.points[line->getHashCode()]), state.stickyRef == StickyRef::RUNNING ? HARD_SNAP : SOFT_SNAP);
+				}
+				else{
+					drawLine(*line, SOFT_SNAP);
+					drawPoint(*(grid.points[line->getHashCode()]), SOFT_SNAP);
+				}
+			}
+		}
+	}
+	hlEndFrame();
+}
+
+
+void jhapticgui::PhantomScene::attractTo(float gain, float magnitude, bool start){
+	HLdouble devicePosition[3];
+	HDdouble direction[3];
+
+	hlGetDoublev(HL_DEVICE_POSITION, devicePosition);
+
+	direction[0] = (state.attractionPoint)->getX() - devicePosition[0];
+	direction[1] = (state.attractionPoint)->getY() - devicePosition[1];
+	direction[2] =  0 - devicePosition[2];
+
+	HDdouble directionNorm = norm(direction);
+
+	if (directionNorm != 0 ){
+		direction[0] /= directionNorm;
+		direction[1] /= directionNorm;
+		direction[2] /= directionNorm;
+	}
+
+	hlPushAttrib(HL_EFFECT_BIT);
+	hlEffectdv(HL_EFFECT_PROPERTY_DIRECTION, direction);
+	hlEffectd(HL_EFFECT_PROPERTY_GAIN, gain /* 0.5f */);
+	hlEffectd(HL_EFFECT_PROPERTY_MAGNITUDE, magnitude /*0.5f*/);
+
+	if (start){
+		hlStartEffect(HL_EFFECT_CONSTANT, attractionFX);
+	}else{
+		hlUpdateEffect(attractionFX);
+	}
+	hlPopAttrib();
+}
+
+jhapticgui::PhantomScene::PhantomScene(void(*messageCallback) (const Message &m)) : 
+HapticScene(messageCallback), gCursorDisplayList(0), isLastPointTouched(false), lastTouchedPoint(-100), 
+isAttractedPointTouched(false) {}
+		
+
+jhapticgui::PhantomScene::~PhantomScene(void){
+	delete hLine;
+	while (!grid.lines.empty()){
+		delete grid.lines.back();
+		for (int i = 0; i < grid.lines.size(); i++){
+			delete grid.lines[i];
+		}
+	}
+}
+
+
+bool jhapticgui::PhantomScene::initHaptics(void){	
+	HDErrorInfo error;
+	ghHD = hdInitDevice(HD_DEFAULT_DEVICE);
+
+	if (HD_DEVICE_ERROR(error = hdGetError())){
+		return false;
+	}
+
+	ghHLRC = hlCreateContext(ghHD);
+	hlMakeCurrent(ghHLRC);
+
+	// Enable optimization of the viewing parameters when rendering geometry for OpenHaptics.
+	hlEnable(HL_HAPTIC_CAMERA_VIEW);
+
+	hlTouchableFace(HL_FRONT);
+
+	stickyWall.ID = hlGenShapes(1);
+	stickyWall.displayList = glGenLists(1);
+	//glNewList(stickyWall.displayList, GL_COMPILE_AND_EXECUTE);
+
+	forceWallFront.ID = hlGenShapes(1);
+	forceWallFront.displayList = glGenLists(1);
+	forceWallFront.offset = 0.005;
+
+
+	forceWallBack.ID = hlGenShapes(1);
+	forceWallBack.displayList = glGenLists(1);
+	forceWallBack.offset = -0.005;
+
+#ifdef SECOND_PLANE
+	id.wall2 = hlGenShapes(1);
+#endif
+
+	fx = hlGenEffects(1);
+	attractionFX = hlGenEffects(1);
+
+	hLine = new HLine();
+
+	for (int i = 0; i < grid.lines.size(); i++){
+		grid.lines[i] = new HLine();
+		grid.points[i] = new HPoint(hlGenShapes(1));
+	}
+
+	grid.verticalLine = new HLine();
+
+	grid.cage.left.ID = hlGenShapes(1);
+	grid.cage.right.ID = hlGenShapes(1);
+	grid.cage.top.ID = hlGenShapes(1);
+	grid.cage.bottom.ID = hlGenShapes(1);
+	grid.cage.subBottom.ID = hlGenShapes(1);
+	grid.cage.overTop.ID = hlGenShapes(1);
+
+	/* callbacks */
+	hlAddEventCallback(HL_EVENT_TOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, checkContact, this);
+	hlAddEventCallback(HL_EVENT_UNTOUCH, HL_OBJECT_ANY, HL_CLIENT_THREAD, checkContact, this);
+	hlAddEventCallback(HL_EVENT_1BUTTONDOWN, HL_OBJECT_ANY, HL_CLIENT_THREAD, checkButtons, this);
+	hlAddEventCallback(HL_EVENT_2BUTTONDOWN, HL_OBJECT_ANY, HL_CLIENT_THREAD, checkButtons, this);
+	hlAddEventCallback(HL_EVENT_1BUTTONUP, HL_OBJECT_ANY, HL_CLIENT_THREAD, checkButtons, this);
+	hlAddEventCallback(HL_EVENT_MOTION, HL_OBJECT_ANY, HL_CLIENT_THREAD, checkMotion, this);
+
+	
+	return true;
+}
+
+void jhapticgui::PhantomScene::setSize(int w, int h){
+	HapticScene::setSize(w, h);
+
+	hduVector3Dd fromScreenSize;
+	fromScreen(hduVector3Dd(getWidth(), getHeight() + (2 * LINE_STRETCH), 0), fromScreenSize);
+}
+
+void jhapticgui::PhantomScene::beginFrame(void){}
+
+
+
+void jhapticgui::PhantomScene::drawCursor(void) {
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	HLdouble proxyxform[16];
+
+	GLUquadricObj *qobj = 0;
+	glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT);
+
+	glPushMatrix();
+
+	if (!gCursorDisplayList){
+		gCursorDisplayList = glGenLists(1);
+		glNewList(gCursorDisplayList, GL_COMPILE);
+		qobj = gluNewQuadric();
+		gluCylinder(qobj, 0.0, kCursorRadius, kCursorHeight,
+			kCursorTess, kCursorTess);
+		glTranslated(0.0, 0.0, kCursorHeight);
+		gluCylinder(qobj, kCursorRadius, 0.0, kCursorHeight / 5.0,
+			kCursorTess, kCursorTess);
+		gluDeleteQuadric(qobj);
+		glEndList();
+	}
+
+	// Get the proxy transform in world coordinates.
+
+	hlGetDoublev(HL_PROXY_TRANSFORM, proxyxform);
+	glMultMatrixd(proxyxform);
+
+	// Apply the local cursor scale factor.
+
+	glScaled(mCursorScale, mCursorScale, mCursorScale);
+	glEnable(GL_COLOR_MATERIAL);
+
+	glColor3f(0.0, 0.5, 1.0);
+	glCallList(gCursorDisplayList);
+	glPopMatrix();
+	glPopAttrib();
+
+
+}
+
+void jhapticgui::PhantomScene::drawPlane(Axis axis, float offset, GLuint displayList, bool useDisplayList){
+
+	if (displayList && useDisplayList){
+		glCallList(displayList);
+	}else{
+
+		if (useDisplayList){
+			displayList = glGenLists(1);
+			glNewList(displayList, GL_COMPILE_AND_EXECUTE);
+		}
+
+		glPushAttrib(GL_ENABLE_BIT);
+		glPolygonOffset(1, 1);
+		glEnable(GL_POLYGON_OFFSET_FILL);
+
+		glBegin(GL_QUADS);
+		switch (axis){
+		case Axis::X :
+			glVertex3f(offset, -5, -5);
+			glVertex3f(offset, 5, -5);
+			glVertex3f(offset, 5, 5);
+			glVertex3f(offset, -5, 5);
+			break;
+		case Axis::Y:
+			glVertex3f(-5, offset, -5);
+			glVertex3f(5, offset, -5);
+			glVertex3f(5, offset, 5);
+			glVertex3f(-5, offset, 5);
+			break;
+		case Axis::Z:
+			glVertex3f(-5, -5, offset);
+			glVertex3f(5, -5, offset);
+			glVertex3f(5, 5, offset);
+			glVertex3f(-5, 5, offset);
+			break;
+		};
+		glEnd();
+
+		glDisable(GL_POLYGON_OFFSET_FILL);
+
+		glPopAttrib();
+		if (useDisplayList) {
+			glEndList();
+		}
+	}
+}
+
+
+
+/** 
+ * 
+ * The arguments to be passed are those returned respectively by processMessage and checkCallbacks 
+ * and are used to notify the haptic engine there has been an update in the haptic scene. 
+ * Indeed, the h3d library routines need to be called only when a change occurs. Unlike openGL rooutines, 
+ * which need to be called on every frame
+ *
+ * @param messageUpdate the value returned by processMessage 
+ * @param callbacksUpdate the value returned by checkCallbacks, for example when the proxy has been moved
+
+ */
+void jhapticgui::PhantomScene::drawScene(unsigned int messageUpdate, unsigned int callbacksUpdate){
+	drawSceneHaptics();
+	drawSceneGraphics();
+
+}
+
+void jhapticgui::PhantomScene::updateWorkspace(void){
+
+}
+
+
+unsigned int jhapticgui::PhantomScene::checkCallbacks(void){
+	/* openhaptics call to check all the events */
+	hlCheckEvents();
+	return 0;
+}
+
+#include <algorithm>  
+void HLCALLBACK jhapticgui::PhantomScene::checkMotion(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata){
+	PhantomScene *pThis = static_cast<PhantomScene*>(userdata);
+	PhantomScene::State & state = pThis->state;
+
+	HLdouble proxyPosition[3];
+	hlGetDoublev(HL_DEVICE_POSITION, proxyPosition);
+
+	if (state.display == DISPLAY_VALUE_MODE || state.display == DISPLAY_GRAPH_MODE || state.display == DISPLAY_EDIT_GRID_MODE){
+
+		if ( state.touch.line || state.touch.gridLine){
+			if (fabs(state.lastRecPos[0] - proxyPosition[0]) > SCRUB_THRESHOLD) {
+				state.lastRecPos[0] = proxyPosition[0];
+
+				char msgArgs[Message::MAX_ARGS_LEN];
+				sprintf_s(msgArgs, "%f", pThis->getNormalizedProxyPos()[0]);
+				pThis->send(Message(OutCommands::SCRUB_ON, msgArgs, 0));
+			}
+		}		
+	}
+	
+	if (state.display == DISPLAY_EDIT_GRID_MODE && state.gridType == FREEFORM){
+		/* sends touch info  about the motion for free_form */
+
+		HLdouble proxyPosition[3];
+		hlGetDoublev(HL_DEVICE_POSITION, proxyPosition);
+
+
+		if (state.touch.gridLine) { // check for untouch 
+			const auto &element = std::find_if(pThis->grid.lines.begin(), pThis->grid.lines.end(),
+				[&pThis](const HLine* l){ return pThis->state.touch.gridLineID == l->getID(); }
+			);
+
+			if (element != pThis->grid.lines.end()){
+				if (fabs((*element)->Y1 - proxyPosition[1]) > epsilon || proxyPosition[0] > (*element)->X2 || proxyPosition[0] < (*element)->X1){ //untouch
+					checkContact(HL_EVENT_UNTOUCH, pThis->lastTouchedPoint, thread, cache, userdata);
+					state.touch.gridLine = false;
+				}
+			}
+		}
+
+
+		for (const auto& l : pThis->grid.lines){
+			if (fabs(l->Y1 - proxyPosition[1]) < epsilon &&  proxyPosition[0] > l->X1 && proxyPosition[0] < l->X2){
+				/*if (pThis->lastTouchedPoint != p.hapticID){
+				checkContact(HL_EVENT_UNTOUCH, pThis->lastTouchedPoint, thread, cache, userdata);
+				}*/
+				checkContact(HL_EVENT_TOUCH, l->getID(), thread, cache, userdata);
+				/* keep track of the touched line */
+				state.touch.gridLineID = l->getID();
+				state.touch.gridLine = true;
+				break;
+			}
+		}
+
+	}
+}
+
+void HLCALLBACK jhapticgui::PhantomScene::checkButtons(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata){
+	PhantomScene *pThis = static_cast<PhantomScene*>(userdata);
+	PhantomScene::State & state = pThis->state;
+	
+	if (state.display == DISPLAY_VALUE_MODE || state.display == DISPLAY_GRAPH_MODE){
+		if (event == HL_EVENT_1BUTTONDOWN){
+			state.button1Pressed = true;
+		}
+		else if (event == HL_EVENT_1BUTTONUP){
+			state.button1Pressed = false;
+			pThis->send(Message(OutCommands::SCRUB_OFF, "", 0));
+		}
+	}
+
+	if (state.display == DISPLAY_GRAPH_MODE){
+		if (event == HL_EVENT_1BUTTONDOWN && state.touch.point != HL_OBJECT_ANY){
+			state.display = DISPLAY_EDIT_GRID_MODE;
+			pThis->grid.on = true;
+			pThis->editGridPoint = state.touch.point;
+			pThis->createEditGrid();
+		}
+		else if (event == HL_EVENT_2BUTTONDOWN){
+			if (state.touch.point == HL_OBJECT_ANY){ // nothing touched create new point
+				std::array<float, 2> proxyPos = pThis->getNormalizedProxyPos();
+				char msgArgs[Message::MAX_ARGS_LEN];
+				sprintf_s(msgArgs, "%f %f", proxyPos[0], proxyPos[1]);
+				pThis->send(Message(OutCommands::ADD_SEQ_VAL, msgArgs, 0));
+			}
+			else { // delete touched point 
+				auto& element = std::find_if(pThis->graph->getPoints().begin(),
+					pThis->graph->getPoints().end(), [state](HPoint* const p){ return p->getHapticID() == state.touch.point; });
+
+				pThis->send(Message(OutCommands::REM_SEQ_VAL,"",(*element)->getHashCode()));
+			}
+		}
+			
+	}
+	else if (state.display == DISPLAY_EDIT_GRID_MODE){
+		if (event == HL_EVENT_1BUTTONDOWN){
+			state.display = DISPLAY_GRAPH_MODE;
+			auto& element = std::find_if(pThis->graph->getPoints().begin(),
+				pThis->graph->getPoints().end(), [pThis](HPoint* const p){ return p->getHapticID() == pThis->editGridPoint; });
+			
+			pThis->editGridPoint = HL_OBJECT_ANY;
+
+			std::array<float, 2> proxyPos = pThis->getNormalizedProxyPos();
+
+			char msgArgs[Message::MAX_ARGS_LEN];
+			sprintf_s(msgArgs, "%f %f", proxyPos[0], proxyPos[1]);
+			
+			int hashCode = (*element)->getHashCode();
+			pThis->send(Message(OutCommands::MOVE_SEQ_VAL, msgArgs, hashCode));
+
+		}
+	}
+}
+
+
+void HLCALLBACK jhapticgui::PhantomScene::checkContact(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata){
+	PhantomScene *pThis = static_cast<PhantomScene*>(userdata);
+	PhantomScene::State & state = pThis->state;
+
+	if (state.display == DISPLAY_GRAPH_MODE || state.display == DISPLAY_VALUE_MODE) {
+		if (event == HL_EVENT_TOUCH  && object == pThis->hLine->getID()) { // check line touch 
+			state.touch.line = true;
+		}
+		else if (event == HL_EVENT_UNTOUCH  && object == pThis->hLine->getID()){ // check line untouch 
+			state.touch.line = false;
+			pThis->send(Message(OutCommands::SCRUB_OFF, "", 0));
+		}
+		else if (event == HL_EVENT_TOUCH && state.display == DISPLAY_GRAPH_MODE){ // check point touch 
+			const auto& element = std::find_if(pThis->graph->getPoints().begin(),
+				pThis->graph->getPoints().end(),
+				[object](const HPoint* const p){ return  object == p->getHapticID(); });
+			if (element != pThis->graph->getPoints().end()){
+				state.touch.point = (*element)->getHapticID();
+				pThis->send(Message(OutCommands::POINT_TOUCHED, "", (*element)->getHashCode()));
+			}
+		}
+		else if (event == HL_EVENT_UNTOUCH && state.display == DISPLAY_GRAPH_MODE){ // check point untouch
+			if (object == state.touch.point){
+				state.touch.point = HL_OBJECT_ANY;
+			}
+		}
+	}
+	
+	if (state.display == DISPLAY_EDIT_GRID_MODE){
+		if (event == HL_EVENT_TOUCH  && state.stickyRef == StickyRef::NONE) {
+			/* look for the touched element among the lines */
+			auto & element = std::find_if(pThis->grid.lines.begin(),
+				pThis->grid.lines.end(), [object](const HLine* l){ return l->getID() == object; });
+
+			if (element != pThis->grid.lines.end()){ // if found it 
+				int hashCode = (*element)->getHashCode();
+				
+				if (state.gridType == GRID1 && hashCode == pThis->grid.lines[15]->getHashCode()){
+					state.stickyRef = StickyRef::RUNNING;
+					state.stickyRefStart = clock();
+				}
+				else if (state.gridType == GRID3 && pThis->state.stickyRef == StickyRef::NONE &&
+					(hashCode == pThis->grid.lines[15]->getHashCode() ||
+					hashCode == pThis->grid.lines[7 + 15]->getHashCode() ||
+					hashCode == pThis->grid.lines[-7 + 15]->getHashCode())) {
+					state.stickyRef = StickyRef::RUNNING;
+					state.stickyRefStart = clock();
+				}
+
+				pThis->send(Message(OutCommands::LINE_TOUCHED, "", hashCode));
+			}
+			else{ // same thing with points on vertical line 
+				/* look for the touched element among the lines */
+				auto & element = std::find_if(pThis->grid.points.begin(),
+					pThis->grid.points.end(), [object](const HPoint* p){ return p->getHapticID() == object; });
+
+				if (element != pThis->grid.points.end()){ // if found it 
+					int hashCode = (*element)->getHashCode();
+
+					if (state.gridType == GRID1 && hashCode == pThis->grid.points[15]->getHashCode()){
+						state.stickyRef = StickyRef::RUNNING;
+						state.stickyRefStart = clock();
+					}
+					else if (state.gridType == GRID3 && pThis->state.stickyRef == StickyRef::NONE &&
+						(hashCode == pThis->grid.points[15]->getHashCode() ||
+						hashCode == pThis->grid.points[7 + 15]->getHashCode() ||
+						hashCode == pThis->grid.points[-7 + 15]->getHashCode())) {
+						state.stickyRef = StickyRef::RUNNING;
+						state.stickyRefStart = clock();
+					}
+
+					pThis->send(Message(OutCommands::LINE_TOUCHED, "", hashCode));
+				}
+			}
+		}
+		
+	}
+
+#ifdef REFER
+	else if (event == HL_EVENT_TOUCH  && !pThis->state.referenceActive) {
+			/* if a non-reference point is touched the snappiness is re-enabled */
+			bool anotherPoint = false;
+			for (int i = 0; i < pThis->points.size(); i++){
+
+				if ((i == 15 || i == 7 + 15 || i == -7 + 15) && object == pThis->points[i].hapticID){
+					break;
+				}
+				else if (object == pThis->points[i].hapticID){ // if it's another point 
+					anotherPoint = true;
+					break;
+				}
+			}
+
+			if (anotherPoint){
+				pThis->state.referenceActive = true;
+				//FIXME to remove
+				const auto& element = std::find_if(pThis->points.begin(), pThis->points.end(), [object](const Point& p){ return  object == p.hapticID; });
+				std::cout << "true " << element->index << std::endl;
+			}
+		}
+#endif
+}
+
+
+std::array<float, 2> jhapticgui::PhantomScene::getNormalizedProxyPos(){
+	std::array<float, 2> toReturn;
+
+	HLdouble proxyPosition[3];
+	hlGetDoublev(HL_DEVICE_POSITION, proxyPosition);
+
+	hduVector3Dd bottomLeftPoint, topRightPoint;
+	fromScreen(hduVector3Dd(0, 0, 0), bottomLeftPoint);
+	fromScreen(hduVector3Dd(getWidth(), getHeight(), 0), topRightPoint);
+
+	hduVector3Dd screenCoord;
+	toScreen(hduVector3Dd(proxyPosition[0], proxyPosition[1], proxyPosition[2]), screenCoord);
+	float x = screenCoord[0] / getWidth();
+	float y = screenCoord[1] / getHeight();
+
+
+	float normalizedX = (proxyPosition[0] - bottomLeftPoint[0]) / (topRightPoint[0] - bottomLeftPoint[0]);
+	float normalizedY = (proxyPosition[1] - bottomLeftPoint[1]) / (topRightPoint[1] - bottomLeftPoint[1]);
+
+	toReturn[0] = normalizedX;
+	toReturn[1] = normalizedY;
+
+	return toReturn;
+}
+
+unsigned int jhapticgui::PhantomScene::processMessage(const Message & m){
+		//std::cout << "PhantomScene: message received: " << m.command << " " << m.args << " " << m.ID << std::endl;
+
+
+	if(!strcmp(m.command,InCommands::DISPLAY_RENDER_VALUE)){
+		state.display = DISPLAY_VALUE_MODE;
+
+		hduVector3Dd leftPoint, rightPoint;
+		fromScreen(hduVector3Dd(0, getHeight() / 2, 0), leftPoint);
+		fromScreen(hduVector3Dd(getWidth(), getHeight() / 2, 0), rightPoint);
+
+		hLine->X1 = leftPoint[0];
+		hLine->Y1 = leftPoint[1];
+		hLine->X2 = rightPoint[0];
+		hLine->Y2 = rightPoint[1];
+
+	}
+	else if (!strcmp(m.command, InCommands::DISPLAY_SEQUENCE)) {
+		state.display = DISPLAY_GRAPH_MODE;
+
+		state.gridType = m.ID;
+
+		float y = 0.0f, widthMs = 0.0f, viewPortMs = 0.0f;
+		/* arguments for Graph changed is the initial value of Graph */
+		sscanf_s(m.args, "%f %f %f", &widthMs, &y, &viewPortMs);
+
+		hduVector3Dd leftPoint, rightPoint;
+		fromScreen(hduVector3Dd(0, getHeight() * y, 0), leftPoint);
+		fromScreen(hduVector3Dd(getWidth(), getHeight() * y, 0), rightPoint);
+
+		graph = new Graph(leftPoint[0], rightPoint[0], leftPoint[1], widthMs, viewPortMs);
+	}
+	else if(!strcmp(m.command,InCommands::DISPLAY_NONE)){
+		state.display = NO_DISPLAY_MODE;
+	}
+
+	else if (!strcmp(m.command, InCommands::SEQUENCE_VALUE_ADD)){
+		float x = 0.0f, y = 0.0f;
+		sscanf_s(m.args, "%f %f", &x, &y);
+
+		x = x / graph->getViewPortMs();
+
+		hduVector3Dd point;
+		fromScreen(hduVector3Dd( getWidth() * x , getHeight() *y , 0), point);
+		const HPoint & p = graph->addPoint(m.ID, point[0], point[1]);
+	}
+
+	else if (!strcmp(m.command, InCommands::SEQUENCE_VALUE_CHANGE)){
+		float x = 0.0f, y = 0.0f;
+		sscanf_s(m.args, "%f %f", &x, &y);// x time value, y normalized value 
+
+		x = x / graph->getViewPortMs();
+
+		hduVector3Dd point;
+		fromScreen(hduVector3Dd(getWidth() * x, getHeight() *y, 0), point);
+		graph->changePoint(m.ID, point[0], point[1]);
+	}
+	else if (!strcmp(m.command, InCommands::SEQUENCE_VALUE_REMOVE)){
+		graph->removePoint(m.ID);
+	}
+	else if (!strcmp(m.command, InCommands::SEQUENCE_BEGIN)){
+		float y;
+		sscanf_s(m.args, "%f", &y);
+		hduVector3Dd point;
+		fromScreen(hduVector3Dd(0, getHeight() *y, 0), point);
+		graph->setStart(point[1]);
+	}
+	else if (!strcmp(m.command, InCommands::RENDER_VALUE)){
+		float val = 0;
+		sscanf_s(m.args, "%f", &val);
+
+		state.friction = val;
+	}
+
+	return 0;
+}
+
+
+void jhapticgui::PhantomScene::createEditGrid(){
+	
+	// find the coordinates to draw the grid 
+	const std::vector<HPoint*> & points = graph->getPoints();
+	HLuint pickedPoint = editGridPoint;
+	auto element = std::find_if(points.begin(), points.end(),
+		[pickedPoint](HPoint* p){ return p->getHapticID() == pickedPoint; });
+
+	if (element == points.end()){
+		std::cout << " WARNING POINTS END " << std::endl;
+		exit(0);
+	}
+	auto index = std::distance(points.begin(), element);
+	
+	if (index == 0){
+		grid.editGridLeft = graph->getStart().getX();
+	}
+	else{
+		grid.editGridLeft = graph->getPoints().at(index - 1)->getX();
+	}
+
+	if (index == points.size() - 1){
+		grid.editGridRight = graph->getEnd().getX();
+	}
+	else{
+		grid.editGridRight = graph->getPoints().at(index + 1)->getX();
+	}
+
+	hduVector3Dd topPoint, bottomPoint;
+	fromScreen(hduVector3Dd(0, getHeight() + LINE_STRETCH, 0), topPoint);
+	fromScreen(hduVector3Dd(0, 0 - LINE_STRETCH, 0), bottomPoint);
+
+	grid.editGridTop = topPoint[1];
+	grid.editGridBottom = bottomPoint[1];
+
+	grid.cage.left.offset = grid.editGridLeft;
+	grid.cage.right.offset = grid.editGridRight;
+	grid.cage.top.offset = grid.editGridTop;
+	grid.cage.bottom.offset = grid.editGridBottom;
+
+	grid.verticalLine->X1 = (*element)->getX(); 
+	grid.verticalLine->Y1 = grid.editGridTop;
+	grid.verticalLine->X2 = (*element)->getX();
+	grid.verticalLine->Y2 = grid.editGridBottom;
+
+	
+
+
+	/* distribute the lines on the upper part of the line, including zero (<=) */
+	for (int i = 0; i <= grid.HALF_SIZE; i++){
+		hduVector3Dd glCoordinatePosition;
+		float halfHeight = getHeight() / 2;
+		fromScreen(hduVector3Dd(0, halfHeight + (i*((halfHeight + LINE_STRETCH) / grid.HALF_SIZE)), 0), glCoordinatePosition);
+
+		grid.lines[i + grid.HALF_SIZE]->X1 = grid.editGridLeft; 
+		grid.lines[i + grid.HALF_SIZE]->Y1 = glCoordinatePosition[1];
+		grid.lines[i + grid.HALF_SIZE]->X2 = grid.editGridRight;
+		grid.lines[i + grid.HALF_SIZE]->Y2 = glCoordinatePosition[1];
+		grid.lines[i + grid.HALF_SIZE]->setHashCode(i + grid.HALF_SIZE); // assign the index in the array 
+
+		/* points are laid on the vertical line */
+		grid.points[i + grid.HALF_SIZE]->setCoord(grid.verticalLine->X1, glCoordinatePosition[1]);
+		grid.points[i + grid.HALF_SIZE]->setHashCode(index = i + grid.HALF_SIZE);
+	}
+
+	for (int i = -1; i >= -grid.HALF_SIZE; i--){
+		hduVector3Dd glCoordinatePosition;
+		float halfHeight = getHeight() / 2;
+		fromScreen(hduVector3Dd(0, halfHeight + (i*((halfHeight + LINE_STRETCH) / grid.HALF_SIZE)), 0), glCoordinatePosition);
+
+		grid.lines[i + grid.HALF_SIZE]->X1 = grid.editGridLeft;
+		grid.lines[i + grid.HALF_SIZE]->Y1 = glCoordinatePosition[1];
+		grid.lines[i + grid.HALF_SIZE]->X2 = grid.editGridRight;
+		grid.lines[i + grid.HALF_SIZE]->Y2 = glCoordinatePosition[1];
+		grid.lines[i + grid.HALF_SIZE]->setHashCode(index = i + grid.HALF_SIZE); // assign the index in the array 
+
+		/* points are laid on the vertical line */
+		grid.points[i + grid.HALF_SIZE]->setCoord(grid.verticalLine->X1, glCoordinatePosition[1]);
+		grid.points[i + grid.HALF_SIZE]->setHashCode(index = i + grid.HALF_SIZE);
+	}
+
+
+	editGridPointHash = (*element)->getHashCode();
+}
+
+void jhapticgui::PhantomScene::endFrame(void){}
+
+
+
+
+void jhapticgui::PhantomScene::initGL(void){
+	static const GLfloat light_model_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
+	static const GLfloat light0_diffuse[] = { 0.9f, 0.9f, 0.9f, 0.9f };
+	static const GLfloat light0_direction[] = { 0.0f, -0.4f, 1.0f, 0.0f };
+
+	// Enable depth buffering for hidden surface removal.
+	glDepthFunc(GL_LEQUAL);
+	glEnable(GL_DEPTH_TEST);
+
+	// Cull back faces.
+	glCullFace(GL_BACK);
+	glEnable(GL_CULL_FACE);
+
+	// Setup other misc features.
+	glEnable(GL_LIGHTING);
+	glEnable(GL_NORMALIZE);
+	glShadeModel(GL_SMOOTH);
+
+	// Setup lighting model.
+	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
+	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
+	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_model_ambient);
+	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
+	glLightfv(GL_LIGHT0, GL_POSITION, light0_direction);
+	glEnable(GL_LIGHT0);
+}
+
+HHD jhapticgui::PhantomScene::ghHD = HD_INVALID_HANDLE;
+
+const unsigned int jhapticgui::PhantomScene::FRONT_BUTTON = (1 << 2);
+const unsigned int jhapticgui::PhantomScene::LEFT_BUTTON = (1 << 1);
+const unsigned int jhapticgui::PhantomScene::RIGHT_BUTTON = (1 << 3);
+const unsigned int jhapticgui::PhantomScene::REAR_BUTTON = (1 << 0);
+const unsigned int jhapticgui::PhantomScene::NO_BUTTON = 0;
+
+const int jhapticgui::PhantomScene::LINE_FORCE_FACTOR_STICKY = 2000;
+const int jhapticgui::PhantomScene::LINE_FORCE_FACTOR_LOOSE = 100;
+const int jhapticgui::PhantomScene::POINT_FORCE_FACTOR = 5;
+const float jhapticgui::PhantomScene::MAX_VISCOSITY = 200;//200.0f;  
+const float jhapticgui::PhantomScene::VISCOSITY_RADIUS = 0.005f;
+const float jhapticgui::PhantomScene::SPRING_FORCE_FACTOR = 600;
+
+const  int jhapticgui::PhantomScene::STICKY_INTERVAL = CLOCKS_PER_SEC / 2.5; // CLOCKS_PER_SEC = 1000
+
+/**
+ * The minimum distance the proxy has to move away from the last position 
+ * where it sent a scrub message, in order to send another scrub message
+ */
+const double jhapticgui::PhantomScene::SCRUB_THRESHOLD = 0.00005;
+const double jhapticgui::PhantomScene::SPRING_THRESHOLD = 0.005;
+const float jhapticgui::PhantomScene::SHIFT_AMOUNT = 5.0f; // in millisec
+
+const int jhapticgui::PhantomScene::GRAPHIC_SCALE = 2; 
+const float jhapticgui::PhantomScene::SCENE_WIDTH = 0.06f;//0.1f;
+const float jhapticgui::PhantomScene::SCENE_HEIGHT = 0.06f;
+const float jhapticgui::PhantomScene::SCENE_DEPTH = 0.06f;
+
+const float jhapticgui::PhantomScene::HARD_SNAP = 5.0f;//2.7f;
+const float jhapticgui::PhantomScene::SOFT_SNAP = 1.8f; // 3.0 
+const float jhapticgui::PhantomScene::LINE_SNAP = 15.0f;
+
+const double jhapticgui::PhantomScene::kCursorRadius = 0.5;
+const double jhapticgui::PhantomScene::kCursorHeight = 1.5;
+const int jhapticgui::PhantomScene::kCursorTess = 15;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/PhantomScene.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,246 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+#include "Message.h"
+#include "HapticScene.h"
+#include "HapticObjects.h"
+#include "Graph.h"
+
+#include <GL/glut.h>
+
+#include <HL/hl.h>
+#include <HDU/hduMatrix.h>
+#include <HDU/hduError.h>
+
+#include <HLU/hlu.h>
+
+#include <math.h>  
+#include <time.h>
+#include "utils.h"
+#include <vector>
+#include <array>
+
+ // #define SECOND_PLANE
+
+
+
+namespace jhapticgui {
+
+	const double epsilon = 0.01;
+
+	class PhantomScene : public HapticScene {
+		static HHD ghHD;
+		static const double kCursorRadius;
+		static const double kCursorHeight;
+		static const int kCursorTess;
+		static const int LINE_STRETCH = 0;// 150 ;
+		
+
+		GLuint gCursorDisplayList;
+		HHLRC ghHLRC;
+
+		struct Wall {
+			Wall() : displayList(0) {}
+			HLuint ID;
+			GLuint displayList;
+			double offset;
+		};
+		
+		Wall stickyWall;
+		Wall forceWallFront;
+		Wall forceWallBack;
+
+
+#ifdef SECOND_PLANE
+		HLuint wall2;
+#endif
+		
+		enum GridType { FREEFORM = 0, GRID0, GRID1, GRID3 };
+
+		enum StickyRef {NONE, START, RUNNING};
+
+		enum class Attraction { NONE, START, RUNNING, STOP };
+
+		enum class Axis {X, Y, Z};
+
+		enum DisplayMode  { NO_DISPLAY_MODE, DISPLAY_GRAPH_MODE, DISPLAY_VALUE_MODE, DISPLAY_EDIT_GRID_MODE };
+		/* STOP_SCRUB is to hold the information that the scrubbing effect must be stopped until the next frame
+		from next frame on the mode will be set to NO_SCRUB  */
+		enum ScrubMode { NO_SCRUB_MODE, STOP_SCRUB_MODE, SCRUB_MODE };
+		/* refers to the state of moving sequence values action */
+		enum DragMode { NO_DRAG_MODE, DRAG_MODE };
+
+		enum ForceFieldMode { NO_FORCE_FIELD_MODE, FORCE_FIELD_MODE };
+
+		struct Touch {
+			Touch() : line(false), point(HL_OBJECT_ANY), gridLineID(HL_OBJECT_ANY), gridLine(false){}
+			bool line;
+			HLuint point;
+			HLuint gridLineID;
+			bool gridLine;
+		};
+
+		struct State{
+			State() : display(NO_DISPLAY_MODE), previousDisplay(NO_DISPLAY_MODE), scrub(NO_SCRUB_MODE), 
+			   drag(NO_DRAG_MODE), forceField(NO_FORCE_FIELD_MODE), isPeaking(false),
+			   attraction(Attraction::NONE), stickyRef(StickyRef::NONE), button1Pressed(false), friction(0.0f){}
+			DisplayMode display;
+			DisplayMode previousDisplay;
+			ScrubMode scrub;
+			DragMode drag;
+			ForceFieldMode forceField;
+			bool isPeaking;
+			StickyRef stickyRef;
+			clock_t stickyRefStart;
+			Attraction attraction;
+			HPoint* attractionPoint;
+			HLdouble lastRecPos[3];
+			bool button1Pressed;
+			Touch touch;
+			int gridType;
+			float friction;
+			
+		} state;
+
+		HLuint fx;
+
+		HLuint attractionFX;
+		HLuint lastTouchedPoint;
+		bool isLastPointTouched;
+		bool isAttractedPointTouched;
+		HLine *hLine;
+		HLuint editGridPoint = HL_OBJECT_ANY;
+		int editGridPointHash;
+		Graph *graph;
+		
+
+		struct Grid {
+			const int HALF_SIZE = 15;
+			bool on = false;
+			HLdouble editGridLeft;
+			HLdouble editGridRight;
+			HLdouble editGridTop;
+			HLdouble editGridBottom;
+
+			std::array<HLine*, 15 * 2 + 1> lines;
+			std::array<HPoint*, 15 * 2 + 1> points;
+
+			HLine *verticalLine;
+
+			struct {
+				Wall left;
+				Wall right;
+				Wall top;
+				Wall bottom;
+				Wall subBottom;
+				Wall overTop;
+			} cage;
+		} grid;
+
+		static const unsigned int FRONT_BUTTON;
+		static const unsigned int LEFT_BUTTON;
+		static const unsigned int RIGHT_BUTTON;
+		static const unsigned int REAR_BUTTON;
+		static const unsigned int NO_BUTTON;
+
+		static const int LINE_FORCE_FACTOR_STICKY;
+		static const int LINE_FORCE_FACTOR_LOOSE;
+		static const float MAX_VISCOSITY;
+		static const float VISCOSITY_RADIUS;
+		static const float SPRING_FORCE_FACTOR;
+
+		static const double SCRUB_THRESHOLD;
+		static const double SPRING_THRESHOLD;
+		static const float SHIFT_AMOUNT;
+
+		static const float HARD_SNAP;
+		static const float SOFT_SNAP;
+		static const float LINE_SNAP;
+
+		static const HDdouble VIBRATION_AMPLITUDE;
+		static const HDint  VIBRATION_FREQUENCY;
+		static const unsigned int VIBRATION_DURATION;
+		static const  int STICKY_INTERVAL;
+		/** how bigger graphic coordinates are than haptic coordinates */
+        static const int GRAPHIC_SCALE; 
+		static const int POINT_FORCE_FACTOR;
+
+		/**
+		 * thw width of the haptic scene. This value is mapped to the haptic viewPort of the hosting application
+		 */
+		static const float SCENE_WIDTH;
+		static const float SCENE_HEIGHT;
+		static const float SCENE_DEPTH;
+
+		/* draws the viscosity line both in graphics and haptics */
+		void drawViscosityLine(unsigned int messageUpdate);
+		/* draws the graph both in graphics and haptics */
+		void drawGraph(unsigned int messageUpdate);
+
+		void drawSceneHaptics();
+		void drawSceneGraphics();
+		void drawPlane(Axis axis, float zoffset, GLuint displayList, bool useDisplayList = true);
+
+		void attractTo(float gain, float magnitude, bool start);
+
+		void drawPoint(const HPoint& p, float snappiness);
+		void drawLine(const HLine& line, float snappiness);
+	public:
+
+		PhantomScene(void (*messageCallback) (const Message & m) );
+		~PhantomScene(void);
+
+		/* try and initialize the haptic device, return false if the initialization fails */
+		bool initHaptics(void);
+		void setSize(int width, int height);
+
+		unsigned processMessage(const Message & m);
+
+		void beginFrame(void);
+
+		void endFrame(void);
+		/* drawing routines (both graphically and haptically) */
+
+		/* draws cursor and updates state.proxyPos */
+		void drawCursor(void) ;
+
+		void drawScene(unsigned int messageUpdate, unsigned int callbacksUpdate);
+
+		static void HLCALLBACK checkContact(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata);
+		static void HLCALLBACK checkButtons(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata);
+		static void HLCALLBACK checkMotion(HLenum event, HLuint object, HLenum thread, HLcache *cache, void *userdata);
+
+		unsigned int checkCallbacks(void);
+
+		void updateWorkspace(void);
+
+		virtual void initGL(void);
+
+		void drawEditPlane(void);
+
+		void drawCage(void);
+
+		void createEditGrid();
+
+		std::array<float, 2> getNormalizedProxyPos();
+	};
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/stdafx.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// DawPhantomHaptics.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/stdafx.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,18 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+
+#include <stdlib.h>
+#include <iostream>
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/targetver.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,470 @@
+
+#include "stdafx.h"
+#include "uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.h"
+#include "HapticScene.h"
+
+
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glut.h>
+
+#include <GL/glut.h>
+#ifdef FREEGLUT
+#include <GL/freeglut.h>
+#endif
+
+
+#ifdef COMPILE_FALCON
+#include "FalconScene.h"
+#endif
+
+#ifndef COMPILE_FALCON
+#include "PhantomScene.h"
+#endif
+
+using namespace jhapticgui;
+
+
+
+/* Function declarations */
+void display(void);
+void reshape(int width, int height);
+void idle(void);
+
+void send(const Message &m); // haptic scene callback function
+
+void checkExceptions(JNIEnv *env, char* what);
+void initJniVariables(void);
+void die(char* msg);
+void Notify(jobject *monitor);
+void MonitorEnter(jobject *monitor);
+void MonitorExit(jobject *monitor);
+
+/* Variables */
+static JNIEnv *env;
+static jobject *thisObj;
+static jobject hapticListener;
+static HapticScene *scene;
+static Message message;
+
+/* JNI Variables */
+
+static jfieldID disposeFieldID;
+static jfieldID deviceInitFailedFieldID;
+static jfieldID deviceInitDoneFieldID;
+static jfieldID messageReadyFieldID;
+static jfieldID commandFieldID;
+static jfieldID argsFieldID;
+static jfieldID IDFieldID;
+
+/* synchronization methods ids */
+static jmethodID notifyMethodID;
+static jmethodID waitListenerMethodID;
+static jmethodID messageToListenerMethodID;
+
+
+
+JNIEXPORT void JNICALL Java_uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice_init
+  (JNIEnv *environment, jobject obj, jint w, jint h){
+	
+	env = environment;
+	thisObj = &obj;
+
+	/* JNI stuff */
+	initJniVariables();
+
+#ifdef COMPILE_FALCON
+	scene = new FalconScene(send);
+#else 
+	scene = new PhantomScene(send);
+#endif
+	
+
+	/* try to initialize the haptic, tell the java thread about the success/failure. The endeavour   *
+	 * takes the thisObj on the haptics java object in order to access the nodes and edges concurrently */
+	MonitorEnter(thisObj);
+
+	bool mustSayGoodbye = false;
+
+	if(!scene->initHaptics()){
+		/* set the field in the java class to comunicate the main thread (waiting   *
+		 * for the initialization to complete) that the initialization failed       */
+		env->SetBooleanField(*thisObj, deviceInitFailedFieldID, JNI_TRUE);
+		mustSayGoodbye = true;
+	}
+
+	if(mustSayGoodbye)
+		std::cout << "Failed to initialize haptic device" << std::endl;
+	else
+		std::cout << "Haptic device successfully initialized" << std::endl;
+
+	/* initialization is done, set the flag for the java thread*/
+	env->SetBooleanField(*thisObj, deviceInitDoneFieldID, JNI_TRUE);
+
+	/* notify the java thread */
+	Notify(thisObj);
+	
+	/* release the lock */
+	MonitorExit(thisObj);
+	
+	if(mustSayGoodbye){
+		/* stops the haptic listener */
+		send(Message("stop","",0));
+		/* initialization failed: return */
+		return;
+	}
+
+	/* haptic device is initialized, now build the openGL window */
+
+	/* fake main argv and argc as this is a dll */
+	char *argv[1] = {"Haptics"};
+	int argc = 1;
+
+	/* glut initialization */
+    glutInit(&argc, argv);
+	
+	glutInitWindowSize( w, h );
+	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
+	glutCreateWindow( "Haptic Device Window" );
+
+	/* set up GLUT callback functions */
+	glutDisplayFunc ( display  );
+	glutReshapeFunc ( reshape );
+	glutIdleFunc( idle );
+
+	scene->initGL();
+
+#ifdef FREEGLUT
+	/* with this option set freeglut allows to stop the main 
+	   loop without teminating the whole application   */
+	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
+#endif
+	glutMainLoop();
+	
+	delete scene;
+	return;
+}
+
+/* GLUT display callback */
+void display(){
+	
+	/* takes the lock on thisObj */
+	MonitorEnter(thisObj);
+	
+	/* check if there is a dispose request */
+	if( env->GetBooleanField(*thisObj,disposeFieldID) == JNI_TRUE ){
+		/* set back to false to prevent the java thread from waiting again */
+		env->SetBooleanField(*thisObj,disposeFieldID,JNI_FALSE);
+		/* stops the haptic listener */
+		send(Message("stop","",0));
+		/* notify the java thread that this thread is about to die */
+		Notify(thisObj);
+
+		/* release the lock on this object */
+		MonitorExit(thisObj);
+#ifdef FREEGLUT
+		glutLeaveMainLoop ();
+		return;
+#else
+		/* if freeglut is not used, the whole application must be terminated */
+		die("Haptic device disposed");
+#endif
+
+	}
+
+	/* check if there is a message ready for the java thread */
+	bool messageReady = (env->GetBooleanField(*thisObj,messageReadyFieldID) == JNI_TRUE);
+	if(messageReady){
+		/* get the id of the field */
+		jint ID = env->GetIntField(*thisObj,IDFieldID);
+		jstring command = static_cast<jstring>(env->GetObjectField(*thisObj, commandFieldID));
+		jstring args = static_cast<jstring>(env->GetObjectField(*thisObj, argsFieldID));
+
+		/* get lenght of the strings */
+		int commandLen = env->GetStringLength(command);
+		int argsLen = env->GetStringLength(args);
+
+		/* read the command fields into the command struct */
+		env->GetStringUTFRegion(command,0,commandLen,message.command);
+		env->GetStringUTFRegion(args,0,argsLen,message.args);
+		message.ID = ID;
+		
+		/* set the flag back to false */
+		env->SetBooleanField(*thisObj,messageReadyFieldID,JNI_FALSE);
+
+		/* clean up references */
+		env->DeleteLocalRef(command);
+		env->DeleteLocalRef(args);
+		/* notify the java thread, waiting for the command to be read */
+		Notify(thisObj);
+	}
+	
+	/* release the lock. HapticScene methods from now on can execute commands without deadlock risk */
+	MonitorExit(thisObj);
+
+	unsigned int messageUpdate = 0;
+	if(messageReady)
+		messageUpdate = scene->processMessage(message);
+
+	/* --- start drawing --- */
+	scene->beginFrame();
+
+	/* draw the diagram graphicly and, if the collection has changed, haptically too */
+	scene->drawCursor();
+
+	/* the drawing can be affected by a message from the Java Thread but also 
+	   by the user actions (move, push button etc.). That's why callbacksUpdate
+	   value from the previous loop is passed as an arg as well */
+	static unsigned int callbacksUpdate = 0;
+	scene->drawScene(messageUpdate,callbacksUpdate);
+	
+	scene->endFrame();
+
+	/* check if any callback has been issued :  */
+	callbacksUpdate = scene->checkCallbacks();
+
+	/* swap graphic buffers. */
+	glutSwapBuffers();
+}
+
+/* GLUT reshape callback */
+void reshape (int w, int h){
+
+	/* only do the calculations once otherwise all the points will be
+	be updated to fit the screen  */
+	static bool doneOnce = false;
+	if (doneOnce){
+		return;
+	}
+	doneOnce = true;
+
+#ifdef COMPILE_FALCON
+	scene->setSize(w,h);
+
+	static const double ANGLE = 100;
+	glViewport(0, 0, w, h);   
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	gluPerspective(ANGLE, // angle 
+		(float)w / h, // aspect ratio
+		0.01, // near clipping plane .01
+		500);// far clipping plane 1000
+	glMatrixMode(GL_MODELVIEW);
+#else
+	
+	static const double kPI = 3.1415926535897932384626433832795;
+	static const double kFovY = 40;
+	static const double nearDist = 1.0 / tan((kFovY / 2.0) * kPI / 180.0 /* radiants for 1 degree */);
+	static const double farDist = nearDist + 2.0;
+	double aspect;
+
+	scene->setSize(w, h);
+	//cManager->setScreenHeight(h);
+	glViewport(0, 0, w, h);
+
+	// Compute the viewing parameters based on a fixed fov and viewing
+	// a canonical box centered at the origin.
+	aspect = (double)w / h;
+
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	gluPerspective(kFovY, aspect, nearDist, farDist);
+	// Place the camera down the Z axis looking at the origin.
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	gluLookAt(0, 0, nearDist + 1.0,
+		0, 0, 0,
+		0, 1, 0);
+	hduVector3Dd origin;
+	fromScreen(hduVector3Dd(0, 0, 0), origin); // can be replaced with code of fromscreen
+
+	glLoadIdentity();
+	gluLookAt(-origin[0], origin[1], nearDist + 1.0,
+		      -origin[0], origin[1], 0,
+		       0,         1,         0);
+
+
+	// update workspace 
+	GLdouble modelview[16];
+	GLdouble projection[16];
+	GLint viewport[4];
+
+	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
+	glGetDoublev(GL_PROJECTION_MATRIX, projection);
+	glGetIntegerv(GL_VIEWPORT, viewport);
+
+
+	hlMatrixMode(HL_TOUCHWORKSPACE);
+	hlLoadIdentity();
+
+	// Fit haptic workspace to view volume.
+	hluFitWorkspace(projection);
+
+	// Compute cursor scale.
+	PhantomScene* phantom = static_cast<PhantomScene*>(scene);
+	phantom->setCursorScale(hluScreenToModelScale(modelview, projection, viewport)* 20);
+	//scene->mCursorScale *= 20;//CURSOR_SIZE_PIXELS;
+
+	hduVector3Dd p0, p1;
+	bool bNoError;
+
+	bNoError = fromScreen(hduVector3Dd(0, 0, 0), p0);
+	if (!bNoError){
+		die("bNoError 1");
+	}
+
+	bNoError = fromScreen(hduVector3Dd(1, 1, 0), p1);
+	if (!bNoError){
+		die("bNoError");
+	}
+
+	double m_windowTworldScale = (p1 - p0).magnitude() / sqrt(2.0);
+	phantom->setWorldScale(m_windowTworldScale);
+
+#endif
+}
+
+/* GLUT idle callback */
+void idle(){
+	glutPostRedisplay();
+}
+
+
+/* initialize all the variables needed for the jni access to java fields */
+void initJniVariables(void){
+	/* --- CLASSES --- */
+	/* this class */
+
+	jclass hapticDeviceClass = env->GetObjectClass(*thisObj);
+	if(hapticDeviceClass == NULL){
+		die("Could not find the Haptics class");
+	}
+	/* the haptic listener, member of this class */
+	jclass hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/depic/jhapticgui/HapticListener;");
+	if(hapticListenerClass == NULL){
+		die("Could not find the haptic listener class");
+	}
+	
+	/* the Object class for wait and notify methods */
+	jclass objectClass = env->FindClass("Ljava/lang/Object;");
+	if(objectClass == NULL ){
+		die("Could not find te Object class");
+	}
+
+	/* --- FIELD ID's --- */
+
+	/* boolean set by this thread to notify the java thread the unsuccessful initialization of haptic device */
+	deviceInitFailedFieldID = env->GetFieldID(hapticDeviceClass,"initFailed", "Z");
+	if(deviceInitFailedFieldID == NULL){
+		die("failed to find the initFailed field id");
+	}
+
+	/* boolean set by this thread to notify the java thread the initialization of haptic device is done */
+	deviceInitDoneFieldID = env->GetFieldID(hapticDeviceClass,"initDone", "Z");
+	if(deviceInitDoneFieldID == NULL){
+		die("failed to find initDone field id");
+	}
+
+	/* boolean set by the java thread to notify this thread the program has been shut down */
+	disposeFieldID = env->GetFieldID(hapticDeviceClass, "dispose", "Z");
+	if(disposeFieldID == NULL){
+		die("failed to find the disposeFieldID field id");
+	}
+
+	messageReadyFieldID = env->GetFieldID(hapticDeviceClass,"messageReady","Z");
+	if(messageReadyFieldID == NULL){
+		die("failed to find the messageReadyFieldID field id");
+	}
+
+	commandFieldID = env->GetFieldID(hapticDeviceClass,"command","Ljava/lang/String;");
+	if(commandFieldID == NULL){
+		die("failed to find the commandFieldID field id");
+	}
+
+	argsFieldID = env->GetFieldID(hapticDeviceClass,"args","Ljava/lang/String;");
+	if(argsFieldID == NULL){
+		die("failed to find the argsFieldID field id");
+	}
+
+	IDFieldID = env->GetFieldID(hapticDeviceClass,"ID","I");
+	if(IDFieldID == NULL){
+		die("failed to find the IDFieldID field id");
+	}
+
+	jfieldID hapticListenerFieldID = env->GetFieldID(hapticDeviceClass,"hapticListener","Luk/ac/qmul/eecs/depic/jhapticgui/HapticListener;"); 
+	hapticListener = env->GetObjectField(*thisObj,hapticListenerFieldID);
+	if(hapticListener == NULL){
+		die("failed to find hapticListener field id");
+	}
+
+	/* --- SYNCHRONIZATION METHODS ID --- */
+	/* this.notify() */
+	notifyMethodID = env->GetMethodID(objectClass,"notify","()V");
+	if(notifyMethodID == NULL){
+		die("failed to find the notify method id");
+	}
+
+	/* listener.wait() */
+	waitListenerMethodID = env->GetMethodID(objectClass,"wait","()V");
+	if(waitListenerMethodID == NULL){
+		die("failed to find the wait method id");
+	}
+
+	messageToListenerMethodID = env->GetMethodID(hapticListenerClass,"addMessage","(Ljava/lang/String;Ljava/lang/String;I)V");
+	if(messageToListenerMethodID == NULL){
+		die("failed to find the addMessage method id");
+	}
+	
+	/* clean up local references */
+	env->DeleteLocalRef(hapticDeviceClass);
+	env->DeleteLocalRef(hapticListenerClass);
+	env->DeleteLocalRef(objectClass);
+}
+
+void send(const Message &m){
+	
+	jstring jstrCommand = env->NewStringUTF(m.command);
+	jstring jstrArgs = env->NewStringUTF(m.args); 
+
+	env->CallVoidMethod(hapticListener,messageToListenerMethodID,jstrCommand,jstrArgs,m.ID);
+	checkExceptions(env,"Could not call hapticListener.send()");
+
+	env->DeleteLocalRef(jstrCommand);
+	env->DeleteLocalRef(jstrArgs);
+}
+
+void checkExceptions(JNIEnv *env, char* what){
+	if(env->ExceptionOccurred()){
+		std::cerr << "Exception occurred!!!" << std::endl;
+		std::cerr << what << std::endl;
+		env->ExceptionDescribe();
+		exit(-1);
+	}
+}
+
+
+void die(char* msg){
+	std::cerr << msg << std::endl;
+	exit(-1);
+}
+
+void Notify(jobject *monitor){
+	env->CallVoidMethod(*monitor,notifyMethodID);
+	checkExceptions(env, "Could not call notify()");
+}
+
+void MonitorEnter(jobject *monitor){
+	if(env->MonitorEnter(*monitor) != JNI_OK){
+		die("Could not allocate memory for monitor enter");
+	}
+}
+
+void MonitorExit(jobject *monitor){
+	if(env->MonitorExit(*monitor) != JNI_OK){
+		die("Could not release memory for monitor exit ");
+	}
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,23 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class uk_ac_qmul_eecs_depic_haptics_HapticDevice */
+
+// must change class path to match the new library
+// also must change JNI class references 
+#ifndef _Included_uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice 
+#define _Included_uk_ac_qmul_eecs_depic_haptics_HapticDevice
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     uk_ac_qmul_eecs_depic_haptics_HapticDevice
+ * Method:    init
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_uk_ac_qmul_eecs_depic_jhapticgui_HapticDevice_init
+  (JNIEnv *, jobject, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/utils.cpp	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,68 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "utils.h"
+
+void stopExecution(char* msg){
+	std::cerr << msg << std::endl;
+	exit(-1);
+}
+
+
+bool fromScreen(const hduVector3Dd &win, hduVector3Dd &obj) {
+
+	hduMatrix m_worldTview;
+    hduMatrix m_viewTclip;
+    int m_viewport[4];
+	glGetDoublev(GL_MODELVIEW_MATRIX, m_worldTview);
+    glGetDoublev(GL_PROJECTION_MATRIX, m_viewTclip);
+    glGetIntegerv(GL_VIEWPORT, m_viewport); 
+    int nResult =	gluUnProject(
+        win[0], win[1], win[2],
+		m_worldTview,
+        m_viewTclip,
+        m_viewport,
+        &obj[0], &obj[1], &obj[2]);
+	
+    return nResult == GL_TRUE;
+}
+
+bool toScreen(const hduVector3Dd &obj, hduVector3Dd &win)  {
+	
+	hduMatrix m_worldTview;
+    hduMatrix m_viewTclip;
+    int m_viewport[4];
+	glGetDoublev(GL_MODELVIEW_MATRIX, m_worldTview);
+    glGetDoublev(GL_PROJECTION_MATRIX, m_viewTclip);
+    glGetIntegerv(GL_VIEWPORT, m_viewport); 
+    int nResult = gluProject(
+        obj[0], obj[1], obj[2],
+		m_worldTview,
+        m_viewTclip,
+        m_viewport,
+        &win[0], &win[1], &win[2]);
+
+    return nResult == GL_TRUE;
+}
+
+
+HDdouble norm(HDdouble v[3]){
+	return sqrt( (v[0]*v[0])+(v[1]*v[1])+(v[2]*v[2]));
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/DawHaptics/DawHaptics/utils.h	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,36 @@
+/*
+Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+#include <jni.h>
+#include "stdafx.h"
+
+#include <GL/glut.h>
+
+#include <HL/hl.h>
+#include <HDU/hduMatrix.h>
+
+
+
+
+void stopExecution(char* msg);
+bool fromScreen(const hduVector3Dd &win, hduVector3Dd &obj);
+bool toScreen(const hduVector3Dd &obj, hduVector3Dd &win);
+HDdouble norm(HDdouble v[3]);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/AudioLoader.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,189 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.swing.SwingWorker;
+
+import uk.ac.qmul.eecs.depic.daw.AudioLoader.ReturnObject;
+import uk.ac.qmul.eecs.depic.daw.beads.BeadsSampleWrapper;
+
+public class AudioLoader extends SwingWorker<ReturnObject,Void>{
+	public static final int FILE_LOAD_TOTAL_PROGRESS = 100;
+	/**
+	 * The default conversion format used. Also the conversion format returned by {@code getConversionFormat()}
+	 */
+	public static final AudioFormat DEFAULT_CONVERSION_FORMAT = new AudioFormat(
+			8000.0f, // sample rate
+			16,      // bit per sample
+			1,       // mono
+			true,    // signed 
+			false    // little endian (default .wav files)   
+			);
+	
+	private File audioFile;
+	private int minChunkSize;
+	private int maxScaleFactor;
+	
+	public AudioLoader(File audioFile, int minChunkSize, int maxScaleFactor){
+		this.audioFile = audioFile;
+		this.minChunkSize = minChunkSize;
+		this.maxScaleFactor = maxScaleFactor;
+	}
+	
+	/**
+	 * Reads the audio files and build all the min and max for all the shunks of frames 
+	 */
+	@Override
+	protected ReturnObject doInBackground() throws Exception {
+		/* get all the info about the file format, needed later for the estimate of the converted file length */
+		AudioInputStream originalFile = AudioSystem.getAudioInputStream(audioFile);
+		
+		AudioFormat originalAudioFormat = originalFile.getFormat();
+		long originalNumTotalFrames = originalFile.getFrameLength();
+		float originalFrameSize = originalAudioFormat.getFrameSize();
+		float originalFrameRate = originalAudioFormat.getFrameRate();
+		float originalNumChannels = originalAudioFormat.getChannels();
+		
+		if(originalNumTotalFrames == 0)
+			throw new UnsupportedAudioFileException("File Empty");
+		/* convert the audio format to the one suitable for parsing chunks and get min and max from them (see getConversionFormat()) */
+		AudioFormat conversionFormat = getConversionFormat();
+		if(!AudioSystem.isConversionSupported(conversionFormat,originalAudioFormat)){
+			throw new UnsupportedAudioFileException("Cannot convert file to the following format: "+conversionFormat);
+		}
+		
+		AudioInputStream convertedFile = AudioSystem.getAudioInputStream(conversionFormat, originalFile);
+		/* start parsing the file and building the chunks' minimums and maximums */
+		/* all the variable from here on, unless they begin with "originalFile" refer to the converted audio stream */
+		
+		byte[] audioBytes = new byte[minChunkSize * conversionFormat.getFrameSize()];
+		/* prepare the ByteBuffer, wrapping the byte array, that will be used to read Short values */
+		ByteBuffer byteBuffer = ByteBuffer.wrap(audioBytes);
+		/* set the endiannes according to the audio format */
+		byteBuffer.order(conversionFormat.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
+
+		/* make an estimation of the frames in the converted file, based on the original file. This is necessary because *
+		 * convertedFile is a stream pointing to original file and not a file itself. Therefore getFrameLength() returns *
+		 * -1 as t doesn't have any knowledge of the length of the underlying file. So make an estimate of the length   *
+		 * of the file after conversion in order to allocate enough space when the ArrayList in newFlechunks is allocated*
+		 * and for the progress of file load. */
+		float convertedFrameSize = conversionFormat.getFrameSize(); 
+		float convertedFrameRate = conversionFormat.getFrameRate();
+		float convertedNumChannels = conversionFormat.getChannels();
+		
+		long convertedNumTotalFrames = (long) (
+				originalNumTotalFrames * 
+				(convertedFrameSize/originalFrameSize) * 
+				(convertedFrameRate/originalFrameRate) *
+				(convertedNumChannels/originalNumChannels));
+		long convertedNumTotalBytes = convertedNumTotalFrames * conversionFormat.getFrameSize();  
+		
+		/* creates the first list of chunks with smallest size. Array size = audio frames/num frames of minimum chunk */
+		WavePeaks newFileChunks = new WavePeaks(maxScaleFactor);  
+		/* first List s for scale factor = 1, that is the finest zoom scale  */
+		newFileChunks.add(1, new ArrayList<Chunk>((int)(convertedNumTotalFrames/minChunkSize) +1));
+		int numBytesRead = 0;
+		float totalBytesRead = 0f;
+		try(BufferedInputStream chunkBufferedAudio = new BufferedInputStream(convertedFile,audioBytes.length)){
+			while((numBytesRead = chunkBufferedAudio.read(audioBytes)) != -1){
+				if(isCancelled())
+					return null;
+				totalBytesRead += numBytesRead;
+				/* normalize the progress value to total load */
+				int progress = (int) ((totalBytesRead/convertedNumTotalBytes)*FILE_LOAD_TOTAL_PROGRESS);
+				if(progress < FILE_LOAD_TOTAL_PROGRESS)
+					setProgress(progress);
+				/* Now read the byte buffer, backed by audioByte, and find min and max. The audio format *   
+				 * has been converted to signed 16 bit frames, so it can be read in a Short value        */
+				Short currentMax = Short.MIN_VALUE;
+				Short currentMin = Short.MAX_VALUE;
+
+				/* find maximum and minimum values in this chunk */
+				byteBuffer.clear();
+				byteBuffer.limit(numBytesRead);
+				while(byteBuffer.hasRemaining()){
+					Short frame = byteBuffer.getShort();
+					if(frame > currentMax)
+						currentMax = frame;
+					if(frame < currentMin)
+						currentMin = frame;
+				}
+				newFileChunks.get(1).add(new Chunk(currentMin, currentMax));
+			}
+		}
+		
+		for(int scaleFactor = 2; scaleFactor <= maxScaleFactor; scaleFactor++){
+			List<Chunk> previousList = newFileChunks.get(scaleFactor-1);
+			List<Chunk> newList = new ArrayList<>(previousList.size()/2+1);
+			
+			for(int i=0; i<previousList.size();i += 2){
+				/* check if we're at the last array item, which happens when the size is odd      * 
+				 * In this case we don't merge two items but just take the last item as a new one */
+				if(i == previousList.size()-1){
+					newList.add(previousList.get(i));
+					break; // end of the array anyway
+				}
+				newList.add(new Chunk(previousList.get(i),previousList.get(i+1)));
+			}
+			newFileChunks.add(scaleFactor, newList);
+		}
+		
+		/* open the Sample for playback */			
+		BeadsSampleWrapper sample = new BeadsSampleWrapper(new 
+				net.beadsproject.beads.data.Sample(
+						audioFile.getAbsolutePath(),
+						net.beadsproject.beads.data.Sample.Regime.newStreamingRegimeWithAging(1000, 1000)
+				)
+		);
+		/* return sample and chunks to the event dispatching thread */
+		return new ReturnObject(newFileChunks,sample,originalAudioFormat,conversionFormat);
+	}
+	
+	protected AudioFormat getConversionFormat(){
+		return DEFAULT_CONVERSION_FORMAT;
+	}
+	
+	public static class ReturnObject {
+		public ReturnObject(WavePeaks peaks, BeadsSampleWrapper s, 
+						AudioFormat originalFormat, AudioFormat conversionFormat) {
+			super();
+			this.peaks = peaks;
+			this.sample = s;
+			this.originalFormat = originalFormat;
+			this.conversionFormat = conversionFormat;
+		}
+		
+		public WavePeaks peaks;
+		public BeadsSampleWrapper sample;
+		public AudioFormat originalFormat;
+		public AudioFormat conversionFormat;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Automation.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,118 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.awt.Color;
+
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+
+public interface Automation extends Sequence  {
+	/**
+	 * Adds a new {@code AutomationValue} object to this automation.
+	 * 
+	 * @param position the position of the new object 
+	 * @param value the value of the new object
+	 * 
+	 * @return the new automation just created 
+	 */
+	public AutomationValue add(float position, float value);
+	
+	/**
+	 * Removes an {@code AutomationValue} object from this automation. 
+	 * 
+	 * @param values
+	 * @return {@code true} if this automation changed as a result of the call
+	 */
+	public boolean remove(Sequence.Value value);
+	
+	/**
+	 * Returns the colour to draw this automation graphically 
+	 * 
+	 */
+	public Color getColor();
+	
+	/**
+	 * Removes all the automation values. 
+	 * 
+	 */
+	public void reset();
+	
+	/**
+	 * 
+	 * NONE_AUTOMATION's colour is white 
+	 * 
+	 */
+	public final static Automation NONE_AUTOMATION = new Automation() {
+
+		@Override
+		public AutomationValue add(float position, float value) { return null;	}
+
+		@Override
+		public boolean remove(Sequence.Value value) {
+			return false;
+		}
+
+		@Override
+		public Range<Float> getRange() {
+			return new Range<Float>(0.0f,0.0f);
+		}
+
+		@Override
+		public void reset() {}
+
+		@Override
+		public float getBegin() {
+			return 0.0f;
+		}
+
+		@Override
+		public float getEnd() {
+			return 0.0f;
+		}
+		
+		@Override 
+		public float getLen(){
+			return 0.0f;
+		}
+
+		@Override
+		public int getValuesNum() {
+			return 0;
+		}
+
+		@Override
+		public AutomationValue getValueAt(
+				int index) {
+			return null;
+		}
+
+		@Override
+		public void addSequenceListener(SequenceListener l) {}
+
+		@Override
+		public void removeSequenceListener(SequenceListener l) {}
+
+		@Override
+		public Color getColor() {
+			return Color.WHITE;
+		}
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/AutomationValue.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,97 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+/**
+ * 
+ * This class represent a value in an Automation. An automation is a set of line envelopes and 
+ * a value represent one end of an automation line.
+ * 
+ * Therefore an automation value normally points to where the automation changes its slope.
+ * 
+ * Since an automation is a 2D graph  
+ * 
+ *
+ */
+public abstract class AutomationValue implements Comparable<AutomationValue>, Sequence.Value {
+	private float position;
+	/* ranges from 0.0 to 1.0 */
+	private float value;
+	
+	public AutomationValue(float position, float value){
+		this.position = position;
+		this.value = value;
+	}
+	
+	public void setTimePosition(float position){
+		this.position = position;
+	}
+	
+	/**
+	 * Returns the position of this automation value in milliseconds 
+	 * 
+	 * The position represents where this value is on time
+	 * 
+	 * @return the position of this automation value in milliseconds  
+	 */
+	public float getTimePosition() {
+		return position;
+	}	
+
+	public float getValue() {
+		return value;
+	}
+
+	public void setValue(float value) {
+		this.value = value;
+	}
+	
+	
+	public void setLocation(float position, float value){
+		setTimePosition(position);
+		setValue(value);
+	}
+	
+	/**
+	 * Returns the enclosing Automation. 
+	 * 
+	 * @return the enclosing Automation.
+	 */
+	public abstract Automation getAutomation();
+	
+	@Override
+	public int compareTo(AutomationValue v) {
+		if(v == null)
+			return 1;
+		if(position < v.getTimePosition())
+			return -1;
+		else if(position > v.getTimePosition())
+			return 1;
+		else 
+			return 0;
+	}
+	
+	@Override
+	public String toString(){
+		return "Automation Value. position:"+position+" value:"+value;
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Chunk.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,64 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+
+public class Chunk extends Range<Float> {
+	public static final Chunk SILENCE = new Chunk((short)0,(short)0);
+	private float normalizedStart;
+	private float normalizedEnd;
+	
+	public Chunk(Chunk chunk1, Chunk chunk2) {
+		super(chunk1, chunk2);
+		
+		normalizedStart = Math.min(chunk1.getNormStart(), chunk2.getNormStart());
+		normalizedEnd = Math.max(chunk1.getNormEnd(), chunk2.getNormEnd());
+	}
+
+	public Chunk(Short s1, Short s2) {
+		normalizedStart = (s1-Short.MIN_VALUE)/(float)domain();
+		normalizedEnd = (s2-Short.MIN_VALUE)/(float)domain();
+		
+		MathUtils.Scale scale = new MathUtils.Scale(0.0f, 1.0f, -1.0f, 1.0f);
+		start = scale.linear(normalizedStart);
+		end = scale.linear(normalizedEnd);
+	}
+
+	public static int domain() {
+		return Short.MAX_VALUE - Short.MIN_VALUE;
+	}
+	
+	public float getNormStart(){
+		return normalizedStart;
+	}
+	
+	public float getNormEnd(){
+		return normalizedEnd;
+	}
+	
+	@Override
+	public String toString(){
+		return "Chunk - range:["+getStart()+","+getEnd()+"]  normalized range ["+getNormStart()+","+getNormEnd()+"]" ;
+	}
+}	
+	
+	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Clip.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,202 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+
+public class Clip extends Range<Integer> implements Comparable<Clip> {
+	private Sample sample;
+	private int sampleStart;
+	private float startMs;
+	private float endMs;
+	private float millisecPerChunk;
+	private float sampleStartMs;
+	
+	public Clip(int start, int end, Sample sample, int sampleStart, float millisecPerChunk) {
+		super(start, end);
+		this.sample = sample;
+		this.sampleStart = sampleStart;
+		
+		this.millisecPerChunk = millisecPerChunk;
+		updateTimeMs();
+		sampleStartMs = sampleStart * millisecPerChunk;
+	}
+	
+	/**
+	 * 
+	 * Copy constructor
+	 * 
+	 * @param c another instance of {@code Clip}
+	 */
+	public Clip(Clip c){
+		this(c.start,c.end,c.sample,c.sampleStart,c.millisecPerChunk);
+	}
+
+	private void updateTimeMs(){
+		startMs = start * millisecPerChunk;
+		endMs = end * (millisecPerChunk) + millisecPerChunk;
+	}
+	
+	/**
+	 * Shifts the clips of the amount given as argument 
+	 * @param amount
+	 */
+	public void shift(int amount){
+		start += amount;
+		end = end += amount;
+		
+		updateTimeMs();
+	}
+	
+	public void setStartAt(int position){
+		int diff = position - start;
+		start = position;
+		end = end + diff;
+		
+		updateTimeMs();
+	}
+	
+	public Sample getSample() {
+		return sample;
+	}
+	
+	/**
+	 * Returns the chunks index where this selection starts relative to the start of the sample.
+	 * 
+	 * This is different from {@code getStart()}, as {@code getStart()} returns the 
+	 * absolute position (a.k.a. chunk index) of the selection start   
+	 * 
+	 * @return
+	 */
+	public int getSampleStart(){
+		return sampleStart;
+	}
+	
+	public float getSampleStartMs(){
+		return sampleStartMs;
+	}
+	
+	public float getStartTimeMs(){
+		return startMs;
+	}
+	
+	public float getEndTimeMs(){
+		return endMs;
+	}
+	
+	public float getMillisecPerChunk(){
+		return millisecPerChunk;
+	}
+	
+	/**
+	 * Checks whether {@code p} is contained in the selection,
+	 * that is greater than {@code getStart()} and lower than {@code getEnd()}.
+	 * 
+	 *  
+	 * @param p the integer value to check for 
+	 * @return {@code true} if {@code pos} is contained in the selection, {@code false} otherwise
+	 */
+	public boolean contains(int pos){
+		return ((pos>=getStart()) && (pos<=getEnd()));
+	}
+	
+	public boolean containsTime(float time){
+		return time >= startMs && time <= endMs;
+	}
+	
+	/**
+	 * Splits the selection around the given position.
+	 * 
+	 * If {@code pos} is less than the clip start or greater than the clip end, then the array will have size 1 and will
+	 * contain this Clip. Conversely if {@code pos} is contained in this clip an array with two new instances of {@code Clip} is returned. The clip at index 0, will range 
+	 * from {@code geStart()} to {@code pos-1}, whereas the clip at index 1 will range from {@code pos} to {@code getEnd()} 
+	 * 
+	 * 
+	 * @param pos the split position
+	 * @return an array of one or two clips, resulted from a split at position {@code pos}
+	 */
+	public Clip [] split(int pos){
+		/* if pos can go from start+1 to end                                    *
+		 * if pos == start+1, the split will be [start,start] and [start+1,end] *
+		 * if pos == end, the split will be [start,end-1] and [end,end]         */
+		if(pos <= getStart() || pos > getEnd()){ 
+			return new Clip [] {this};
+		}else{
+			return new Clip [] { 
+				new Clip(getStart(),pos-1,sample,sampleStart,millisecPerChunk),
+				new Clip(pos,getEnd(),sample,sampleStart+pos-getStart(),millisecPerChunk)
+			};
+		}
+	}
+	
+	@Override
+	public int compareTo(Clip s) {
+		return getStart().compareTo(s.getStart());
+	}
+
+	@Override
+	public String toString(){
+		return super.toString()+ ", selection in Ms:["+startMs+","+endMs+"], sample:"+ sample+ ", sample start:" +sampleStart;
+	}
+	
+	public static Clip join(Clip clip1, Clip clip2){
+		/* nulls not joinable */
+		if(clip1 == null || clip2 == null){
+			return null;
+		}
+		
+		/* clips with different sample or different resolution not joinable */
+		if(!clip1.sample.equals(clip2.sample) || clip1.millisecPerChunk != clip2.millisecPerChunk ){
+			return null;
+		}
+			
+		/* try clip2 after clip1 */
+		if(clip1.end+1 == clip2.start){
+			int clip1Len = clip1.end - clip1.start;
+			
+			if(clip1.sampleStart + clip1Len + 1 == clip2.sampleStart){
+				return new Clip(
+						clip1.start,
+						clip2.end,
+						clip1.sample,
+						clip1.sampleStart,
+						clip1.millisecPerChunk
+						);
+			}
+		/* try clip1 after clip2 */	
+		}else if(clip2.end+1 == clip1.start){
+			int clip2Len = clip2.end - clip2.start;
+			
+			if(clip2.sampleStart + clip2Len + 1 == clip2.sampleStart){
+				return new Clip(
+						clip2.start,
+						clip1.end,
+						clip2.sample,
+						clip2.sampleStart,
+						clip2.millisecPerChunk
+						);
+			}
+		}
+		
+		/* not joinable */
+		return null;
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/ClipList.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,425 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class ClipList extends  ArrayList<Clip> {
+	private static final long serialVersionUID = 1L;
+	private int scaleFactor;
+	private List<Integer> starts;
+	private Map<Sample,WavePeaks> peaksMap;
+	private List<SoundWaveListener> changeListeners;
+	private ClipListEditor clipListEditor;
+	
+	public ClipList(int scaleFactor, Map<Sample,WavePeaks> peaksMap,SoundWave wave) {
+		this.scaleFactor = scaleFactor;
+		this.peaksMap = peaksMap;
+		changeListeners = new ArrayList<>(1);
+		starts = new ArrayList<>();
+		clipListEditor = new ClipListEditor();
+	}
+	
+	/**
+	 * Adds a new {@code SampleSelection}. The order of selections is kept (ordered insert).  
+	 * 
+	 * @param s the new selection to add
+	 */
+
+	@Override
+	public boolean add(Clip s){
+		int index = Collections.binarySearch(starts, s.getStart());
+		
+		if(index < 0)
+			index = ~index;
+		
+		starts.add(index, s.getStart());
+		super.add(index,s);
+		return true;
+	}
+	
+	/**
+	 * Unsupported method
+	 * 
+	 * @throws UnsupportedOperationException
+	 * 
+	 */
+	@Override
+	public void add(int index, Clip element){
+		throw new UnsupportedOperationException();
+	}
+	
+	@Override
+	public boolean addAll(Collection<? extends Clip > collection){
+		for(Clip c : collection){
+			add(c);
+		}
+		return (!collection.isEmpty());
+	}
+	
+	@Override
+	public boolean addAll(int index, Collection<? extends Clip> c){
+		throw new UnsupportedOperationException();
+	}
+	
+	@Override
+	public boolean 	removeAll(Collection<?> c){
+		boolean changed = false;
+		for( Object o : c){
+			boolean removed = remove(o);
+			changed = changed || removed; 
+		}
+		return changed;
+	}
+	
+	@Override 
+	public boolean retainAll(Collection<?> collection){
+		List<Clip> newList = new ArrayList<>(size());
+		for(Clip c : this){
+			if(collection.contains(c)){
+				newList.add(c);
+			}
+		}
+		
+		if(newList.size() == size())
+			return false;
+		
+		clear();
+		addAll(newList);
+		return true;
+	}
+
+	@Override
+	public boolean remove(Object c){
+		boolean removed = super.remove(c); 
+		if(removed){
+			starts.remove(((Clip)c).getStart()); // FIXME boh
+		}
+		return removed;
+	}
+	
+	
+	public void clear(){
+		super.clear();
+		starts.clear();
+	}
+	
+	public void addSoundWaveListener(SoundWaveListener l ){
+		changeListeners.add(l);
+	}
+	
+	public boolean removeSoundWaveListener(SoundWaveListener l ){
+		return changeListeners.remove(l);
+	}
+	
+	protected void fireSoundWaveListeners(SoundWaveEvent evt){
+		for(SoundWaveListener l : changeListeners){
+			l.update(evt);
+		}
+	}
+	
+	/**
+	 * Returns the length of the SoundWave 
+	 * 
+	 * @return
+	 */
+	public int getLength() {
+		if(isEmpty())
+			return 0;
+		else {
+			int max = get(0).getEnd();
+			for(int i=1; i< size(); i++){
+				if(max < get(i).getEnd()){
+					max = get(i).getEnd();
+				}
+			}
+			
+			return max+1;
+		}
+	}
+
+	public float getLengthMs(){
+		if(isEmpty())
+			return 0.0f;
+		else{
+			float max = get(0).getEndTimeMs();
+			for(int i=1; i< size(); i++){
+				if(max < get(i).getEndTimeMs()){
+					max = get(i).getEndTimeMs();
+				}
+			}
+			
+			return max;
+		}
+	}
+	
+	public List<Clip> getClipsAt(int pos){ // FIXME maybe find a better algorithm
+		if(isEmpty())
+			return Collections.emptyList();
+		
+		List<Clip> clipsAt = new ArrayList<>(size());
+		for(Clip s : this){
+			/* take advantage of the fact that ClipList is ordered by start */
+			if(s.getStart() > pos){
+				break;
+			}else if(s.contains(pos)){
+				clipsAt.add(s);
+			}
+		}
+		
+//		if(selections.get(chachedIndex).contains((int)p)){
+//			return selections.get(chachedIndex);
+//		}else{
+//			for(int i=0;i<selections.size();i++){
+//				if(selections.get(i).contains(i)){
+//					chachedIndex = i;
+//					return selections.get(chachedIndex);
+//				}
+//			}
+//		}
+		return clipsAt;
+	}
+	
+	public List<Clip> getClipsAtTime(float time){
+		if(isEmpty()){
+			return Collections.emptyList();
+		}
+		
+		List<Clip> clipsAt = new ArrayList<>(size());
+		for(Clip s : this){
+			/* clips are ordered by start (and hence startTimeMs) so next clips will all have *
+			 * startTimeMs greater than time. Further check is therefore useless              */
+			if(s.getStartTimeMs() > time){
+				break;
+			}else if(s.containsTime(time)){
+				clipsAt.add(s);
+			}
+		}
+		return clipsAt;
+	}
+	
+	/**
+	 * changes the scale factor of this clip list. All the contained clips will be rescaled according to the new scale factor.
+	 * 
+	 * @param newScaleFactor
+	 */
+	public void setScaleFactor(int newScaleFactor){
+		for(int i=0; i<size(); i++){
+			Clip clip = get(i);
+			/* use the Selection static method to convert the clip start and end to the new scale factor values */
+			Selection converted = Selection.convertFactor(new Selection(clip.getStart(),clip.getEnd(),scaleFactor), newScaleFactor);
+			/* replace the old clip with an update scale factor one */
+			set(i, new Clip(
+					converted.getStart(),
+					converted.getEnd(),
+					clip.getSample(),
+					Selection.convertFactor(new Selection(clip.getSampleStart(),clip.getEnd(),scaleFactor), newScaleFactor).getStart(),
+					clip.getSample().getLength()/peaksMap.get(clip.getSample()).withScaleFactor(newScaleFactor).size())); 
+		}
+		
+		scaleFactor = newScaleFactor;
+	}
+	
+	public int getScaleFactor(){
+		return scaleFactor;
+	}
+	
+	public SoundWaveEditor getClipEditor () {
+		return clipListEditor;
+	}
+	
+	/* returns a new list obtained by splitting this at the given position */
+	private static List<Clip> splitList(int pos, List<Clip> list){
+		List<Clip> newList = new LinkedList<>();
+		
+		for(int i=0; i<list.size(); i++){
+			Clip c = list.get(i);
+			/* split around getStart() */
+			if(c.contains(pos)){
+				Clip [] split = c.split(pos);
+				newList.add(split[0]);
+				if(split.length == 2)
+					newList.add(split[1]);
+			}else{
+				newList.add(c);
+			}
+		}
+		
+		return newList;
+	}
+	
+	private class ClipListEditor implements SoundWaveEditor {
+		List<Clip> copiedClips;
+		Selection copySelection;
+		boolean copied;
+		
+		ClipListEditor(){
+			copiedClips = new LinkedList<>();
+			copySelection = Selection.ZERO_SELECTION;
+		}
+		
+		@Override
+		public boolean cut(SoundWave wave, Selection selection) {
+			if(selection == null || selection.getStart() == selection.getEnd()){
+				return false;
+			}
+			
+			/* cut also copies in memory */
+			copySelection = selection;
+			
+			/* splits this clipList on start and end of selection and 
+			 * saves in copiedClips the clips within the selection   */
+			
+			/* get the split list, splitting by selection start */
+			List<Clip> splitList = ClipList.splitList(selection.getStart(),ClipList.this);
+			/* substitute this list with the split list */
+			ClipList.this.clear();
+			ClipList.this.addAll(splitList);
+			
+			/* get the split list, splitting by selection end */
+			splitList = ClipList.splitList(selection.getEnd(),ClipList.this);
+			/* substitute this list with the split list */
+			ClipList.this.clear();
+			ClipList.this.addAll(splitList);
+
+			copiedClips.clear();
+			for(Clip c : ClipList.this){
+				/* when a clip is split at position, position will go with the right split           * 
+				 * this is why getStart() must be grater and equal and getEnd() lower but not equal  */
+				if(c.getStart() >= selection.getStart() && c.getEnd() < selection.getEnd()){
+					copiedClips.add(c);
+				}
+			}
+			copied = removeAll(copiedClips);
+			
+			if(copied){
+				fireSoundWaveListeners(new SoundWaveEvent(wave,SoundWaveEvent.CUT,copiedClips));
+			}
+			return copied; 
+		}
+	
+		@Override
+		public boolean copy(SoundWave wave, Selection selection) {
+			if(selection == null || selection.getStart() == selection.getEnd()){
+				return false;
+			}
+			/* keep track of the selection used to copy */
+			copySelection = selection;
+			
+			/* splits on the selection start */
+			List<Clip> splitList = splitList(selection.getStart(),ClipList.this);
+			Iterator<Clip> iterator = splitList.iterator();
+			
+			/* removes all the clips at the left of selection start */
+			while(iterator.hasNext()){
+				Clip c = iterator.next();
+				if(c.getStart() < selection.getStart()){ // not equal because when splitting at position 
+					iterator.remove();                   // position will go with the right split 
+				}
+			}
+			
+			/* splits what's left at the selection end */
+			splitList = splitList(selection.getEnd(),splitList);
+			iterator = splitList.iterator();
+			
+			/* removes all the clips at the right of selectino end */
+			while(iterator.hasNext()){
+				Clip c = iterator.next();
+				if(c.getEnd() >= selection.getEnd()){
+					iterator.remove();
+				}
+			}
+			
+			copiedClips = splitList;
+			
+			if(splitList.isEmpty()){
+				return false;
+			}else{
+				fireSoundWaveListeners(new SoundWaveEvent(wave,SoundWaveEvent.COPY,copiedClips));
+				return true;
+			}
+		}
+	
+		@Override
+		public boolean paste(SoundWave wave, int position) {
+			if(copiedClips.isEmpty()){
+				return false;
+			}
+
+			/* shift the clips to the new position */
+			for(Clip c : copiedClips){
+				/* shift the clip to the new position. Keeps into account the position relative *
+				 * to the original copy selection (c.getStart() - copySelection.getStart()).    *
+				 * This is to have the clips original to be pasted as they were in the original *
+				 * selection, avoid them to all be to be placed with start at position          */
+				Clip newClip = new Clip(c);
+				newClip.setStartAt(position + c.getStart() - copySelection.getStart());
+				
+				/* add the new clips after shifting them to the new position */
+				add(newClip);
+			}
+			
+			fireSoundWaveListeners(new SoundWaveEvent(wave,SoundWaveEvent.PASTE,copiedClips));
+			return true;
+		}
+		
+		@Override
+		public boolean insert(SoundWave wave, int position){
+			if(copiedClips.isEmpty()){
+				return false;
+			}
+			
+			/* split the list at position */
+			List<Clip> splitList = ClipList.splitList(position, ClipList.this);
+			
+			/* shift right all the clips at the right of position */
+			for(Clip c : splitList){
+				if(c.getStart() >= position){ // >= because when split at position, position will go to the right split 
+					c.shift((int)copySelection.lenght());
+				}
+			}
+			
+			/* insert the copied clips */
+			for(Clip c : copiedClips){
+				/* shift the clip to the new position. Keeps into account the position relative *
+				 * to the original copy selection (c.getStart() - copySelection.getStart()).    *
+				 * This is to have the clips original to be pasted as they were in the original *
+				 * selection, avoid them to all be to be placed with start at position          */
+				Clip newClip = new Clip(c);
+				newClip.setStartAt(position + c.getStart() - copySelection.getStart());
+				
+				/* add the new clips after shifting them to the new position */
+				splitList.add(newClip);
+			}
+			
+			/* update this clip list with the new list */
+			ClipList.this.clear();
+			ClipList.this.addAll(splitList);
+			
+			fireSoundWaveListeners(new SoundWaveEvent(wave,SoundWaveEvent.INSERT,copiedClips));
+			return true;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Daw.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,64 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import javax.swing.SwingUtilities;
+
+import uk.ac.qmul.eecs.depic.daw.beads.BeadsSoundEngineFactory;
+import uk.ac.qmul.eecs.depic.daw.gui.MainFrame;
+
+public class Daw implements Runnable{
+	private static SoundEngineFactory soundEngineFactory;
+
+	/**
+	 * Main function. Calls {@code run()} in the Event dispatching thread.
+	 * 
+	 * @param args command line argument vector 
+	 */
+	public static void main(String[] args) {
+		System.out.println("Digital Audio Workstation prototype running on Java "+
+				System.getProperty("java.version")+" "+System.getProperty("os.arch"));
+		Daw daw = new Daw();
+		try {
+            SwingUtilities.invokeAndWait(daw);
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+	}
+	
+	/**
+	 * Creates a new {@code MainFrame} and makes it visible in the Event Dispatching Thread.
+	 */
+	public void run(){
+		MainFrame frame = new MainFrame();
+		/* become visible */
+		frame.pack();
+		frame.setVisible(true);
+	}
+	
+	public static SoundEngineFactory getSoundEngineFactory(){
+		if(soundEngineFactory == null){
+			soundEngineFactory = new BeadsSoundEngineFactory();
+		}
+		return soundEngineFactory;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/DbWave.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,307 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+
+public class DbWave implements Wave {
+	private Wave soundWave ;
+	private Sequence peakLevelSequence;
+	
+	public DbWave(Wave soundWave){
+		this.soundWave = soundWave;
+	}
+	
+	@Override
+	public int getChunkNum() {
+		return soundWave.getChunkNum();
+	}
+
+	@Override
+	public Chunk getChunkAt(int i) {
+		return new DBChunk(soundWave.getChunkAt(i));
+	}
+
+	@Override
+	public void setScaleFactor(int scaleFactor) {
+		soundWave.setScaleFactor(scaleFactor);
+	}
+
+	@Override
+	public int getScaleFactor() {
+		return soundWave.getScaleFactor();
+	}
+
+	@Override
+	public int getMaxScaleFactor() {
+		return soundWave.getMaxScaleFactor();
+	}
+
+	@Override
+	public float getWaveTime() {
+		return soundWave.getWaveTime();
+	}
+
+	@Override
+	public float getMillisecPerChunk() {
+		return soundWave.getMillisecPerChunk();
+	}
+	
+	/**
+	 * Returns a sequence representing the peak meter line, if any 
+	 * 
+	 * @return the peak level sequence or {@code null} if this wave doesn't have a sequence 
+	 */
+	@Override
+	public Sequence getSequence(){
+		return peakLevelSequence;
+	}
+	
+	public void createNewSequence(){
+		peakLevelSequence = new PeakMeterSequence(this);
+	}
+	
+	public void deleteSequence(){
+		peakLevelSequence = null;
+	}
+	
+	@Override 
+	public boolean hasSequence(){
+		return (peakLevelSequence != null);
+	}
+
+
+	private static class PeakMeterSequence implements Sequence {
+		private static final float TAN_THETA = 1/5000.0f; 
+		float previousPeak;
+		float previousPeakTime;
+		Range<Float> range;
+		List<Sequence.Value> values;
+		private float len;
+		
+		PeakMeterSequence(Wave w){
+			previousPeak = 0.0f;
+			previousPeakTime = 0.0f;
+			values = new ArrayList<>(w.getChunkNum()/5);
+			range = new Range<Float>(0.0f,1.0f);
+			len =  w.getChunkNum()*w.getMillisecPerChunk();
+			
+			for(int i=0; i<w.getChunkNum();i++){
+				Chunk c = w.getChunkAt(i);
+				final float time = i*w.getMillisecPerChunk();
+				
+				/* decayTime is the time where the previous peak was, plus the time  *
+				 * that the peak line will take, to decay to zero                    */
+				float decayTime = previousPeakTime +(previousPeak / TAN_THETA);
+				
+				float peakLineDecayPoint = 0.0f;
+				/* if the peak line has already decayed to zero in the past, * 
+				 * peakLineDecayPoint will just be left to 0                 */
+				if(time < decayTime || MathUtils.equal(time, decayTime,w.getMillisecPerChunk()/2)){
+					/* timeLeftToZero is always > 0, otherwise would not be in this block  */
+					float timeLeftToZero = decayTime - time;
+					/* returns the y value of the peak line at timePoistion */
+					peakLineDecayPoint = timeLeftToZero * TAN_THETA;
+				}
+				
+				if(c.getNormEnd() > peakLineDecayPoint) {
+					final int index = values.size();
+					final float value = c.getNormEnd();
+					
+					/* this chunk was higher than the peak line at time * 
+					 * position therefore it becomes the new peak value */
+					previousPeak = value;
+					previousPeakTime = time;
+					
+					values.add(new Sequence.Value() {
+						@Override
+						public int index() {
+							return index;
+						}
+						
+						@Override
+						public float getValue() {
+							return value;
+						}
+						
+						@Override
+						public float getTimePosition() {
+							return time;
+						}
+						
+						@Override
+						public Sequence getSequence() {
+							return PeakMeterSequence.this;
+						}
+						
+						@Override
+						public String toString(){
+							return "Peak meter sequence value. Value:"+getValue()+" time pos:"+getTimePosition();
+						}
+					});
+				}else if( MathUtils.equal(time, decayTime, w.getMillisecPerChunk())){
+					previousPeak = 0.0f;
+					previousPeakTime = time;
+					final int index = values.size();
+					values.add(new Sequence.Value() {
+						@Override
+						public int index() {
+							return index;
+						}
+						
+						@Override
+						public float getValue() {
+							return 0.0f;
+						}
+						
+						@Override
+						public float getTimePosition() {
+							return time;
+						}
+						
+						@Override
+						public Sequence getSequence() {
+							return PeakMeterSequence.this;
+						}
+						
+						@Override
+						public String toString(){
+							return "Peak meter sequence value. Value:"+getValue()+" time pos:"+getTimePosition();
+						}
+					});
+				}
+			}
+		}
+		
+		/**
+		 * Calculates the point on the time line (x-axis) where the peak curve will be completely decayed 
+		 * to zero. 
+		 * 
+		 * @param time the time of the decay point 
+		 * @param previousPeak the previous peak value. that is where the line has started decaying 
+		 * @param previousPeakTime the time of the previous peak value 
+		 *  
+		 * @return
+		 */
+		float calculateDecayLineAtTime(float time,float previousPeak, float previousPeakTime){
+			/* decayTime is the time where the previous peak was plus the time  *
+			 * that the peak line will take to decay to zero                    */
+			float decayTime = previousPeakTime +(previousPeak / TAN_THETA);
+			
+			/* the peak line has already decayed to zero in the past */
+			if(time > decayTime){
+				return  0.0f;
+			}
+			
+			/* always > 0, otherwise would have returned already */
+			float timeLeftToZero = decayTime - time;
+			/* returns the y value of the peak line at timePoistion */
+			return timeLeftToZero * TAN_THETA;
+		}
+		
+		@Override
+		public float getBegin() {
+			return 0.0f;
+		}
+	
+		@Override
+		public float getEnd() {
+			return values.isEmpty() ? getBegin() : values.get(values.size()-1).getValue();
+		}
+	
+		@Override
+		public int getValuesNum() {
+			return values.size();
+		}
+	
+		@Override
+		public Value getValueAt(int index) {
+			return values.get(index);
+		}
+	
+		@Override
+		public Range<Float> getRange() {
+			return range;
+		}
+	
+		@Override
+		public void addSequenceListener(SequenceListener l) {
+			// this sequence is immutable, therefore listeners would never be notfied
+		}
+	
+		@Override
+		public void removeSequenceListener(SequenceListener l) {
+			// this sequence is immutable, therefore listeners would never be notfied
+		}
+	
+		@Override
+		public float getLen() {
+			return len;
+		}
+		
+	}
+	
+}
+
+class DBChunk extends Chunk {
+	float normStart;
+	float normEnd;
+	
+	DBChunk(Chunk c) {
+		super((short)0,(short)0);
+		start = MathUtils.toDb(Math.abs(c.getStart()));
+		if(Float.isInfinite(start)){
+			start = 0.0f;
+		}
+		end = MathUtils.toDb(Math.abs(c.getEnd()));
+		if(Float.isInfinite(end)){
+			end = 0.0f;
+		}
+		
+		normStart = 0.0f;
+		
+		float max = Math.max(Math.abs(c.getStart()),Math.abs(c.getEnd()));
+		float db = MathUtils.toDb(max);
+		if(Float.isInfinite(db)){
+			normEnd = 0;
+		}else if(db < -60){
+			normEnd = 0;
+		}else{
+			normEnd = new MathUtils.Scale(-60.0f, 0.0f, 0.0f, 1.0f).linear(db);
+		}
+	}
+
+	@Override
+	public float getNormStart() {
+		return normStart;
+	}
+
+	@Override
+	public float getNormEnd() {
+		return normEnd;
+	}
+	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Direction.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,27 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+public enum Direction {
+	UP,
+	DOWN,
+	LEFT,
+	RIGHT,
+	NONE
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Parameter.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,108 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+public interface Parameter {
+	public interface Type {
+		String getLabel();
+		
+		String getUnitofMeasurment();
+	}
+	
+	public Type GAIN_TYPE = new Type(){
+		@Override
+		public String getLabel() {
+			return "Gain ";
+		}
+		
+		@Override
+		public String getUnitofMeasurment(){
+			return "db";
+		}
+	};
+	
+	public Type PAN_TYPE = new Type(){
+		@Override
+		public String getLabel() {
+			return "Pan ";
+		}
+		
+		@Override
+		public String getUnitofMeasurment(){
+			return "";
+		}
+	};
+	
+	public float getValue();
+	
+	public void setValue(float value);
+	
+	public float getInitialValue();
+	
+	public Range<Float> getRange();
+	
+	public Type getType();
+	
+	public void automate(Automation a);
+	
+	public static final Parameter NONE_PARAMETER = new Parameter() {
+		private Type NONE_TYPE = new Type(){
+			@Override
+			public String getLabel() {
+				return "None";
+			}
+			
+			@Override
+			public String getUnitofMeasurment(){
+				return "";
+			}
+		};
+		
+		Range<Float> r = new Range<Float>(0.0f,0.0f);
+		
+		@Override
+		public float getValue() {
+			return 0;
+		}
+
+		@Override
+		public void setValue(float value) {	}
+
+		@Override
+		public Range<Float> getRange() {
+			return r;
+		}
+
+		@Override
+		public Type getType() {
+			return NONE_TYPE;
+		}
+
+		@Override
+		public void automate(Automation a) {}
+
+		@Override
+		public float getInitialValue() {
+			return 0;
+		}
+		
+	};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/ParametersControl.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,37 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+
+public interface ParametersControl {
+	
+	Parameter  getGainParameter();
+	
+	Parameter  getPanParameter();
+	
+	public Automation getCurrentAutomation();
+	
+	public Parameter getCurrentAutomationType();
+	
+	public void setCurrentAutomation(Parameter.Type type);
+	
+	public Automation getAutomation(Parameter.Type type);
+	
+	public Parameter getParameter(Parameter.Type type);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Sample.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,49 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+
+public interface Sample {
+
+	public int getBytesPerSample();
+
+	public void getFrame(int arg0, float[] arg1);
+
+	public void getFrameCubic(double arg0, float[] arg1);
+
+	public void getFrameLinear(double arg0, float[] arg1);
+
+	public void getFrameNoInterp(double arg0, float[] arg1);
+
+	public void getFrames(int arg0, float[][] arg1);
+
+	public float getLength();
+
+	public int getNumChannels();
+
+	public long getNumFrames();
+
+	public float getSampleRate();
+
+	public double msToSamples(double arg0);
+
+	public double samplesToMs(double arg0);
+
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Selection.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,165 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+		
+public final class Selection extends Range<Integer>{
+	public static Selection ZERO_SELECTION = new Selection(0,1);
+	private int scaleFactor; 
+	private boolean open;
+	
+	/**
+	 * Constructs a new instance of {@code LoopPoints} from the bound values passed as argument. 
+	 *
+	 * The lower value is assigned to the start of the loop, whereas the higher value is assigned to the end of the loop.
+	 * If the two values are equals the loop is assumed to have no end but only a starting point.  
+	 *
+	 * @param start the start of the range, it must be greater or equal than 0 
+	 * @param end the end of the range, it must be either greater or equal than 0 or equal to {@code NO_POSITION}
+	 * @param scaleFactor 
+	 * 
+	 * @throws IndexOutOfBoundsException whether {@code start} is a negative
+	 *         value or {@code end} is a negative value different from {@code NO_POSITION}
+	 * @throws IllegalArgumentException if {@code end} is greater than {@code start}
+	 */
+	public Selection(int start, int end, int scaleFactor) {	
+		super(start,end);
+		if(start < 0 && !equals(ZERO_SELECTION)){
+			throw new IndexOutOfBoundsException("bounds value must be a positive integer . start:"+start);
+		}
+		if(end < 0){
+			throw new IndexOutOfBoundsException("bounds value must be a positive integer end:"+end);
+		}
+		if(start > end ){
+			throw new IllegalArgumentException("start cannot be greater than end. start:"+start+" end:"+end);
+		}
+		
+		open = false;
+		this.scaleFactor = scaleFactor; // FIXME controls on scaleFactor 
+	}
+	
+	public Selection(int start, int scaleFactor){
+		super(start,start);   
+		if(start < 0){
+			throw new IndexOutOfBoundsException("bounds value must be a positive integer . start:"+start);
+		}
+		
+		this.scaleFactor = scaleFactor;
+		open = true;
+	}
+	
+	/**
+	 * Returns the end value of the loop
+	 * 
+	 * @return the end point or -1 if the loop has only the start and no end to indicate the final frame. 
+	 * This value is consistent with the {@code Clip interface} 
+	 * 
+	 *  @see javax.sound.sampled.Clip#setLoopPoints(int, int)
+	 */
+	@Override
+	public final Integer getEnd(){
+		if(open)
+			return -1;
+		else
+			return super.getEnd();
+	}
+
+	/**
+	 * Returns the scale factor of this selection 
+	 * 
+	 * @return the scale factor of this selection
+	 */
+	public int getScaleFactor(){
+		return scaleFactor;
+	}
+	
+	/**
+	 * Checks whether the selection defines an open or closed interval.  
+	 * 
+	 * The selection is open is the end is equal to {@code NO_POSITION}.
+	 * 
+	 * @return {@code true} is the selection is open, {@code false} otherwise
+	 */
+	public boolean isOpen(){
+		return open;
+	}
+	
+	
+	
+	/**
+	 * Returns {@code true} if {@code p} is contained in the selection ( p is greater than {@code getStart()}
+	 * and smaller than {@code getEnd()} ).
+	 * 
+	 *  If the selection is open, {@code false} is returned.
+	 * 
+	 * @param p the integer vale to check for
+	 * 
+	 * @return @return {@code true} if {@code p} is contained in the selection, {@code false} otherwise
+	 */
+	public boolean contains(int p){
+		if(isOpen()){
+			return false;
+		}else{
+			return ((p>=getStart()) && (p<=getEnd()));			
+		}
+		
+	}
+	
+	@Override
+	public String toString(){
+		if(open)
+			return getClass().getSimpleName()+" ["+getStart()+",inf), scale factor:"+scaleFactor;
+		else
+			return getClass().getSimpleName()+" ["+getStart()+","+getEnd()+"], scale factor:"+scaleFactor;
+	}
+	
+	/**
+	 * Convert a selection to another selection with a new scale factor.
+	 * 
+	 * The {@code start} and {@code end} of the selection are changed accordingly. 
+	 * 
+	 * @param s the selection to convert 
+	 * @param newFactor the factor of the converted selection  
+	 * @return {@code s} if {@code newFactor} is already the scale factor of {@code s}, a new selection 
+	 *    
+	 */
+	public static Selection convertFactor(Selection s, int newFactor){
+		if(s.getScaleFactor() == newFactor)
+			return s;
+		int newStart = (int)(s.getStart() * Math.pow(2,s.getScaleFactor()-newFactor));
+		if(!s.isOpen()){
+			int newEnd = (int)(s.getEnd() * Math.pow(2,s.getScaleFactor()-newFactor));
+			return new Selection(newStart,newEnd,newFactor);
+		}else{
+			return new Selection(newStart,newFactor);			
+		}
+	}
+	
+	public static int convertFactor(int position, int fromFactor, int toFactor){
+		if(fromFactor == toFactor){
+			return position;
+		}
+			
+		return (int)(position * Math.pow(2,fromFactor-toFactor));
+	}
+	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Sonification.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,35 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+public interface Sonification {
+
+	public SequenceMapping getSequenceMapping(SoundType soundType);
+	
+	public void withinSelection(float g, float p);
+	
+	public void outsideSelection();
+	
+	public void play(SoundType type);
+	
+	public void play(SoundType type, float gain, float pan);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Sound.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,93 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+public class Sound {
+	private Enum<?> type;
+	private float pan;
+	private float pitch;
+	private float gain;
+	private Object soundSource;
+	private PropertyChangeSupport changeSupport;
+	
+	public Sound(Enum<?> type) {
+		this.type= type;
+		gain = 1.0f;
+		pan = 0.0f;
+		pitch = 440.0f;
+		changeSupport = new PropertyChangeSupport(this);
+	}
+	
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		changeSupport.addPropertyChangeListener(listener);
+    }
+	
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		changeSupport.removePropertyChangeListener(listener);
+    }
+	
+	public Enum<?> getType(){
+		return type;
+	}
+	
+	public float getPitch() {
+		return pitch;
+	}
+
+	public Sound setPitch(float pitch) {
+		float oldPitch = this.pitch;
+		this.pitch = pitch;
+		changeSupport.firePropertyChange("pitch", oldPitch, pitch);
+		return this;
+	}
+
+	public float getGain() {
+		return gain;
+	}
+
+	public Sound setGain(float gain) {
+		float oldGain = this.gain;
+		this.gain = gain;
+		changeSupport.firePropertyChange("gain", oldGain, gain);
+		return this;
+	}
+
+	public Sound setPan(float pan){
+		float oldPan = this.pan;
+		this.pan = pan;
+		changeSupport.firePropertyChange("pan", oldPan, pan);
+		return this;
+	}
+	
+	public float getPan(){
+		return pan;
+	}
+	
+	public Object getSource(){
+		return soundSource;
+	}
+	
+	@Override
+	public String toString(){
+		return "Sound type:"+getType();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/SoundEngineFactory.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,28 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+
+public interface SoundEngineFactory {
+	public SoundWave createSoundWave();
+	
+	public Parameter createParameter(Parameter.Type type, SoundWave wave);
+	
+	public Sonification getSharedSonification();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/SoundType.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,31 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+public enum SoundType {
+	HAPTIC_PORT_TOUCH,
+	ERROR,
+	SHIFT_START,
+	SHIFT_END,
+	OK,
+	AUTOMATION,
+	LOOP,
+	PEAK_LEVEL,
+	ZOOM_LEVEL;		
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/SoundWave.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,158 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.List;
+
+import javax.swing.JComponent;
+
+public interface SoundWave extends Wave {
+
+	public static final String FILE_ERROR_PROPERTY = "FILE_ERROR_PROPERTY";
+	public static final int FILE_LOAD_TOTAL_PROGRESS = 100;
+	public static final int MIN_SUPPORTED_CHUNK_SIZE = 1;
+	public static final int STOP_SCANNING = -100;
+
+	
+
+	/**
+	 * Adds a {@code SoundWaveListener} to this {@code SoundWave}. 
+	 * 
+	 * The listener will be notified when a {@SoundWaveEvent} happens.
+	 * Adding twice the same object (as per the {@code equals()}) as well as 
+	 * adding {@code null} will have no effect.
+	 * 
+	 * @param l the listener to add to this {@code SoundWave}
+	 * 
+	 */
+	public void addSoundWaveListener(SoundWaveListener l);
+
+	/**
+	 * Removes {@code l} from the listeners if it has been previously added.
+	 *  
+	 * @param l the listener to remove
+	 */
+	public void removeSoundWaveListener(SoundWaveListener l);
+
+	
+	/**
+	 * 
+	 * 
+	 * @param stream a stream of audio data. The stream is wrapped with a {@code BufferedInputStream} before
+	 * being used. 
+	 * @param propertyChangelistener
+	 */
+	public void loadAudioData(File audioFile,PropertyChangeListener propertyChangelistener);
+
+	public void stopLoading(boolean mayInterruptIfRunning);
+	
+	/**
+	 * Disposes the current audio data. 
+	 * 
+	 * Registered {@code SoundWaveListener} objects are notified with a 
+	 * {@code CLOSE} event. A call to this method has no effect if no audio data is loaded.
+	 * 
+	 */
+	public void close();
+	
+	public DbWave getDbWave();
+	
+	public void generatePeakMeter(boolean generate);
+	
+	public TransportControl getTransportControl();
+	
+	public int getCurrentChunkPosition();
+	
+	public void scan(int position);
+	
+	public JComponent [] getPreferencesPanels();
+	
+	/**
+	 * Sets a new selection for this sound wave.
+	 * 
+	 * Registered {@code SoundWaveListener} objects are notified with a 
+	 * {@code SELECTION_CHANGED} event.
+	 * 
+	 * @param selection the new selection. Using {@code null} or {@code Selection.VOID_SELECTION} will remove the selection  
+	 */
+	public void setSelection(Selection selection);
+	
+	/**
+	 * Sets a new position for this sound wave.
+	 * 
+	 * Registered {@code SoundWaveListener} objects are notified with a 
+	 * {@code POSITION_CHANGED} event. The event argument is an open {@code Selection} 
+	 * whose start is the new position and scale factor is 1. 
+	 * 
+	 * @param position the new position 
+	 */
+	public void setPosition(int position);
+
+	
+	/**
+	 * 
+	 * @param chuckPosition
+	 * @param scaleFactor
+	 * @return or {@code -1} if chunkPosition is greater than the chunk number to less than 0 
+	 */
+	public List<Integer> getFramesPositionAt(int chuckPosition);
+	
+	
+	public SoundWaveEditor getEditor();
+	
+	/**
+	 * The current Automation 
+	 * 
+	 * @return the current {@code Automation} object or {@code null} if no sample is loaded and 
+	 */
+	
+	
+	public ParametersControl getParametersControl();
+	
+	
+	public interface TransportControl { 
+		
+		/**
+		 * Plays the sound sample.
+		 * 
+		 *  Registered {@code SoundWaveListener} objects are notified with a 
+		 * {@code START} event. 
+		 * 
+		 *  
+		 */
+		public void play();
+		
+		public void setLoop(boolean on);
+		
+		public boolean isLoopOn();
+		
+		public void stop();
+		
+		public void pause();
+		
+		public void rew(); 
+		
+		public void fwd();
+		
+		public float getPlayPosition();
+	}
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/SoundWaveEditor.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,29 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+public interface SoundWaveEditor {
+	public boolean cut(SoundWave wave, Selection s);
+	
+	public boolean copy(SoundWave wave, Selection s);
+	
+	public boolean paste(SoundWave wave, int position);
+	
+	public boolean insert(SoundWave wave, int position);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/SoundWaveEvent.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,144 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.util.EventObject;
+
+public class SoundWaveEvent extends EventObject {
+	private static final long serialVersionUID = 1L;
+	private Object args;
+	private String type;
+	
+	/**
+	 * Creates a new Sound event with no argument 
+	 * @param soundWave
+	 * @param type
+	 */
+	public SoundWaveEvent(SoundWave soundWave, String type){
+		super(soundWave);
+		this.type = type;
+	}
+	
+	public SoundWaveEvent(SoundWave soundWave, String type, Object args){
+		super(soundWave);
+		this.type = type;
+		setArgs(args);
+	}
+	
+	/**
+	 * Returns a reference to the sound wave this event originated from
+	 * 
+	 *  @return the sound wave this event originated from
+	 */
+	@Override
+	public SoundWave getSource(){
+		return (SoundWave)super.getSource();
+	}
+	
+	public String getType(){
+		return type;
+	}
+
+	/**
+	 * Returns the arguments of this event. 
+	 * 
+	 * Different types of event require different type of arguments. See the static event 
+	 * types to know which argument they come with. 
+	 * 
+	 * @return the argument object or {@code null} if this event was constructed with 
+	 * no arguments. 
+	 */
+	public Object getArgs() {
+		return args;
+	}
+
+	public void setArgs(Object args) {
+		this.args = args;
+	}
+	
+	/**
+	 * Event generated when an audio file is open and a new sound wave is created.
+	 */
+	public static final String OPEN = "OPEN";
+	/**
+	 * /**
+	 * Event generated when the sound wave is closed and all the related resources
+	 * (sound file descriptors etc.) disposed.
+	 */
+	public static final String CLOSE = "CLOSE";
+	/**
+	 * Event generated when the sound wave selection changes.
+	 * 
+	 * The event argument is a {@code Selection} object with the new selection. 
+	 */
+	public static final String SELECTION_CHANGED = "SELECTION_CHANGED";
+	/**
+	 * Event generated when the sound wave's cursor position changes as a 
+	 * consequence of scrubbing through the sound wave. 
+	 * 
+	 * The argument is an open {@code Selection}, normalized to {@code SoundWave}'s current scale factor, 
+	 * whose start value is the new position.  
+	 * 
+	 *  @see Selection#convertFactor(Selection, int)
+	 */
+	public static final String POSITION_CHANGED = "POSITION_CHANGED";
+	/**
+	 * Event generated when the sound wave playback is started.
+	 */
+	public static final String START = "START";
+	/**
+	 * Event generated when the sound wave playback is stopped.
+	 */
+	public static final String STOP = "STOP";
+	/**
+	 * Event generated when the sound wave playback is paused.
+	 */
+	public static final String PAUSE = "PAUSE";
+	/**
+	 * Event generated when an automation is changed. 
+	 * 
+	 * The argument of this event is the new {@code Automation} object which has been set as current 
+	 * for the sound wave. If the automation was of String {@code NONE}, then the argument will be {@code null}
+	 */
+	public static final String AUTOMATION_CHANGED = "AUTOMATION_CHANGED";
+	
+	public static final String CUT = "CUT";
+	
+	public static final String COPY = "COPY";
+
+	public static final String PASTE = "PASTE";
+	
+	public static final String INSERT = "INSERT";
+	
+	public static final String PEAK_METER = "VIEW_CHANGED";
+	
+	/**
+	 * argument is the scanning poistion 
+	 */
+	public static final String SCAN = "SCAN";
+	
+	/**
+	 * Event generated when the current zoom factor of the {@code SoundWave} is changed.
+	 * 
+	 * 
+	 */
+	public static final String SCALE_FACTOR_CHANGED = "SCALE_FACTOR_CHANGED";
+		
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/SoundWaveListener.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,25 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+public interface SoundWaveListener {
+	public void update(SoundWaveEvent evt);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/Wave.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,49 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+public interface Wave {
+	public int getChunkNum();
+	
+	public Chunk getChunkAt(int i);
+	
+	/**
+	 * Sets the new scale factor. 
+	 * 
+	 * Registered {@code SoundWaveListener} objects are notified with a 
+	 * {@code SCALE_FACTOR_CHANGED} event. 
+	 * 
+	 * @param scaleFactor the new scale factor 
+	 */
+	public void setScaleFactor(int scaleFactor);
+	
+	public int getScaleFactor();
+	
+	public int getMaxScaleFactor();
+	
+	public float getWaveTime();
+	
+	public float getMillisecPerChunk();
+	
+	public Sequence getSequence();
+	
+	public boolean hasSequence();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/WavePeaks.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,59 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class WavePeaks extends ArrayList<List<Chunk>> {
+	private static final long serialVersionUID = 1L;
+	private boolean constructed;
+
+	public WavePeaks(int maxScaleFactor) {
+		super(maxScaleFactor+1);
+		/* wave peaks indexes the list of chunks by scale factor since there is 
+		 * no 0 scaleFactor the first position of the list is never used but it must
+		 * be filled up in order to avoid index out of bounds exception  */
+		for(int i=0; i<maxScaleFactor+1;i++)
+			add(null);
+		constructed = true;
+	}
+
+	@Override
+	public void add(int scaleFactor, List<Chunk> element){
+		if(constructed && scaleFactor == 0)
+			throw new IllegalArgumentException("scaleFactor cannot be 0");
+		super.set(scaleFactor, element);
+	}
+	
+	@Override
+	public boolean add(List<Chunk> element){
+		if(constructed)
+			throw new UnsupportedOperationException("Use add(scaleFactor), List<ChunkElement>");
+		else
+			return super.add(element);
+	}
+	
+	
+	public List<Chunk> withScaleFactor(int scaleFactor){
+		return super.get(scaleFactor);
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/BeadsAutomation.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,329 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.beadsproject.beads.core.UGen;
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.AutomationValue;
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.SequenceEvent;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+
+public class BeadsAutomation extends UGen implements Automation {
+	private static final float MIN_THRESHOLD = 0.05f;
+	//private Parameter parameter;
+	private BeadsSoundWave wave;
+	private List<AutomationValue> values;
+	private List<SequenceListener> listeners;
+	private Range<Float> range;
+	private Color color;
+	
+	/* begin and end values are the automation values at the ends  *
+	 * of the clip       */
+	private AutomationValue beginValue;
+	private AutomationValue endValue;
+	/* holds a copy of the values and it's accessed only by          *
+	 * calculateBuffer and recompute which are synchronized  methods */
+	private List<AutomationValue> valuesForSound;
+	private float value;
+	
+	
+	public BeadsAutomation(BeadsSoundWave wave,Range<Float> range,Color color, float initialValue){
+		super(wave.ac,1);
+		this.wave = wave;
+		this.range = range;
+		this.color = color;
+		
+		listeners = new ArrayList<>();
+		values = new ArrayList<>();
+		valuesForSound = new ArrayList<>(values);
+		
+		beginValue = new BeadsAutomationValue(0.0f,initialValue){
+			@Override
+			public float getValue() {
+				if(values.size() > 0){
+					return values.get(0).getValue();
+				}else{
+					return BeadsAutomation.this.getValue();
+				}
+			}
+		};
+		
+		endValue = new BeadsAutomationValue(wave.samplePlayer.getLenght(),initialValue){
+			/* end value always returns the last value float value */
+			@Override
+			public float getValue() {
+				if(values.size() > 0){
+					return values.get(values.size()-1).getValue();
+				}else{
+					return beginValue.getValue();
+				}
+			}
+		};
+	}
+	
+	@Override
+	public Color getColor() {
+		return color;
+	}
+	
+	@Override
+	public void setValue(float value){
+		this.value = value;
+		fireAutomationListeners(SequenceEvent.What.BEGIN_CHANGED, beginValue);
+		
+	}
+	
+	@Override
+	public float getValue(){
+		return value;
+	}
+	
+	@Override
+	public AutomationValue add(float position, float value) {
+		AutomationValue newAutomationValue = new BeadsAutomationValue(position,value); 
+		values.add(newAutomationValue);
+		/* sort the values collection by their position */
+		Collections.sort(values);
+		for(int i=0; i<values.size(); i++){
+			((BeadsAutomationValue)values.get(i)).setIndex(i);
+		}
+		recompute();
+		fireAutomationListeners(SequenceEvent.What.VALUE_ADDED,newAutomationValue);
+		return newAutomationValue;
+	}
+
+	@Override
+	public boolean remove(Sequence.Value value) {
+		boolean valuesChanged = values.remove(value);
+		if(valuesChanged){
+			recompute();
+			fireAutomationListeners(SequenceEvent.What.VALUE_REMOVED,(AutomationValue)value);
+		}
+		return valuesChanged;
+	}
+	
+	@Override
+	public float getBegin(){
+		return beginValue.getValue();
+	}
+	
+	@Override
+	public float getEnd(){
+		return endValue.getValue();
+	}
+	
+	/**
+	 * Returns the lenght of the automation in milliseconds 
+	 * 
+	 * @return the lenght of the automation in milliseconds
+	 */
+	@Override
+	public float getLen(){
+		return wave.getWaveTime();
+	}
+	
+	private synchronized void recompute(){
+		/* make a new list of values for the sound processing        * 
+		 * by making copies of values we ensure no race condition    *
+		 * between the Beads thread and the Event dispatching Thread */
+		valuesForSound.clear();
+		for(AutomationValue v : values)
+			valuesForSound.add(new BeadsAutomationValue(v.getTimePosition(),v.getValue()));
+	}
+
+	@Override
+	public void reset() {
+		values.clear();
+		recompute();
+		fireAutomationListeners(SequenceEvent.What.VALUE_CHANGED,null);
+	}
+	
+	@Override
+	public synchronized void calculateBuffer() {
+		/* look for the envelope segment where the current  *  
+		 * samplePlayer position envelope value lays        */
+		float samplePos = (float)wave.samplePlayer.getPosition();
+		for(int i = 0; i < bufferSize; ++i) {
+			AutomationValue segBegin = beginValue;
+			AutomationValue segEnd = valuesForSound.isEmpty() ? endValue : valuesForSound.get(0);
+			
+			/* at the end of the cycle segBegin and segEnd will hold the begin * 
+			 * and end of the segment where samplePos envelope values lays     */
+			for(int j=0; j<valuesForSound.size(); j++){
+				/* is the position is between this automation value and *   
+				 * the next then it lays on the segment connecting them */
+				if(samplePos > segBegin.getTimePosition() && samplePos < segEnd.getTimePosition()){
+					break;
+				}else{
+					/* prepare segBegin and segEnd for the next cycle */
+					segBegin = valuesForSound.get(j);
+					segEnd = (j != valuesForSound.size()-1) ? valuesForSound.get(j+1) : endValue ; 
+				}
+			}
+			
+			/* now segBegin and segEnd are the end of the segment where the position lays,*
+			 * so now calculate interpolation between them to get the envelope value      */
+			float segLen = segEnd.getTimePosition() - segBegin.getTimePosition();
+			if(segLen == 0){
+				bufOut[0][i] = segEnd.getTimePosition();
+				return;
+			}
+			/* let D be the distance between the sample position and the beginning of the  * 
+			 * segment then ratio is the ratio between D and the segment length 		   */
+			float ratio =  (samplePos - segBegin.getTimePosition()) / segLen;
+			
+			/* this comes from the following formula:
+			 * newBufferValue = segBegin.getValue + (ratio * (segEnd.getValue() - segBegin.getValue()) */
+			bufOut[0][i] = (1f - ratio) * segBegin.getValue() + ratio * segEnd.getValue();
+			/* clipping */
+			if(bufOut[0][i] < range.getStart() + MIN_THRESHOLD ) // FIXME
+				bufOut[0][i] = range.getStart();
+			if(bufOut[0][i] > range.getEnd())
+				bufOut[0][i] = range.getEnd();
+			
+		}
+	}
+	
+	@Override
+	public Range<Float> getRange() {
+		return range;
+	}
+	
+	@Override
+	public int getValuesNum() {
+		return values.size();
+	}
+
+	@Override
+	public AutomationValue getValueAt(
+			int index) {
+		return values.get(index);
+	}
+	
+	@Override
+	public String toString(){
+		return "Automation: range:"+range;
+	}
+	
+	@Override
+	public void addSequenceListener(SequenceListener l) {
+		listeners.add(l);
+	}
+
+	@Override
+	public void removeSequenceListener(SequenceListener l) {
+		listeners.remove(l);
+		
+	}
+
+	protected void fireAutomationListeners(SequenceEvent.What what,AutomationValue val){
+		SequenceEvent evt = new SequenceEvent(this,val,what);
+		for(SequenceListener l : listeners){
+			l.sequenceUpdated(evt);
+		}
+	}
+	
+	private class BeadsAutomationValue extends AutomationValue {
+		private int index;
+		private void setIndex(int i) {
+			index = i; 
+		}
+		
+		public BeadsAutomationValue(float position, float value) {
+			super(position, value);
+		}
+
+		@Override
+		public Automation getAutomation() {
+			return BeadsAutomation.this;
+		}
+		
+		@Override
+		public void setLocation(float position, float value){
+			int thisIndex = values.indexOf(this);
+			
+			/* check boundaries */
+			if(position < 0)
+				position = 0;
+			if(value > range.getEnd())
+				value = range.getEnd();
+			else if(value < range.getStart())
+				value = range.getStart();
+			
+			/* check position boundaries. an automation value cannot be moved left past *
+			 * the previous automation value and right past the next automation value   */
+			if(thisIndex > 0 && position < values.get(thisIndex-1).getTimePosition()){
+				position = values.get(thisIndex-1).getTimePosition(); // check left
+				Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR); // FIXME maybe check in automation graph
+			}
+			
+			if(thisIndex < values.size()-1 && values.get(thisIndex+1).getTimePosition() < position){
+				position = values.get(thisIndex+1).getTimePosition(); // check right
+				Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR); // FIXME maybe check in automation graph
+			}
+			
+			super.setLocation(position, value);
+			/*no sort necessary as by setting location the order of the values 
+    		is not changed as a value cannot be moved past its neighbours*/ 			
+			recompute();
+			fireAutomationListeners(SequenceEvent.What.VALUE_CHANGED,this);
+		}
+		
+		@Override
+		public void setTimePosition(float position){
+			super.setTimePosition(position);
+			recompute();
+			fireAutomationListeners(SequenceEvent.What.VALUE_CHANGED, this);
+		}
+		
+		@Override
+		public void setValue(float value){
+			if(value > range.getEnd())
+				value = range.getEnd();
+			else if(value < range.getStart())
+				value = range.getStart();
+			
+			super.setValue(value);
+			if(index() == 0){
+				BeadsAutomation.this.setValue(value);
+			}
+			recompute();
+			fireAutomationListeners(SequenceEvent.What.VALUE_CHANGED, this);
+		}
+
+		@Override
+		public Sequence getSequence() {
+			return BeadsAutomation.this;
+		}
+
+		@Override
+		public int index() {
+			return index;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/BeadsParametersControl.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,139 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Parameter;
+import uk.ac.qmul.eecs.depic.daw.Parameter.Type;
+import uk.ac.qmul.eecs.depic.daw.ParametersControl;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+
+class BeadsParametersControl implements ParametersControl {	
+	Parameter[] parameters;
+	GainParameter playersGain; 
+	PanParameter playersPan;
+	
+	Automation currentAutomation;
+	Parameter currentAutomationParameter;
+	Map<Parameter.Type,Automation> automations;
+	private BeadsSoundWave soundWave;
+
+	BeadsParametersControl(BeadsSoundWave soundWave){
+		this.soundWave = soundWave;
+		automations = new HashMap<>();
+		automations.put(Parameter.NONE_PARAMETER.getType(), Automation.NONE_AUTOMATION);
+		currentAutomation = Automation.NONE_AUTOMATION;
+		currentAutomationParameter = Parameter.NONE_PARAMETER;
+		
+		parameters = new Parameter[2];
+		/* gain for soundWave */
+		parameters[0] = new GainParameter(soundWave.ac, 2);
+		/* pan for soundWave */
+		parameters[1] = new PanParameter(soundWave.ac);
+	}
+	
+	
+	@Override
+	public Automation getCurrentAutomation() {
+		return currentAutomation;
+	}
+
+	@Override
+	public Parameter getCurrentAutomationType() {
+		return currentAutomationParameter;
+	}
+
+
+	@Override
+	public void setCurrentAutomation(Parameter.Type type) {
+		currentAutomation = getAutomation(type);
+		
+		currentAutomationParameter = Parameter.NONE_PARAMETER;
+		for(Parameter p : parameters){
+			if(p.getType().equals(type)){
+				currentAutomationParameter = p;
+			}
+		}
+		soundWave.fireSoundWaveListeners(SoundWaveEvent.AUTOMATION_CHANGED, currentAutomation);
+	}
+
+	@Override
+	public Automation getAutomation(Type type) {
+		if(type == null){
+			type = Parameter.NONE_PARAMETER.getType();
+		};
+		
+		Automation automation = automations.get(type);
+		
+		if(automation != null){
+			return automation;
+		}
+		
+		if(Parameter.GAIN_TYPE.equals(type)) { 
+			automation = new BeadsAutomation(soundWave,
+					parameters[0].getRange(),
+					Color.ORANGE,
+					parameters[0].getInitialValue());
+			
+			automations.put(type, automation);
+			parameters[0].automate(automation);
+			return automation;
+		}else if (Parameter.PAN_TYPE.equals(type)) {
+			automation = new BeadsAutomation(soundWave,
+					parameters[1].getRange(),
+					Color.CYAN,
+					parameters[1].getInitialValue());
+			
+			automations.put(type, automation);
+			parameters[1].automate(automation);
+			return automation;
+		}else {
+			throw new IllegalArgumentException("Parameter not recognized: "+type);
+		}
+		
+	}
+
+	@Override
+	public Parameter getParameter(Parameter.Type type) {
+		for(Parameter p : parameters){
+			if(p.getType().equals(type)){
+				return p;
+			}
+		}
+		
+		return Parameter.NONE_PARAMETER;
+	}
+
+
+	@Override
+	public Parameter getGainParameter() {
+		return parameters[0];
+	}
+
+
+	@Override
+	public Parameter getPanParameter() {
+		return parameters[1];
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/BeadsSampleWrapper.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,118 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import uk.ac.qmul.eecs.depic.daw.Sample;
+
+public class BeadsSampleWrapper implements Sample {
+	private net.beadsproject.beads.data.Sample delegate;
+	
+	net.beadsproject.beads.data.Sample getDelegate(){
+		return delegate;
+	}
+	
+	public BeadsSampleWrapper(net.beadsproject.beads.data.Sample delegate) {
+		this.delegate = delegate;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(obj instanceof BeadsSampleWrapper){
+			return equals(((BeadsSampleWrapper)obj).delegate);
+		}else{
+			return delegate.equals(obj);
+		}
+	}
+	
+
+	@Override
+	public int getBytesPerSample() {
+		return delegate.getBytesPerSample();
+	}
+
+
+	@Override
+	public void getFrame(int arg0, float[] arg1) {
+		delegate.getFrame(arg0, arg1);
+	}
+
+	@Override
+	public void getFrameCubic(double arg0, float[] arg1) {
+		delegate.getFrameCubic(arg0, arg1);
+	}
+
+	@Override
+	public void getFrameLinear(double arg0, float[] arg1) {
+		delegate.getFrameLinear(arg0, arg1);
+	}
+
+	@Override
+	public void getFrameNoInterp(double arg0, float[] arg1) {
+		delegate.getFrameNoInterp(arg0, arg1);
+	}
+
+	@Override
+	public void getFrames(int arg0, float[][] arg1) {
+		delegate.getFrames(arg0, arg1);
+	}
+
+	@Override
+	public float getLength() {
+		return delegate.getLength();
+	}
+
+	@Override
+	public int getNumChannels() {
+		return delegate.getNumChannels();
+	}
+
+	@Override
+	public long getNumFrames() {
+		return delegate.getNumFrames();
+	}
+
+	@Override
+	public float getSampleRate() {
+		return delegate.getSampleRate();
+	}
+
+	@Override
+	public int hashCode() {
+		return delegate.hashCode();
+	}
+
+	@Override
+	public double msToSamples(double arg0) {
+		return delegate.msToSamples(arg0);
+	}
+
+	public void run() {
+		delegate.run();
+	}
+
+	@Override
+	public double samplesToMs(double arg0) {
+		return delegate.samplesToMs(arg0);
+	}
+
+	@Override
+	public String toString() {
+		return delegate.toString();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/BeadsSoundEngineFactory.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,74 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import uk.ac.qmul.eecs.depic.daw.Parameter;
+import uk.ac.qmul.eecs.depic.daw.Parameter.Type;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.SoundEngineFactory;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.BeadsSonification;
+
+public class BeadsSoundEngineFactory implements SoundEngineFactory {
+	private static final int MAX_TRACK_SCALE_FACTOR = 10;
+	private static final int MIN_CHUNCKSIZE = 8;
+	private int minChunk;
+	private int maxScaleFactor;
+	private int initialScaleFactor;
+	private Sonification sonification;
+	
+	public BeadsSoundEngineFactory(){
+		minChunk = MIN_CHUNCKSIZE;
+		maxScaleFactor = MAX_TRACK_SCALE_FACTOR;
+		initialScaleFactor = MAX_TRACK_SCALE_FACTOR/2 + 1;
+		sonification = new BeadsSonification();
+	}
+	
+	@Override
+	public SoundWave createSoundWave() {
+		return new BeadsSoundWave(
+				minChunk,
+				maxScaleFactor,
+				initialScaleFactor);
+	}
+	
+	public BeadsSoundEngineFactory setSoundWaveParameters(int minChunk,int maxScaleFactor, int initialScaleFactor){
+		this.minChunk = minChunk; 
+		this.maxScaleFactor = maxScaleFactor;
+		this.initialScaleFactor = initialScaleFactor;
+		return this;
+	}
+
+	@Override
+	public Parameter createParameter(Type type, SoundWave wave) {	
+		if(Parameter.GAIN_TYPE.equals(type)) {
+			return new GainParameter(((BeadsSoundWave)wave).ac,2);
+		}else if(Parameter.PAN_TYPE.equals(type)) {  
+			return new PanParameter(((BeadsSoundWave)wave).ac);
+		}else{
+			return null;
+		}
+	}
+
+	@Override
+	public Sonification getSharedSonification() {
+		return sonification;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/BeadsSoundWave.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,519 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import javax.swing.JComponent;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import uk.ac.qmul.eecs.depic.daw.AudioLoader;
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Chunk;
+import uk.ac.qmul.eecs.depic.daw.Clip;
+import uk.ac.qmul.eecs.depic.daw.ClipList;
+import uk.ac.qmul.eecs.depic.daw.DbWave;
+import uk.ac.qmul.eecs.depic.daw.Parameter;
+import uk.ac.qmul.eecs.depic.daw.ParametersControl;
+import uk.ac.qmul.eecs.depic.daw.Sample;
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEditor;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
+import uk.ac.qmul.eecs.depic.daw.WavePeaks;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+class BeadsSoundWave implements SoundWave  {
+	private Set<SoundWaveListener> listeners;
+	private int minChunkSize;
+	private int maxScaleFactor;
+	private AudioLoader worker;
+	private Selection selection;
+	private int cursorPosition; 
+	private int currentScaleFactor;
+	private int referenceScaleFactor;
+	private final TransportControl transportControl;
+	private final BeadsParametersControl parametersControl;
+	private DbWave dbWave;
+	
+	/* fields used by package classes */
+	final ClipList clips;
+	final AudioContext ac;
+	final Map<Sample, WavePeaks> peaksMap;
+	final List<GranularPlayer> granulars;
+	final SamplePlayer samplePlayer;
+	
+	
+	/**
+	 * 
+	 * @param minChunkSize the minimum chunk size in frames 
+	 * @param maxScaleFactor the maximum scale factor 
+	 */
+	public BeadsSoundWave(int minChunkSize, int maxScaleFactor, int currentScaleFactor){
+		this.minChunkSize = minChunkSize;
+		this.maxScaleFactor = maxScaleFactor; 
+		this.currentScaleFactor = currentScaleFactor;
+		referenceScaleFactor = currentScaleFactor;
+		listeners = new HashSet<>();
+		peaksMap = new HashMap<>();
+		granulars = new ArrayList<>();
+		transportControl = new BeadsTransportControl(this);
+		clips = new ClipList(currentScaleFactor,peaksMap,this);
+		/* Forwards events triggered by the ClipList */
+		clips.addSoundWaveListener(new SoundWaveListener(){
+			@Override
+			public void update(SoundWaveEvent evt) {
+				if(!evt.getType().equals(SoundWaveEvent.OPEN)){
+					for(SoundWaveListener l : listeners)
+						l.update(evt);
+				}
+			}
+		});
+		selection = Selection.ZERO_SELECTION;
+		dbWave = new DbWave(this);
+		
+		/* create and start the audio engine. Need to do it from the beginning *
+		 * in order to enable wave scrubbing when playback is stopped          */
+		ac = new AudioContext();
+		ac.start();
+		/* player of all the samples loaded in this sound wave */
+		samplePlayer = new SamplePlayer(ac,clips);
+		samplePlayer.pause(true);
+		
+		parametersControl = new BeadsParametersControl(this);
+		UGen gain = (UGen)parametersControl.getParameter(Parameter.GAIN_TYPE); 
+		UGen pan = (UGen)parametersControl.getParameter(Parameter.PAN_TYPE); 
+		
+		pan.addInput(samplePlayer);
+		gain.addInput(pan);
+		
+		//BeadsAutomation panAutom = (BeadsAutomation)parametersControl.getAutomation(Type.PAN);
+		//panAutom.addInput(pan);
+		ac.out.addInput(gain);
+
+		
+		/* parameters check */
+		if(minChunkSize < MIN_SUPPORTED_CHUNK_SIZE)
+			throw new IllegalArgumentException("minChunkSize can't be smaller than MIN_SUPPORTED_CHUNK_SIZE ("+MIN_SUPPORTED_CHUNK_SIZE+')');
+			
+		if(maxScaleFactor < 1)
+			throw new IllegalArgumentException(
+					"scaleFactor (" +maxScaleFactor+ ") must be greater or equal to 1");
+		if(!isPowerOf2(minChunkSize)){
+			throw new IllegalArgumentException("min chuck value must be an integer power of 2");
+		}
+	}
+	
+	@Override
+	public void addSoundWaveListener(SoundWaveListener l){
+		if(l != null)
+			listeners.add(l);
+	}
+	
+	@Override
+	public void removeSoundWaveListener(SoundWaveListener l){
+		listeners.remove(l);
+	}
+	
+	/**
+	 * Triggered the registered listeners after an event happens.
+	 *  
+	 * @param args
+	 */
+	protected void fireSoundWaveListeners(String type, Object args){
+		SoundWaveEvent evt = null;
+		if(args == null){
+			evt = new SoundWaveEvent(this,type);
+		}else{
+			evt = new SoundWaveEvent(this,type,args);
+		}
+		
+		for(SoundWaveListener l : listeners)
+			l.update(evt);
+	}
+	
+	@Override
+	public void loadAudioData(File audioFile, PropertyChangeListener propertyChangelistener ){
+		/* set the parameters to the worker, job's going to be done in the bg */
+		worker = new BeadsAudioLoader(audioFile,minChunkSize,maxScaleFactor);
+		worker.addPropertyChangeListener(propertyChangelistener);
+		worker.execute();
+	}
+	
+	@Override
+	public void stopLoading(boolean mayInterruptIfRunning){
+		if(worker != null)
+			worker.cancel(mayInterruptIfRunning);
+	}
+	
+	@Override
+	public void close(){
+		if(!samplePlayer.isPaused()){
+			samplePlayer.pause(true);
+		}
+
+		granulars.clear();
+		peaksMap.clear();
+		clips.clear();
+		setPosition(0);
+		fireSoundWaveListeners(SoundWaveEvent.CLOSE,null);
+	}
+	
+	@Override
+	public int getCurrentChunkPosition(){
+		return cursorPosition;
+	}
+	
+	@Override
+	public List<Integer> getFramesPositionAt(int chunkPosition){  // FIXME *** 
+		
+		List<Clip> clipsAtPos = clips.getClipsAt(chunkPosition);
+		if(clipsAtPos.isEmpty())
+			return new ArrayList<>(0);
+		
+		/* create the array list to return */
+		List<Integer> framesPositions = new ArrayList<>(clipsAtPos.size());
+		
+		for(Clip c : clipsAtPos){
+			if(c.contains(chunkPosition)){
+				/* get the frames in the converted format corresponding to * 
+				 * the beginning of the chunk at chunkPosition            */
+				int convertedFrames = chunkPosition * (minChunkSize << (currentScaleFactor-1));
+				framesPositions.add((int)(
+						convertedFrames 
+						* ((float)c.getSample().getSampleRate()/BeadsAudioLoader.DEFAULT_CONVERSION_FORMAT.getFrameRate()))
+				);
+				return framesPositions;
+			}
+		}
+
+		return framesPositions;
+	}
+	
+	@Override
+	public void setSelection(Selection s){
+		selection = s;
+		if(s == null){
+			selection = Selection.ZERO_SELECTION;
+			/* reset the sample player loop points */
+			samplePlayer.setLoopPointsFraction(0.0f,1.0f);
+		}else{ 
+			/* open selection will produces no selection    *
+			 *  just move the cursor to the selection start */
+			if(s.isOpen()){
+				selection = Selection.ZERO_SELECTION;
+				setPosition(s.getStart());
+			}
+			
+			if(selection.equals(Selection.ZERO_SELECTION)){
+				samplePlayer.setLoopPointsFraction(0,1);
+			}else{
+				float sampleSizeInChunks = clips.getLength();
+				samplePlayer.setLoopPointsFraction(
+						s.getStart()/sampleSizeInChunks,
+						s.isOpen() ? 1.0f : s.getEnd()/sampleSizeInChunks);
+			}
+		}
+		fireSoundWaveListeners(SoundWaveEvent.SELECTION_CHANGED,selection);
+	}
+	
+	@Override
+	public float getMillisecPerChunk(){ // FIXME *** 
+		return samplePlayer.getLenght()/clips.getLength();
+	}
+	
+	@Override
+	public void scan(int position){
+		/* don't go too left, past the beginning of the track */
+		if(position < 0 && position != STOP_SCANNING){
+			return;
+		}
+		
+		/* if sample is loaded update it */
+		if(samplePlayer.getLenght() != 0){
+			if(position == STOP_SCANNING){ // stop scanning
+				/* turn off all granular players */
+				for(GranularPlayer granular : granulars){
+					granular.pause(true);
+				}
+
+			}else{
+				/* active clips are assumed to always hold a different sample from one another */
+				List<Clip> activeClips = clips.getClipsAt(position);
+				
+				for(GranularPlayer granular : granulars){
+					Clip withSameSample = null;
+					for(Clip c : activeClips){
+						if(c.getSample().equals(granular.getSample())){
+							withSameSample = c;
+							break;
+						}
+					}
+					
+					if(withSameSample == null){
+						/* if not in current position, then silence it. if it was      *
+						 * active before, it gets turned of; otherwise nothing happens */
+						granular.pause(true);
+					}else{
+						granular.setPosition(
+								/* start of sample in this clip (in ms) */
+								withSameSample.getSampleStartMs() +
+								/* plus the milliseconds before position */
+								withSameSample.getMillisecPerChunk() * (position - withSameSample.getStart()) 
+								);
+						if(granular.isPaused())
+							granular.start();
+					}
+				}
+			}
+			fireSoundWaveListeners(SoundWaveEvent.SCAN, position);
+		}
+		
+		/* update the position*/
+		if(position != STOP_SCANNING){
+			setPosition(position);
+		}
+	}
+	
+	@Override
+	public void setPosition(int position){
+		/* if sample is loaded, update it */
+		if(samplePlayer.getLenght() != 0){
+			float timePosition = position * getMillisecPerChunk();
+			/* if the user clicks on a point past the loop, just ignore the loop        * 
+			 * otherwise the cursor would immediately jump at the beginning of the loop */
+			if(timePosition > samplePlayer.getLoop().getEnd()){
+				samplePlayer.setLoopType(SamplePlayer.LoopType.NO_LOOP);
+			}else{
+				samplePlayer.setLoopType(SamplePlayer.LoopType.LOOP);
+			}
+			samplePlayer.setPosition(timePosition);
+		}
+		
+		cursorPosition = position;
+	    fireSoundWaveListeners(SoundWaveEvent.POSITION_CHANGED,position);
+	}
+	
+	@Override
+	public void setScaleFactor(int scaleFactor){
+		/* just makes sure scale factor is not out of bounds */
+		if(scaleFactor > maxScaleFactor || scaleFactor < 1 )
+			throw new IllegalArgumentException("Scale factor ("+scaleFactor+") cannot be greater than max scale factor or less than 1");
+		
+		
+		/* the absolute position of the cursor changes according to the scale factor */
+		cursorPosition = Selection.convertFactor(cursorPosition, currentScaleFactor, scaleFactor);
+		currentScaleFactor = scaleFactor; // FIXME remove currentScaleFactor and use selections.getScaleFactor
+		clips.setScaleFactor(scaleFactor);
+
+		/* adjust grain attributes and playback rate according to scale factory  */
+		float msPerChunk = getMillisecPerChunk();
+		for(GranularPlayer gp : granulars){
+			gp.setGrainAttributes(msPerChunk*4, msPerChunk*4);
+			gp.setPitch((float)currentScaleFactor/referenceScaleFactor);
+		}
+
+		fireSoundWaveListeners(SoundWaveEvent.SCALE_FACTOR_CHANGED,scaleFactor);
+	}
+	
+	@Override
+	public int getScaleFactor(){
+		return currentScaleFactor;
+	}
+	
+	@Override
+	public int getMaxScaleFactor(){
+		return maxScaleFactor;
+	}
+	
+	@Override
+	public int getChunkNum(){
+		return clips.getLength();
+	}
+	
+	@Override
+	public Chunk getChunkAt(int pos){
+		List<Clip> clipsAtPos = clips.getClipsAt(pos);
+		if(clipsAtPos.isEmpty())
+			return Chunk.SILENCE;
+		
+		Chunk chunk = null;
+		for(Clip c : clipsAtPos){
+			WavePeaks g = peaksMap.get(c.getSample());
+
+			if(chunk == null){
+				chunk = g.withScaleFactor(currentScaleFactor).get(c.getSampleStart()+(pos-c.getStart()));
+			}else{
+				/* the chunk at the sample start + the position of the cursor relative to the start of the clip           *
+				 * The Chunk constructor with two chunks c1 and c2 as arguments merges the two chunks into a new one with * 
+				 * getStart = max(c1.getStarte(),c2.getStart()) and getEnd = min(c1.getEnd(),c2.getEnd())                 */
+				chunk = new Chunk(chunk,g.withScaleFactor(currentScaleFactor).get(c.getSampleStart()+(pos-c.getStart())));
+			}
+		}
+
+		return chunk;
+	}
+	
+	@Override
+	public float getWaveTime(){
+		return samplePlayer.getLenght();
+	}
+	
+
+	public TransportControl getTransportControl(){
+		return transportControl;
+	}
+	
+	@Override
+	public SoundWaveEditor getEditor(){
+		return clips.getClipEditor();
+	}
+	
+	@Override
+	public ParametersControl getParametersControl(){
+		return parametersControl;
+	}
+	
+	@Override
+	public DbWave getDbWave() {
+		return dbWave;
+	}
+	
+
+	@Override
+	public void generatePeakMeter(boolean generate){
+		if(generate){
+			dbWave.createNewSequence();
+		}else{
+			dbWave.deleteSequence();
+		}
+		/* getSequence returns null if the sequence has been deleted */
+		fireSoundWaveListeners(SoundWaveEvent.PEAK_METER,dbWave.getSequence());
+	}
+	
+	@Override
+	public Sequence getSequence(){
+		return getParametersControl().getCurrentAutomation();
+	}
+
+	/**
+	 * Returns {@code true} if this sound wave currently has an automation set, via its {@code ParametersControl} 
+	 * 
+	 *  @return {@code true} if it has an automation set, {@code false} otherwise 
+	 * 
+	 */
+	@Override
+	public boolean hasSequence(){
+		return !getParametersControl().getCurrentAutomation().equals(Automation.NONE_AUTOMATION);
+	}
+	
+	@Override
+	public JComponent[] getPreferencesPanels() {
+		return null;
+	}
+
+	/* a power of two has only one bit set to '1'  */
+	private static boolean isPowerOf2(int value){
+		if(value <= 0)
+			return false;
+		boolean bitFound = false;
+		
+		while(value != 0){
+			if((value & 1) == 1){
+				if(bitFound)         // another bit already found previously: value is not power of 2 
+					return false;
+				else                 // first bit found
+					bitFound = true; 
+			}
+			value >>= 1;
+		}
+		/* only one bit found: value is power of two */
+		return true;
+	}
+
+	private class BeadsAudioLoader extends AudioLoader{			
+		public BeadsAudioLoader(File audioFile, int minChunkSize,
+				int maxScaleFactor) {
+			super(audioFile, minChunkSize, maxScaleFactor);
+		}
+	
+		@Override
+		public void done(){
+			ReturnObject returnObject = null;
+			if(isCancelled())
+				return;
+			
+			try {
+				returnObject = get();
+			} catch (InterruptedException e) {
+				return; // user interrupted the load 
+			} catch (ExecutionException e) {
+				firePropertyChange(FILE_ERROR_PROPERTY,"",e.getMessage());
+				if(e.getCause() instanceof RuntimeException)
+					e.printStackTrace();
+				return;
+			}
+			
+			/* sets the new data underling the model */
+			peaksMap.put(returnObject.sample,returnObject.peaks);
+			List<Chunk> currentPeaks = returnObject.peaks.withScaleFactor(currentScaleFactor);
+			clips.add(new Clip(
+					cursorPosition,
+					cursorPosition+currentPeaks.size()-1, // ends points to the last chunk
+					returnObject.sample, // the sample 
+					0, // the start position n the sample 
+					returnObject.sample.getLength()/currentPeaks.size())); // the chunks of this sample at saleFactor scale factor 
+			fireSoundWaveListeners(SoundWaveEvent.OPEN,null);
+			/* sample player for listening to the sample */
+			samplePlayer.pause(true);
+			
+			GranularPlayer granular = new GranularPlayer(ac,returnObject.sample);
+			granulars.add(granular);
+			/* adds the ugen to the master gain */
+			((UGen)parametersControl.getParameter(Parameter.GAIN_TYPE)).addInput(granular);
+			
+			setProgress(FILE_LOAD_TOTAL_PROGRESS);
+			/* notify listeners */
+			setSelection(new Selection(0,1));
+			fireSoundWaveListeners(SoundWaveEvent.OPEN,null);
+		}
+	}
+
+	
+}
+
+/*  
+ * 
+ * sample rate: number of samples per second 
+ * sample size in bit: 
+ * frame: all samples for each channel at a given time
+ * frame rate: for each frame contains more sample, frame rate is slower than sample rate 
+ * frame size: header + (num samples per frame * num channels)
+ *  
+ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/BeadsTransportControl.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,99 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+
+class BeadsTransportControl implements SoundWave.TransportControl {
+	BeadsSoundWave wave;
+	private boolean loopOn;
+
+	public BeadsTransportControl(BeadsSoundWave wave) {
+		this.wave= wave;
+	}
+
+	@Override
+	public void play() {
+		if(wave.samplePlayer.getLenght() != 0){
+			/* start the audio engine */
+			wave.samplePlayer.start();
+			wave.fireSoundWaveListeners(SoundWaveEvent.START, wave.samplePlayer.getPosition());
+		}
+	}
+
+	@Override
+	public void setLoop(boolean on){
+		loopOn = on;
+		wave.samplePlayer.setLoopEnabled(loopOn);
+		
+	}
+	
+	@Override
+	public boolean isLoopOn(){
+		return loopOn;
+	}
+	
+	
+	@Override
+	public void pause() {
+		if(wave.samplePlayer.getLenght() != 0){
+			wave.samplePlayer.pause(true);
+			wave.fireSoundWaveListeners(SoundWaveEvent.PAUSE, wave.samplePlayer.getPosition());
+			
+			wave.setPosition(
+					(int) ((wave.samplePlayer.getPosition()/wave.clips.getLengthMs()) * wave.clips.getLength())
+					);
+		}
+	}
+	
+	/**
+	 * Stops playing the sound sample
+	 * 
+	 * Registered {@code SoundWaveListener} objects are notified with a 
+	 * {@code STOP} event. 
+	 * 
+	 */
+	@Override
+	public void stop() {
+		if(wave.samplePlayer.getLenght() != 0){
+			wave.samplePlayer.pause(true);
+			/* place the cursor back to the beginning of the loop */
+			wave.samplePlayer.setPosition(wave.samplePlayer.getLoop().getStart());
+			wave.fireSoundWaveListeners(SoundWaveEvent.STOP, wave.samplePlayer.getPosition());
+		}
+	}
+
+
+	@Override
+	public void rew() {
+		wave.setPosition(0);
+	}
+
+	@Override
+	public void fwd() {
+		wave.setPosition(wave.getChunkNum()-1);
+	}
+	
+	@Override
+	public float getPlayPosition(){
+		return (float)wave.samplePlayer.getPosition();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/GainParameter.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,124 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.Static;
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Parameter;
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+class GainParameter extends Gain implements Parameter {
+	public final static Range<Float> RANGE = new Range<Float>(-60.0f,6.0f);
+	
+	
+	GainParameter(AudioContext ac, int inouts, float gain) {
+		super(ac, inouts);
+		super.setGain(new Static(ac, gain));
+		
+	}
+
+	GainParameter(AudioContext ac, int inouts) {
+		super(ac, inouts);
+		super.setGain(new Static(ac, MathUtils.toAmp(getInitialValue())));
+	}
+	
+	@Override
+	public float getInitialValue(){
+		return 0.0f;
+	}
+
+	@Override
+	public Range<Float> getRange() {
+		return RANGE;
+	}
+
+	/**
+	 * @param value amp value in dB
+	 */
+	@Override
+	public synchronized void setValue(float value){
+		super.getGainUGen().setValue(MathUtils.toAmp(value));
+	}
+	
+	@Override
+	public synchronized float getValue(){
+		return getGain();
+	}
+
+	@Override 
+	public synchronized void calculateBuffer(){
+		super.calculateBuffer();
+	}
+
+	@Override
+	public Parameter.Type getType() {
+		return Parameter.GAIN_TYPE;
+	}
+
+	@Override
+	public void automate(Automation a) {
+		setGain(new DbToAmpConverter(context ,(BeadsAutomation)a));
+	}
+	
+	private static class DbToAmpConverter extends  UGen {
+
+		UGen delegate;
+		
+		DbToAmpConverter(AudioContext ac, UGen delegate){
+			super(ac, delegate.getOuts());
+			this.delegate = delegate;
+		}
+		
+		@Override
+		public void calculateBuffer() {
+			delegate.calculateBuffer();			
+		}
+		
+		@Override
+		public float getValue() {
+			return MathUtils.toAmp(delegate.getValue());
+		}
+		
+		@Override
+		public float getValue(int i, int j){
+			return MathUtils.toAmp(delegate.getValue(i, j));
+		}
+		
+		@Override
+		public double getValueDouble(){
+			return MathUtils.toAmp(delegate.getValue());
+		}
+		
+		@Override
+		public double getValueDouble(int i, int j){
+			return MathUtils.toAmp(delegate.getValue(i, j));
+		}
+		
+		@Override
+	 	public void setValue(float value){
+			delegate.setValue(MathUtils.toDb(value));
+		}
+		
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/GranularPlayer.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,138 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.core.UGenChain;
+import net.beadsproject.beads.data.Buffer;
+import net.beadsproject.beads.data.buffers.OneWindow;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.GranularSamplePlayer;
+import net.beadsproject.beads.ugens.Static;
+import uk.ac.qmul.eecs.depic.daw.Sample;
+
+/**
+ * 
+ * Plays a sample on a granular synthesizer 
+ * 
+ * It has two entries in its class Preferences:  
+ * 
+ *
+ */
+class GranularPlayer extends UGenChain {
+	/* position controls the position in the sample of the granular player */
+	private UGen position;
+	/* playerGain controls the gain of the granular player before the general        *
+	 * players gain. it is used to turn the granular synthesis sound on and off upon scrubbing */
+	private Gain playerGain;
+	/* granular player from beads */
+	private GranularSamplePlayer player;
+	
+	private Sample sample;
+	
+	public GranularPlayer(AudioContext ac, BeadsSampleWrapper sample) {
+		super(ac,0,1);
+		/* granular player for scrubbing */
+		this.sample = sample;
+		player = new GranularSamplePlayer(ac,sample.getDelegate());
+		player.setLoopType(GranularSamplePlayer.LoopType.NO_LOOP_FORWARDS);
+		
+		
+		
+		position = new Static(ac,-1.0f);
+		player.setPosition(position);
+		
+		super.pause(true);
+		player.pause(true);
+		
+		addToChainOutput(player);
+	}
+	
+	/** Turns on the granular player by (smoothly) setting granularPlayerAttack to 0 
+	 */
+	@Override
+	public void start(){
+		pause(false);
+	}
+	
+	public void setFadeRatio(float ratio){
+		Buffer b = new OneWindow().generateBuffer(OneWindow.DEFAULT_BUFFER_SIZE);
+		int fade = (int) (OneWindow.DEFAULT_BUFFER_SIZE/2.0f * ratio);
+		double halfPI = Math.PI/2;
+		for(int i=0; i< fade; i++){
+			b.buf[i] = (float)Math.sin((double)i/fade*halfPI);
+			b.buf[OneWindow.DEFAULT_BUFFER_SIZE-fade+i] = (float)Math.sin((double)halfPI - (i/fade * halfPI));
+		}
+		
+		for(int i =0; i<300;i++ )
+			System.out.println(b.buf[i]);
+		
+		player.setWindow(b);
+	}
+	
+	
+	@Override
+	public void pause(boolean paused){
+		if(paused && isPaused()) // already paused, do nothing 
+			return;	
+		player.pause(paused);
+		super.pause(paused);
+	}
+	
+	@Override
+	public void kill(){
+		playerGain.kill();
+		super.kill();
+	}
+	
+	public Sample getSample(){
+		return sample;
+	}
+	
+	/**
+	 * Sets the new position of the granular player 
+	 *  
+	 * @param newPosition position in milliseconds
+	 */
+	public void setPosition(float newPosition){
+		position.setValue(newPosition);
+	}
+
+	/**
+	 * Sets the current position of the granular player 
+	 *  
+	 * @return the current position in milliseconds
+	 */
+	public float getPosition(){
+		return position.getValue();
+	}
+	
+	public void setGrainAttributes(float size, float interval){
+		// FIXME remove methos if not used 
+//		player.getGrainSizeUGen().setValue(size);
+//		player.getGrainIntervalUGen().setValue(interval);
+		
+	}
+	
+	public void setPitch(float pitch){
+		 player.setPitch(new Static(context,pitch));
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/PanParameter.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,79 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.ugens.Panner;
+import net.beadsproject.beads.ugens.Static;
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Parameter;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+public class PanParameter extends Panner implements Parameter {
+	public final static Range<Float> RANGE = new Range<>(-1.0f,1.0f);
+
+	public PanParameter(AudioContext ac) {
+		super(ac,new Static(ac, 0.0f));
+	}
+
+	public PanParameter(AudioContext ac, float ipos) {
+		super(ac, ipos);
+	}
+
+	@Override
+	public Range<Float> getRange() {
+		return RANGE;
+	}
+
+	@Override
+	public Parameter.Type getType() {
+		return Parameter.PAN_TYPE;
+	}
+	
+	/**
+	 * Sets the pan position 
+	 * 
+	 * synchronized with {@code calculateBuffer}
+	 */
+	@Override
+	public synchronized void setValue(float v){
+		super.getPosUGen().setValue(v);
+	}
+	
+	@Override
+	public synchronized float getValue(){
+		return super.getPos();
+	}
+	
+	@Override
+	public float getInitialValue(){
+		return 0.0f;
+	}
+
+	@Override 
+	public synchronized void calculateBuffer(){
+		super.calculateBuffer();
+	}
+
+	@Override
+	public void automate(Automation a) {
+		this.setPos((BeadsAutomation)a);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/SamplePlayer.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,400 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+/*
+ * This file is part of Beads. See http://www.beadsproject.net for all information.
+ */
+
+import java.util.Collections;
+import java.util.List;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.Bead;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.ugens.Static;
+import uk.ac.qmul.eecs.depic.daw.Clip;
+import uk.ac.qmul.eecs.depic.daw.ClipList;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+/**
+ *
+ */
+public class SamplePlayer extends UGen implements SoundWaveListener {
+	
+	public static final int NUM_CHANNELS = 2;
+	public static final float ADAPTIVE_INTERP_LOW_THRESH = 0.5f;
+	public static final float ADAPTIVE_INTERP_HIGH_THRESH = 2.5f;
+	
+
+	/**
+	 * Used to determine which kind of loop the sample player will use.
+	 */
+	public static enum LoopType {
+		/** Play forwards without looping. */
+		NO_LOOP, 
+		/** Play forwards with loop. */
+		LOOP, 
+	};
+	
+
+	/** The position in milliseconds. */
+	protected double position;                
+
+	/** The rate envelope. */
+	protected UGen rateEnvelope;            
+
+	/** The millisecond position increment per sample. Calculated from the ratio of the {@link AudioContext}'s sample rate and the Sample's sample rate. */
+	protected double positionIncrement;           
+
+
+	/** The loop type. */
+	protected LoopType loopType;
+
+	/** Flag to determine whether playback starts at the beginning of the sample or at the beginning of the loop. */
+	protected boolean startLoop;
+
+
+	/** The rate. Calculated and used internally from the rate envelope. */
+	protected float rate;
+
+	/** The loop start. Calculated and used internally from the loop start envelope. */
+	protected float loopStart;
+
+	/** The loop end. Calculated and used internally from the loop end envelope. */
+	protected float loopEnd;
+	
+	/** Bead responding to sample at end (only applies when not in loop mode). */
+	private Bead endListener;
+
+	/* lenght of the sample player */
+	private float length;
+	
+	private ClipList clips;
+	
+	private List<Clip> clipsAtPosition;
+	private boolean loopEnabled;
+	
+
+	/**
+	 * Instantiates a new SamplePlayer with given number of outputs.
+	 * 
+	 * @param context the AudioContext.
+	 * @param outs the number of outputs.
+	 */
+	public SamplePlayer(AudioContext context, ClipList clips) {
+		super(context, NUM_CHANNELS);
+		this.clips = clips;
+		clipsAtPosition = Collections.emptyList();
+		clips.addSoundWaveListener(this);
+		rateEnvelope = new Static(context, 1.0f);
+		loopType = LoopType.NO_LOOP;
+		loopStart = 0.0f;
+		loopEnd = clips.getLength();
+		length = 0.0f;
+		positionIncrement = context.samplesToMs(1);
+	}
+
+
+	/**
+	 * Sets the playback position to the end of the Sample.
+	 */
+	public void setToEnd() {
+		position = length;
+	}
+
+	/**
+	 * Determines whether the playback position is within the loop points.
+	 * 
+	 * @return true if the playback position is within the loop points.
+	 */
+	public boolean inLoop() {
+		return position < Math.max(loopStart, loopEnd) && position > Math.min(loopStart, loopEnd);
+	}
+
+	/**
+	 * Sets the playback position to the loop start point.
+	 */
+	public void setToLoopStart() {
+		position = Math.min(loopStart, loopEnd);
+	}
+
+	/**
+	 * Starts the sample at the given position.
+	 * 
+	 * @param msPosition the position in milliseconds.
+	 */
+	public void start(float msPosition) {
+		position = msPosition;
+		start();
+	}
+
+	/**
+	 * Resets the position to the start of the Sample.
+	 */
+	public void reset() {
+		position = 0f;
+	}
+
+	/**
+	 * Gets the playback position.
+	 * 
+	 * @return the position in milliseconds.
+	 */
+	public double getPosition() {
+		return position;
+	}
+
+	/**
+	 * Sets the playback position. This will not work if the position envelope is not null.
+	 * 
+	 * @param position the new position in milliseconds.
+	 */
+	public void setPosition(double position) {
+		this.position = position;
+	}
+
+
+	/**
+	 * Gets the rate UGen.
+	 * 
+	 * @return the rate UGen.
+	 */
+	public UGen getRateUGen() {
+		return rateEnvelope;
+	}
+	
+	
+	/**
+	 * Sets the rate to a UGen.
+	 * 
+	 * @param rateUGen the new rate UGen.
+	 */
+	public void setRate(UGen rateUGen) {
+		this.rateEnvelope = rateUGen;
+	}
+	
+	/**
+	 * Gets the rate UGen (this method is provided so that SamplePlayer and GranularSamplePlayer can 
+	 * be used interchangeably).
+	 * 
+	 * @return the rate envelope.
+	 */
+	public UGen getPitchUGen() {
+		return rateEnvelope;
+	}
+
+	
+	/**
+	 * Sets the rate UGen (this method is provided so that SamplePlayer and GranularSamplePlayer can 
+	 * be used interchangeably).
+	 * 
+	 * @param rateUGen the new rate UGen.
+	 */
+	public void setPitch(UGen rateUGen) {
+		this.rateEnvelope = rateUGen;
+	}
+
+	
+
+	/**
+	 * Sets both loop points to static values as fractions of the Sample length, 
+	 * overriding any UGens that were controlling the loop points.
+	 * 
+	 * @param start the start value, as fraction of the Sample length.
+	 * @param end the end value, as fraction of the Sample length.
+	 */
+	public void setLoopPointsFraction(float start, float end) {
+		float l = getLenght();
+		loopStart = start * l;
+		loopEnd = end * l;
+	}
+	
+	public Range<Float> getLoop(){
+		return new Range<Float>(loopStart,loopEnd);
+	}
+
+	/**
+	 * Gets the loop type.
+	 * 
+	 * @return the loop type.
+	 */
+	public LoopType getLoopType() {
+		return loopType;
+	}
+
+	/**
+	 * Sets the loop type.
+	 * 
+	 * @param loopType the new loop type.
+	 */
+	public void setLoopType(LoopType loopType) {
+		if(loopEnabled){
+			this.loopType = loopType;
+		}
+	}
+
+	
+	public void setLoopEnabled(boolean enabled){
+		loopEnabled = enabled;
+		this.loopType = loopEnabled ? LoopType.LOOP : LoopType.NO_LOOP;
+	}
+	
+	/**
+	 * Gets the sample rate.
+	 * 
+	 * @return the sample rate, in samples per second.
+	 */
+	public float getSampleRate() {
+		return rate;
+	}
+
+	
+	@Override
+	public void calculateBuffer(){
+		//major speed up possible here if these envelopes are all either null or paused 
+		//(can we pause Envelope when it is not doing anything?).
+		//if this holds true we can tell buffer to just grab the whole frame at the given rate
+		//and then update the position all at once.
+		rateEnvelope.update();
+		
+		for (int i = 0; i < bufferSize; i++) {
+			/* calculate the samples */
+			
+			/* initialize to silence */
+			for (int j = 0; j < NUM_CHANNELS; j++) {
+				bufOut[j][i] = 0.0f;
+			}
+
+			if(!clipsAtPosition.isEmpty()){ /* if no current sample, then output silence */
+				for(Clip s : clipsAtPosition){
+					float [] frame = new float[s.getSample().getNumChannels()];
+					/* Always use adaptive Interpolation */
+					if(rate > ADAPTIVE_INTERP_HIGH_THRESH) {
+						s.getSample().getFrameNoInterp(s.getSampleStartMs()+(position-s.getStartTimeMs()), frame);
+					} else if(rate > ADAPTIVE_INTERP_LOW_THRESH) {
+						s.getSample().getFrameLinear(s.getSampleStartMs()+(position-s.getStartTimeMs()), frame);
+					} else {
+						s.getSample().getFrameCubic(s.getSampleStartMs()+(position-s.getStartTimeMs()), frame);
+					}
+					
+					/* if the sample is mono, fill the two channels with the same frame */
+					for (int j = 0; j < NUM_CHANNELS; j++) {
+						bufOut[j][i] += frame[j % frame.length];
+						/* clip the sound */
+						if(bufOut[j][i] > 1.0f )
+							bufOut[j][i] = 1.0f;
+						else if(bufOut[j][i] < -1.0f){
+							bufOut[j][i] = -1.0f;
+						}
+					}
+				}
+			}
+					
+			//update the position, loop state, direction
+			calculateNextPosition(i);
+		}
+	}
+
+	public float getLenght(){
+		return Math.max(length,clips.getLengthMs());
+	}
+	
+	public void setLength(float length){
+		this.length = length;
+	}
+	
+	/**
+	 * Used at each sample in the perform routine to determine the next playback position.
+	 * 
+	 * @param i the index within the buffer loop.
+	 */
+	protected void calculateNextPosition(int i) {
+		rate = rateEnvelope.getValue(0, i);
+		switch(loopType) {
+		case NO_LOOP:
+			position += positionIncrement * rate;
+			if(position > clips.getLengthMs() || position < 0.0f) 
+				atEnd();
+			break;
+		case LOOP:
+			position += positionIncrement * rate;
+			if(rate > 0 && position > Math.max(loopStart, loopEnd)) {
+				position = Math.min(loopStart, loopEnd);
+			} else if(rate < 0 && position < Math.min(loopStart, loopEnd)) {
+				position = Math.max(loopStart, loopEnd);
+			}
+			break;
+		}
+		clipsAtPosition = clips.getClipsAtTime((float)position);		
+	}
+
+	/**
+	 * Called when at the end of the Sample, assuming the loop mode is non-looping, or beginning, if the SamplePlayer is playing backwards..
+	 */
+	private void atEnd() {
+		if(endListener != null) {
+			endListener.message(this);
+		}
+		reTrigger();
+	}
+	
+	/**
+	 * Sets a {@link Bead} that will be triggered when this SamplePlayer gets to the end. This occurs when the SamplePlayer's
+	 * position reaches then end when playing forwards in a non-looping mode, or reaches the the beginning when playing backwards in a 
+	 * non-looping mode. It is never triggered in a looping mode. As an alternative, you can use the method {@link Bead.#setKillListener(Bead)}
+	 * as long as {@link #setKillOnEnd(boolean)} is set to true. In other words, you set this SamplePlayer to kill itself when it
+	 * reaches the end of the sample, and then use the functionality of {@link Bead}, which allows you to create a trigger
+	 * whenever a Bead is killed. Set to null to remove the current listener.
+	 * 
+	 * @param endListener the {@link Bead} that responds to this SamplePlayer reaching its end.
+	 */
+	public void setEndListener(Bead endListener) {
+		this.endListener = endListener;
+	}
+	
+	/**
+	 * Gets the current endListener. 
+	 * @see {#setEndListener(Bead)}.
+	 * @return the current endListener.
+	 */
+	public Bead getEndListener() {
+		return endListener;
+	}
+
+	/**
+	 * Re trigger the SamplePlayer from the beginning.
+	 */
+	public void reTrigger() {
+		reset();
+		this.pause(false);
+	}
+	
+	/**
+	 * Updates the lenght when the sound wave changes 
+	 */
+	@Override
+	public void update(SoundWaveEvent evt) {
+		length = clips.getLengthMs();		
+	}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/ZoomUGen.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,62 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGenChain;
+import net.beadsproject.beads.data.Buffer;
+import net.beadsproject.beads.ugens.CombFilter;
+import net.beadsproject.beads.ugens.Envelope;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.WavePlayer;
+
+public class ZoomUGen extends UGenChain {
+
+	public ZoomUGen(AudioContext context, int outputs) {
+		super(context, 0, outputs);
+		
+		
+		WavePlayer sin = new WavePlayer(context,440,Buffer.SINE);
+		Envelope env = new Envelope(context);
+		
+		CombFilter comb = new CombFilter(context,200);
+		
+		env.addSegment(0.8f, 50f,5).addSegment(0.0f, 100f,3.0f);
+		
+		Gain gain = new Gain(context,1);
+		
+		comb.addInput(sin);
+		gain.setGain(env);
+		gain.addInput(comb);
+		
+		addToChainOutput(gain);
+		
+	}
+
+	public static void main(String argv[]){
+		AudioContext ac = new AudioContext();
+		
+		ac.out.addInput(new ZoomUGen(ac,1));
+		
+		ac.start();
+		
+	}
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/AutomationSound.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,76 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.core.UGenChain;
+import net.beadsproject.beads.data.Buffer;
+import net.beadsproject.beads.ugens.BiquadFilter;
+import net.beadsproject.beads.ugens.Function;
+import net.beadsproject.beads.ugens.Noise;
+import net.beadsproject.beads.ugens.TapIn;
+import net.beadsproject.beads.ugens.TapOut;
+import net.beadsproject.beads.ugens.WavePlayer;
+
+public class AutomationSound extends UGenChain {
+	private WavePlayer sinOsc;
+	private UGen noise;
+	private BiquadFilter highPass;
+	private Function add;
+	private TapIn delayIn;
+	private TapOut delayOut;
+	
+	public AutomationSound(AudioContext ac) {
+		super(ac, 0, 2);
+		
+		/* create ugens */
+		sinOsc = new WavePlayer(ac,0.0f,Buffer.SINE);
+		noise = new Noise(ac);
+		highPass = new BiquadFilter(ac,1);
+		delayIn = new TapIn(ac,10f);
+		delayOut = new TapOut(ac,delayIn,0.01f);
+		
+		
+		/* configure ugens */
+		sinOsc.setFrequency(noise);
+		sinOsc.setPhase(-0.35f);
+		
+		highPass.setType(BiquadFilter.BESSEL_HP);
+		highPass.setFrequency(2000);
+		highPass.setQ(3.25f);
+		
+		/* connect ugens */
+		delayIn.addInput(sinOsc);
+		add = new Function(delayOut,sinOsc){
+			@Override
+			public float calculate() {
+				return x[0] + x[1];
+			}
+			
+		};
+		delayIn.addInput(add);
+		highPass.addInput(add);
+		
+		/* output of this ugen */
+		addToChainOutput(highPass);
+	}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsAutomationMapping.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,88 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.data.Sample;
+import net.beadsproject.beads.data.SampleManager;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.SamplePlayer;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.PitchedUGen;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+public class BeadsAutomationMapping extends BeadsSequenceMapping {
+	private final Range<Float> PITCH_SPAN = new Range<Float>(0.2f,1.2f);
+	private Sample sample;
+	
+	BeadsAutomationMapping(){
+		sample = SampleManager.sample(getClass().getResourceAsStream("metallic.wav"));
+	}
+	
+	BeadsAutomationMapping(AudioContext ac){
+		super(ac);
+		sample = SampleManager.sample(getClass().getResourceAsStream("metallic.wav"));
+	}
+	
+	@Override
+	protected PitchedUGen createRenderCurveUGen() {
+		return new AutomationPitchUgen(ac);
+	}
+
+	@Override
+	protected Range<Float> getRenderCurvePitchSpan() {
+		return PITCH_SPAN;
+	}
+	
+	private class AutomationPitchUgen extends PitchedUGen {
+		SamplePlayer player;
+		Gain gain;
+
+		public AutomationPitchUgen(AudioContext ac) {
+			super(ac);
+			player = new SamplePlayer(ac,sample);
+			gain = new Gain(ac,1,0.15f);
+			player.setLoopType(SamplePlayer.LoopType.LOOP_FORWARDS);
+			gain.addInput(player);
+			addToChainOutput(gain);
+		}
+
+		@Override
+		public void setPitch(float pitch) {
+			player.getPitchUGen().setValue(pitch);
+		}
+
+		@Override
+		public void setPitch(UGen pitch) {
+			player.setPitch(pitch);
+		}
+
+		@Override
+		public void setExtremityThresholds(float threshold) {
+			
+		}
+
+		@Override
+		public void setLen(float sustain, float decay) {
+			
+		}
+	}
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsPeakLevelMapping.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,160 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import java.util.prefs.Preferences;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.Bead;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.data.Buffer;
+import net.beadsproject.beads.ugens.Envelope;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.WavePlayer;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.BeadsSonification;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.PitchedUGen;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+public class BeadsPeakLevelMapping extends BeadsSequenceMapping {
+	private final static Range<Float> PITCH_SPAN = new Range<Float>(100.0f,440.0f); 
+	
+	static {
+		/* loads the buffer for the PeakLevelPitchedUgen and stores it into staticBuffs */
+		int bufferLen = 4096;
+		Buffer pulse = new Buffer(bufferLen);
+		int pulseWidth = bufferLen/10;
+		
+		for(int i=0; i<pulseWidth;i++){
+			pulse.buf[i] = 1.0f;
+		}
+		
+		for(int i=pulseWidth; i<bufferLen; i++){
+			pulse.buf[i] = -1.0f;
+		}
+		
+		Buffer.staticBufs.put("Pulse", pulse);
+	}
+	
+	BeadsPeakLevelMapping(AudioContext ac){
+		super(ac);
+	}
+	
+	BeadsPeakLevelMapping(){}
+	
+	@Override
+	protected PitchedUGen createRenderCurveUGen(){
+		return new PeakLevePitchedUGen(ac,PITCH_SPAN,Buffer.SINE);
+	}
+
+	@Override
+	protected Range<Float> getRenderCurvePitchSpan() {
+		return PITCH_SPAN;
+	}
+	
+	class PeakLevePitchedUGen extends PitchedUGen {
+		private Gain renderCurveAtGain;
+		private WavePlayer player;
+		private WavePlayer borderThresholdPlayer;
+		private Gain borderThresholdWet;
+		private float maxFreq;
+		private float minFreq;
+		private float borderPitchThreshold;
+		
+		public PeakLevePitchedUGen(AudioContext ac, Range<Float> freqSpan, Buffer buffer) {
+			super(ac);
+			if(maxFreq < minFreq){
+				throw new IllegalArgumentException("minFreq must be lower than maxFreq. minFreq: "+minFreq+" maxFreq:"+maxFreq );
+			}
+			
+			this.minFreq = freqSpan.getStart();
+			this.maxFreq = freqSpan.getEnd();
+			renderCurveAtGain = new Gain(ac,1,  (Float)Preferences.userNodeForPackage(BeadsSonification.class).getFloat("render_curve.gain",1.0f));
+			player = new WavePlayer(ac,minFreq,buffer);
+			
+			/* border threshold is rendered with a pulse wave that increases in amplitude as 
+			 * the frequency approaches the zero frequency. the pulse is added to the sine wave  */
+			borderThresholdWet = new Gain(ac,1,0.0f);
+			borderThresholdPlayer = new WavePlayer(ac,minFreq,Buffer.staticBufs.get("Pulse"));
+			borderThresholdWet.addInput(borderThresholdPlayer);
+			
+			renderCurveAtGain.addInput(player);
+			renderCurveAtGain.addInput(borderThresholdWet);
+			
+			addToChainOutput(renderCurveAtGain);
+		}
+		
+		@Override
+		public void setPitch(float pitch){
+			/* set the frequency for both the actual curve sound and the threshold sound */
+			player.setFrequency(pitch);
+			borderThresholdPlayer.setFrequency(pitch);
+			
+			if(borderPitchThreshold < 0.0f 	) { // threshold disabled    
+				borderThresholdWet.setGain(0.0f);
+			}else if(maxFreq - pitch <= borderPitchThreshold){
+				float freqSpan = pitch - (maxFreq - borderPitchThreshold);
+				borderThresholdWet.setGain( freqSpan/borderPitchThreshold ); // ranges from 0 to 0.5 see setBorderThreshold 
+			}else if(pitch - minFreq <= borderPitchThreshold){
+				borderThresholdWet.setGain( (borderPitchThreshold-Math.abs(minFreq-pitch)) /borderPitchThreshold ); // ranges from 0 to 0.5
+			}else { // distance higher than the threshold 
+				borderThresholdWet.setGain(0.0f);
+			}
+		}
+		
+		@Override
+		public void setPitch(UGen pitch) {
+			player.setFrequency(pitch);
+			//borderThresholdPlayer.setFrequency(pitch);
+		}
+		
+		public float getFrequency(float freq){
+			return player.getFrequency();
+		}
+		
+		@Override
+		public void start(){
+			player.start();
+			System.out.println("Neads Seq mapping start");
+		}
+		
+		@Override
+		public void kill(){
+			renderCurveAtGain.kill();
+			System.out.println("Neads Seq mapping kill");
+		}
+		
+		@Override
+		public void setLen(float sustain, float decay){			
+			Envelope env = new Envelope(context,sustain);
+			env.addSegment(0.0f,decay, this);
+			renderCurveAtGain.setGain(env);
+		}
+		
+		@Override
+		public void setExtremityThresholds(float borderThreshold){
+			borderPitchThreshold = (maxFreq - minFreq)/2 * borderThreshold;
+		}	
+		
+		@Override
+		public void messageReceived(Bead b){
+			kill();
+		}
+		
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsSequenceMapping.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,232 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import java.util.prefs.Preferences;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.Bead;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.data.Sample;
+import net.beadsproject.beads.data.SampleManager;
+import net.beadsproject.beads.ugens.Envelope;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.SamplePlayer;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.PitchedUGen;
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.Sequence.Value;
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+/**
+ * 
+ * An abstract implementation of sequence mapping.
+ * It takes care of the interface implementation using the beads library. 
+ * Subclasses just need to implement the abstract protected methods to
+ * render the sequence with their own sound. 
+ *
+ */
+public abstract class BeadsSequenceMapping implements SequenceMapping {
+	private static final float RENDER_VALUE_MAX_PITCH = 10.f;
+	private static final float RENDER_VALUE_MIN_PITCH = 0.4f;
+	
+	protected static final float RATE_STEP = 0.1f; 
+	
+	protected AudioContext ac;
+	/* the master gain */
+	protected Gain masterGain;
+	/* sound used in renderValue() */
+	private Sample pop;
+	
+	private PitchedUGen renderCurveAtUGen;
+	private Gain renderCurveGain;
+	
+	private Sample curveZeroSample;
+	
+	/**
+	 * 
+	 * @param ac an audio context. The audio context must be started outside this class 
+	 */
+	public BeadsSequenceMapping(AudioContext ac){
+		this.ac = ac;
+		masterGain = new Gain(ac, 1, 1.0f);
+		ac.out.addInput(masterGain);
+		/* retrieve the sample to play */
+		pop = SampleManager.sample(getClass().getResourceAsStream("pop.mp3"));
+		curveZeroSample = SampleManager.sample(getClass().getResourceAsStream("zero.wav"));
+	}
+	
+	public BeadsSequenceMapping(){
+		this(new AudioContext());
+	}
+	
+	@Override
+	public void renderValue(Value val) {
+		float v = val.getValue();
+		Sequence sequence = val.getSequence();
+		Range<Float> range = sequence.getRange();
+		
+		/* Map the value to the pitch: 
+		 * value / range = pitch - RENDER_VALUE_MIN_PITCH / RENDER_VALUE_MAX_PITCH-RENDER_VALUE_MIN_PITCH */
+		MathUtils.Scale scale =  new MathUtils.Scale(
+				range, 
+				new Range<Float>(RENDER_VALUE_MIN_PITCH,RENDER_VALUE_MAX_PITCH)
+			);
+		
+		float pitch = scale.linear(v);
+	
+		SamplePlayer valuePlayer = new SamplePlayer(ac,pop);
+		valuePlayer.setKillOnEnd(true); 
+		valuePlayer.getRateUGen().setValue(pitch);  
+			
+		masterGain.addInput(valuePlayer);
+	}
+
+	protected abstract PitchedUGen createRenderCurveUGen();
+	
+	protected abstract Range<Float> getRenderCurvePitchSpan();
+	
+	protected UGen getZeroPlayer(){
+		SamplePlayer zeroPlayer = new SamplePlayer(ac,curveZeroSample);	
+		zeroPlayer.setKillOnEnd(true);
+		return zeroPlayer;
+	}
+	
+	@Override
+	public void renderCurve(Sequence s, float startTime) {
+		if(startTime < 0.0f){
+			if(renderCurveGain != null){
+				renderCurveGain.kill();
+				renderCurveGain = null;
+			}
+			return;
+		}
+		final PitchedUGen sequencePlayer = createRenderCurveUGen();
+		final SamplePlayer valuePlayer = new SamplePlayer(ac,pop);
+		final Gain sequencePlayerGain = new Gain(ac,2,0.5f);
+		/* several values need to be played, the player will be killed at the end of the sequence */
+		valuePlayer.setKillOnEnd(false);
+		valuePlayer.setPosition(pop.getLength());
+
+		MathUtils.Scale scale = new MathUtils.Scale(s.getRange(),getRenderCurvePitchSpan());
+
+		/* map the begin of sequence to the pitch of the PitchedUGen */
+		float pitchVal = scale.linear(s.getBegin());
+		final Envelope pitch  = new Envelope(ac,pitchVal);
+		
+		/* add the segments to the envelopes according to the sequence values          *
+		 * so for every i-th value , a new segment is going to the i-th value,         * 
+		 * in t(i-th value)-t(i minus one-th), where t is the time in ms of each value */
+		float lastValueTime = 0.0f;
+		for(int i=0; i<s.getValuesNum();i++){
+			pitchVal =  scale.linear(s.getValueAt(i).getValue());
+			/* Adds the segment to the envelope plus a bead that get's triggered when the destination  *
+			 * value is reached. See Envelope.addSegment(float endValue, float duration, Bead trigger) *
+			 * The beads plays the sample played for the sequence value in renderValue()               */
+			final float sequenceValuePitch = new MathUtils.Scale(
+					s.getRange(), 
+					new Range<Float>(RENDER_VALUE_MIN_PITCH,RENDER_VALUE_MAX_PITCH))
+			.linear(s.getValueAt(i).getValue()); 
+			
+			pitch.addSegment(pitchVal, s.getValueAt(i).getTimePosition() - lastValueTime, new Bead(){
+				/* this method is executed when the segment has been played all */
+				@Override
+				protected  void messageReceived(Bead message){
+					/* pitch is linear to val.getValue. MIN_PITCH is added so if v is 0 the pitch is not 0  */
+					valuePlayer.getRateUGen().setValue(sequenceValuePitch);
+					valuePlayer.reTrigger();
+				}
+			});
+			lastValueTime = s.getValueAt(i).getTimePosition();
+		}
+		
+		/* add the last segment going from the last sequence value to the end of the sequence *
+		 * Also adds a Bead that's triggered when the envelope reaches the end. The Beads     * 
+		 * kills the wave player and the frequency envelope.                                  *
+		 * See Envelope.addSegment(float endValue, float duration, Bead trigger)              */
+		pitchVal = scale.linear(s.getEnd());
+		pitch.addSegment(pitchVal, s.getLen() - lastValueTime, new Bead(){
+			@Override
+			protected  void messageReceived(Bead message){
+				pitch.kill();
+				sequencePlayerGain.kill();
+				valuePlayer.kill();
+			}
+		});
+		
+		sequencePlayerGain.addInput(valuePlayer);
+		/* set the pitch envelope as the frequency for the wavePlayer */
+		sequencePlayer.setPitch(pitch);
+		sequencePlayerGain.addInput(sequencePlayer);
+		/* add to the master gain for the sound to be played */
+		renderCurveGain = sequencePlayerGain;
+		masterGain.addInput(sequencePlayerGain);
+	}
+	
+	@Override
+	public void renderCurveAt(Sequence sequence, float time, float duration){
+		if(duration < 0.0f ){
+			if(renderCurveAtUGen != null)
+				renderCurveAtUGen.kill();
+			renderCurveAtUGen = null;
+			return;
+		}		
+		
+		/* look for the two sequence values, whose times contain time in between */
+		float valueAtTime = new MathUtils.Interpolate(sequence).linear(time);
+		float pitch = new MathUtils.Scale(sequence.getRange(),getRenderCurvePitchSpan()).linear(valueAtTime);
+		
+		Preferences prefs = Preferences.userNodeForPackage(BeadsSequenceMapping.class);
+		float borderThreshold =  prefs.getFloat("borders.threshold", 0.01f);
+		
+		/* infinite duration means play until renderCurveAt is called again with duration = -1 */
+		if(Float.isInfinite(duration)){
+			if(renderCurveAtUGen == null){
+				renderCurveAtUGen = createRenderCurveUGen();
+				renderCurveAtUGen.setPitch(pitch);
+				renderCurveAtUGen.setExtremityThresholds(borderThreshold);
+				masterGain.addInput(renderCurveAtUGen);
+				renderCurveAtUGen.start();
+			}else{
+				renderCurveAtUGen.setPitch(pitch);
+			}
+		}else if(duration > 0.0f){
+				if(renderCurveAtUGen == null){
+					renderCurveAtUGen = createRenderCurveUGen();
+					renderCurveAtUGen.setPitch(pitch);
+					renderCurveAtUGen.setExtremityThresholds(borderThreshold);
+					renderCurveAtUGen.setLen(duration*0.8f, duration*0.2f);
+					ac.out.addInput(renderCurveAtUGen);
+					renderCurveAtUGen.start();
+				}else{
+					renderCurveAtUGen.setLen(duration*0.8f, duration*0.2f);
+				}
+		}
+		
+		/* if the curve is at zero play the zero signal */
+		float zeroPoint = sequence.getRange().getStart() + sequence.getRange().lenght()/2.0f;
+
+		if(MathUtils.equal(valueAtTime, zeroPoint, 0.01)){
+			masterGain.addInput(getZeroPlayer());
+		}
+	}
+	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/BeadsSonification.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,180 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import java.util.prefs.Preferences;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.data.Sample;
+import net.beadsproject.beads.data.SampleManager;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.Noise;
+import net.beadsproject.beads.ugens.OnePoleFilter;
+import net.beadsproject.beads.ugens.Panner;
+import net.beadsproject.beads.ugens.SamplePlayer;
+import net.beadsproject.beads.ugens.Static;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.Sound;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+public class BeadsSonification implements Sonification {
+	
+	private AudioContext ac;
+	private SequenceMapping automationMapping;
+	private SequenceMapping peakLevelMapping;
+	private Sample error;
+	private Sample ok;
+	private Sample shiftBorders;
+	private Sample loop;
+	private UGen selectionSound;
+	
+	
+	public BeadsSonification() {
+		ac = new AudioContext();
+		Gain gain = new Gain(ac,1,1.0f);
+		ac.out.addInput(gain);
+		
+		automationMapping = new BeadsAutomationMapping(ac);
+		peakLevelMapping = new BeadsPeakLevelMapping(ac);
+		
+		error = SampleManager.sample(getClass().getResourceAsStream("error.mp3") );
+		ok = SampleManager.sample(getClass().getResourceAsStream("ok.mp3") );
+		shiftBorders = SampleManager.sample(getClass().getResourceAsStream("selection_borders.wav") ); 
+		loop = SampleManager.sample(getClass().getResourceAsStream("loop.mp3") );
+		ac.start();
+	}
+
+	@Override
+	public SequenceMapping getSequenceMapping(SoundType soundType) {
+		if(soundType == SoundType.AUTOMATION){
+			return automationMapping;
+		}else if(soundType == SoundType.PEAK_LEVEL){
+			return peakLevelMapping;
+		}else{
+			throw new IllegalArgumentException("Sound Type not recognized: "+soundType);
+		}
+	}
+	
+	private void play(Sound sound, float g, float p){
+		UGen output = null;
+		
+		SoundType s = (SoundType) sound.getType();
+		switch(s){
+		
+		case HAPTIC_PORT_TOUCH : {
+			output = new Buzz(ac);
+			break;
+		} 
+
+		case ERROR : {
+			output = playSample(error);
+			break;
+		}
+		
+		case OK : {
+			output = playSample(ok);
+			break;
+		}
+		
+		case LOOP : { 
+			output = playSample(loop);
+			break;
+		}
+		
+		case SHIFT_START : 
+		case SHIFT_END : {
+			SamplePlayer player = new SamplePlayer(ac,shiftBorders);
+			player.setKillOnEnd(true);
+			
+			if(sound.getType() == SoundType.SHIFT_START){
+				player.setPitch(new Static(ac,1.5f));
+			}else{
+				player.setLoopType(SamplePlayer.LoopType.NO_LOOP_FORWARDS);
+			}
+			output = player;
+			break;
+		} 
+		
+		default : return;
+		}
+		
+		Panner pan = new Panner(ac, p);
+		Gain gain = new Gain(ac, 1, g);
+		
+		gain.addInput(output);
+		pan.addInput(gain);
+		ac.out.addInput(pan);
+		
+		output.start();
+	}
+	
+	@Override
+	public void play(SoundType soundType){
+		play(new Sound(soundType), 1.0f, 0.0f);
+	}
+	
+	@Override
+	public void play(SoundType soundType, float gain, float pan){
+		play(new Sound(soundType), gain, pan);
+	}
+	
+	@Override
+	public void withinSelection(float g, float p){
+		if(selectionSound != null)
+			return;
+		
+		Noise noise =  new Noise(ac);
+		OnePoleFilter filter = new OnePoleFilter(
+				ac,
+				Preferences.userNodeForPackage(BeadsSonification.class).getInt("selection.filter", 350)
+				);
+		
+		filter.addInput(noise);
+		
+		Panner pan = new Panner(ac, p);
+		Gain gain = new Gain(ac, 1, g);
+		
+		gain.addInput(filter);
+		pan.addInput(gain);
+		ac.out.addInput(pan);
+		
+		/* set the source to stop the sound later */
+		selectionSound = pan;
+		pan.start();
+	}
+	
+	@Override
+	public void outsideSelection(){
+		if(selectionSound != null){
+			selectionSound.kill();
+			selectionSound = null;
+		}
+		
+	}
+	
+	private UGen playSample(Sample s){
+		SamplePlayer player = new SamplePlayer(ac,s);
+		player.setKillOnEnd(true);
+		return player;
+	}
+	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/Buzz.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,92 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGenChain;
+import net.beadsproject.beads.data.Buffer;
+import net.beadsproject.beads.events.KillTrigger;
+import net.beadsproject.beads.ugens.Envelope;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.WavePlayer;
+
+public class Buzz extends UGenChain {
+	private Gain gain;
+	private  Envelope env;
+	private  WavePlayer wp; 
+	private float len;
+	
+	public static void main (String argv[]) { 
+		AudioContext x = new AudioContext();
+		
+		Buzz b = new Buzz(x);
+		x.out.addInput(b);
+		x.start();
+	}
+
+	/**
+	 * Crate a new Buzz 200 millisec long 
+	 * 
+	 * @param ac the audio context of the Buzz 
+	 */
+	public Buzz(AudioContext ac) {
+		super(ac,0,1);
+		
+		len = 200.0f;
+		gain = new Gain(ac,1,0.5f);
+		env = new Envelope(ac,1.0f);
+		env.addSegment(1.0f, len , new KillTrigger(gain));
+		
+		/* make a kind of impulse wave */
+		Buffer b = new Buffer(4096);
+		for(int i=0; i<b.buf.length; i++ ){
+			int j = i % 200;
+			if(j<40)
+				b.buf[i] = 1.0f;
+				
+		}
+		
+		wp = new WavePlayer(ac, 440.f, b);
+		
+		gain.addInput(wp);
+		gain.setGain(env);
+		
+		this.addToChainOutput(gain);
+	}
+	
+	@Override
+	public void start(){
+		wp.start();
+	}
+	
+	@Override
+	public void kill(){
+		if(!gain.isDeleted())
+			gain.kill();
+	}
+	
+	public float getLen() {
+		return len;
+	}
+
+	public void setLen(float len) {
+		this.len = len;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/PitchedUGen.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,50 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.core.UGenChain;
+
+/**
+ * A UGen with pitch 
+ * 
+ *
+ */
+public abstract class PitchedUGen extends UGenChain {
+	
+	public PitchedUGen(AudioContext ac) {
+		super(ac, 0, 2);
+	}
+
+	public abstract void setPitch(float pitch);
+	
+	public abstract void setPitch(UGen pitch);
+	
+	/**
+	 * Sets a threshold for the sonification at the extremities of the range 
+	 * This can be useful to flag that extremities are about to be reached 
+	 * 
+	 * @param threshold the threshold but be 
+	 */
+	public abstract void setExtremityThresholds(float threshold);
+	
+	public abstract void setLen(float sustain, float decay);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/beads/sonification/SonificationPrefsPanel.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,186 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.beads.sonification;
+
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.prefs.Preferences;
+
+import javax.swing.JLabel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+
+import uk.ac.qmul.eecs.depic.daw.gui.PreferencesPanel;
+
+import javax.swing.JCheckBox;
+
+public class SonificationPrefsPanel extends PreferencesPanel {
+	private static String [] keys = new String [] {
+		"selection.filter",
+		"borders.threshold",
+		"render_val.ref",
+		"render_curve.gain"
+	};
+
+	private static final long serialVersionUID = 1L;
+	private JSpinner selectionFilterSpinner;
+	private JSpinner borderThresholdSpinner;
+	private JCheckBox valRefCheckBox;
+	private JLabel lblRenderCurveGain;
+	private JSpinner curveGainSpinner;
+
+	
+	private static SonificationPrefsPanel singleton;
+	public static SonificationPrefsPanel getInstance(){
+		if(singleton == null)
+			singleton = new SonificationPrefsPanel();
+		return singleton;
+	}
+
+	/**
+	 * Create the panel.
+	 */
+	private SonificationPrefsPanel() {
+		
+		GridBagLayout gridBagLayout = new GridBagLayout();
+		gridBagLayout.columnWidths = new int[]{30, 70, 78, 53, 0};
+		gridBagLayout.rowHeights = new int[]{30, 18, 0, 0, 0, 0, 0};
+		gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
+		gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
+		setLayout(gridBagLayout);
+		
+		JLabel selectionFilterLabel = new JLabel("Selection filter :");
+		GridBagConstraints gbc_selectionFilterLabel = new GridBagConstraints();
+		gbc_selectionFilterLabel.anchor = GridBagConstraints.WEST;
+		gbc_selectionFilterLabel.insets = new Insets(0, 0, 5, 5);
+		gbc_selectionFilterLabel.gridx = 1;
+		gbc_selectionFilterLabel.gridy = 1;
+		add(selectionFilterLabel, gbc_selectionFilterLabel);
+		
+		/* prefs are used to initialize the spinners and the grain ugens */
+		Preferences prefs = Preferences.userNodeForPackage(BeadsSonification.class);
+		
+		selectionFilterSpinner = new JSpinner();
+		selectionFilterSpinner.setModel(new SpinnerNumberModel(
+				new Integer(prefs.getInt(getPrefsList()[0], new Integer(500))),  
+				new Integer(0), // min value 
+				new Integer(1000), // max value
+				new Integer(10))); // increment 
+		GridBagConstraints gbc_selectionFilterSpinner = new GridBagConstraints();
+		gbc_selectionFilterSpinner.insets = new Insets(0, 0, 5, 0);
+		gbc_selectionFilterSpinner.anchor = GridBagConstraints.NORTH;
+		gbc_selectionFilterSpinner.fill = GridBagConstraints.HORIZONTAL;
+		gbc_selectionFilterSpinner.gridx = 3;
+		gbc_selectionFilterSpinner.gridy = 1;
+		add(selectionFilterSpinner, gbc_selectionFilterSpinner);
+		
+		JLabel borderThreshLabel = new JLabel("Border Threshold :"); // FIXME boudles 
+		GridBagConstraints gbc_borderThreshLabel = new GridBagConstraints();
+		gbc_borderThreshLabel.anchor = GridBagConstraints.WEST;
+		gbc_borderThreshLabel.insets = new Insets(0, 0, 5, 5);
+		gbc_borderThreshLabel.gridx = 1;
+		gbc_borderThreshLabel.gridy = 2;
+		add(borderThreshLabel, gbc_borderThreshLabel);
+		
+		borderThresholdSpinner = new JSpinner();
+		borderThresholdSpinner.setModel(new SpinnerNumberModel(
+				new Float(prefs.getFloat(getPrefsList()[1], new Float(-0.1f))),  
+				new Float(-0.1f), // min value 
+				new Float(1.0f), // max value
+				new Float(0.05f))); // increment 
+		GridBagConstraints gbc_borderThreshSpinner = new GridBagConstraints();
+		gbc_borderThreshSpinner.insets = new Insets(0, 0, 5, 0);
+		gbc_borderThreshSpinner.fill = GridBagConstraints.HORIZONTAL;
+		gbc_borderThreshSpinner.anchor = GridBagConstraints.NORTH;
+		gbc_borderThreshSpinner.gridx = 3;
+		gbc_borderThreshSpinner.gridy = 2;
+		add(borderThresholdSpinner, gbc_borderThreshSpinner);
+		
+		JLabel sequenceValRefLabel = new JLabel("Sequence val ref:");
+		GridBagConstraints gbc_sequenceValRefLabel = new GridBagConstraints();
+		gbc_sequenceValRefLabel.anchor = GridBagConstraints.WEST;
+		gbc_sequenceValRefLabel.insets = new Insets(0, 0, 5, 5);
+		gbc_sequenceValRefLabel.gridx = 1;
+		gbc_sequenceValRefLabel.gridy = 3;
+		add(sequenceValRefLabel, gbc_sequenceValRefLabel);
+		
+		valRefCheckBox = new JCheckBox("");
+		valRefCheckBox.setSelected(prefs.getBoolean(getPrefsList()[3], false));
+		sequenceValRefLabel.setLabelFor(valRefCheckBox);
+		GridBagConstraints gbc_valRefCheckBox = new GridBagConstraints();
+		gbc_valRefCheckBox.anchor = GridBagConstraints.WEST;
+		gbc_valRefCheckBox.insets = new Insets(0, 0, 5, 0);
+		gbc_valRefCheckBox.gridx = 3;
+		gbc_valRefCheckBox.gridy = 3;
+		add(valRefCheckBox, gbc_valRefCheckBox);
+		
+		lblRenderCurveGain = new JLabel("Render Curve Gain:");
+		GridBagConstraints gbc_lblRenderCurveGain = new GridBagConstraints();
+		gbc_lblRenderCurveGain.insets = new Insets(0, 0, 5, 5);
+		gbc_lblRenderCurveGain.gridx = 1;
+		gbc_lblRenderCurveGain.gridy = 4;
+		add(lblRenderCurveGain, gbc_lblRenderCurveGain);
+		
+		curveGainSpinner = new JSpinner();
+		curveGainSpinner.setModel(new SpinnerNumberModel(
+				new Float(prefs.getFloat(getPrefsList()[3], new Float(1.0f))),  
+				new Float(0),  // min value 
+				new Float(1), // max value 
+				new Float(0.1))); // increment
+		GridBagConstraints gbc_spinner = new GridBagConstraints();
+		gbc_spinner.insets = new Insets(0, 0, 5, 0);
+		gbc_spinner.fill = GridBagConstraints.HORIZONTAL;
+		gbc_spinner.gridx = 3;
+		gbc_spinner.gridy = 4;
+		add(curveGainSpinner, gbc_spinner);
+		
+		/* sets all the strings for the screen reader  */
+		configureAccessibleContext();
+	}
+
+	
+	private void configureAccessibleContext(){
+		borderThresholdSpinner.getEditor().getAccessibleContext().setAccessibleName("Border Threshold"); // FIXME bundle 
+		selectionFilterSpinner.getEditor().getAccessibleContext().setAccessibleName("Selectionf Filter");
+	}
+	
+	@Override
+	public void savePrefs() {
+		Preferences prefs = Preferences.userNodeForPackage(BeadsSonification.class);
+		
+		prefs.putInt(getPrefsList()[0], (Integer)selectionFilterSpinner.getValue());
+		prefs.putFloat(getPrefsList()[1], (Float)borderThresholdSpinner.getValue());
+		prefs.putBoolean(getPrefsList()[2], (Boolean)valRefCheckBox.isSelected());
+		prefs.putFloat(getPrefsList()[3], (Float)curveGainSpinner.getValue());
+				
+	}
+	
+	@Override
+	public String getTitle(){
+		return "Sonification Synthesis";
+	}
+
+	@Override
+	public String[] getPrefsList() {
+		return keys;
+	}
+}
+
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/error.mp3 has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/loop.mp3 has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/metallic.wav has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/ok.mp3 has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/pop.mp3 has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/selection_borders.wav has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/beads/sonification/zero.wav has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/ArrangeWindow.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,320 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.KeyStroke;
+import javax.swing.border.Border;
+
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
+
+/**
+ * 
+ * The panel containing the audio tracks.
+ * 
+ * Tracks are arranged in a scrollable BoxLayout. 
+ *
+ */
+public class ArrangeWindow extends JPanel implements PropertyChangeListener, SoundWaveListener {
+	private static final long serialVersionUID = 1L;
+	public static final Color BACKGROUND_COLOR = new Color(226,226,226);
+	public static final int SPACE_BETWEEN_TRACKS = 5;
+	public static final Border BORDER_UNSELECTED = BorderFactory.createCompoundBorder(
+			BorderFactory.createEmptyBorder(1, 0, 1, 0), BorderFactory.createMatteBorder(1, 0, 1, 0, Color.GRAY));
+	public static final Border BORDER_SELECTED = BorderFactory.createCompoundBorder(
+				BorderFactory.createMatteBorder(1, 0, 1, 0, new Color(214, 193, 99)), BorderFactory.createMatteBorder(1, 0, 1, 0, Color.GRAY));
+			
+	
+	/* tracks panel contains the rule and audio tracks  */
+	private JScrollPane tracksPanelScroll;
+	/* view of the trackPanelScroll and panel where all the audio tracks are places */
+	private JPanel tracksPanel;
+	/* left panel with AudioTrackParameters */
+	private JPanel parametersPanel;
+	private Rule rule;
+	private int currentTrackIndex;
+	private List<AudioTrack> tracks;
+	private List<AudioTrackParameters> audioTrackParameters;
+	private List<Component> spacesBetweenTracks;
+	private List<Component> spacesBetweenTrackParameters;
+	private MouseInteraction mouseInteraction;
+	
+	
+	public ArrangeWindow(){
+		tracks = new ArrayList<>();
+		audioTrackParameters = new ArrayList<>();
+		spacesBetweenTracks = new ArrayList<>();
+		spacesBetweenTrackParameters = new ArrayList<>();
+		mouseInteraction = new MouseInteraction();
+		
+		setLayout(new BorderLayout());
+		setBackground(BACKGROUND_COLOR);
+		
+		/* left panel: header (of the same height as rule) and parameters */
+		parametersPanel = new JPanel();
+		parametersPanel.setLayout(new BoxLayout(parametersPanel,BoxLayout.Y_AXIS));
+		parametersPanel.add(Box.createRigidArea(new Dimension(247,Rule.HEIGHT+3)));
+		parametersPanel.setBackground(BACKGROUND_COLOR);
+		
+		add(parametersPanel,BorderLayout.WEST);
+		
+		/* right panel: scrollable panel with rule and tracks as header*/
+		tracksPanel = new JPanel();
+		tracksPanel.setLayout(new BoxLayout(tracksPanel,BoxLayout.Y_AXIS));
+		tracksPanel.setBackground(BACKGROUND_COLOR);
+		rule = new Rule();
+		rule.setBackground(BACKGROUND_COLOR);
+		
+		
+		tracksPanelScroll = new JScrollPane(tracksPanel,
+				JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+		
+		/* disable the scrolling via the left-right keys to let the cursor scrub handler 
+		 * take over when the track is focused */
+		tracksPanelScroll.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "none");
+		tracksPanelScroll.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "none");
+		/* setPreferredSize important, otherwise the view port gets resized to the track size * 
+		 * as soon as the window is resized and therefore the scroll bar disappear            */
+		tracksPanelScroll.setPreferredSize(new Dimension(500,500));
+		/* rule is set as the header of the scroll pane with audio tracks */
+		tracksPanelScroll.setColumnHeaderView(rule);
+		
+		add(tracksPanelScroll, BorderLayout.CENTER);
+		
+		currentTrackIndex = -1;
+	}
+	
+	/**
+	 * Adds a track to this component. The added track is automatically set as the current track.
+	 * 
+	 * Adding a track that's already in the arrange window will have no effect.
+	 *   
+	 * @param track the new track to add. It must be a track that is not currently 
+	 * contained in this arrange window
+	 */
+	public void addTrack(AudioTrack track){
+		if(tracks.contains(track))
+			return;
+		
+		AudioTrackParameters trackParameters = new AudioTrackParameters(track,1);
+		
+		/* install the mouse listener */
+		track.addMouseListener(mouseInteraction);
+		trackParameters.addMouseListener(mouseInteraction); // FIXME verify 
+		
+		
+		/* add track and parameters in the respective lists */
+		tracks.add(track);
+		audioTrackParameters.add(trackParameters);
+		Component box1 = Box.createVerticalStrut(SPACE_BETWEEN_TRACKS);
+		Component box2 = Box.createVerticalStrut(SPACE_BETWEEN_TRACKS);
+		spacesBetweenTracks.add(box1);
+		spacesBetweenTrackParameters.add(box2);
+		
+		/* add track and parameters in the respective panels */
+		parametersPanel.add(box1);
+		parametersPanel.add(trackParameters);
+		tracksPanel.add(box2);
+		tracksPanel.add(track);
+		/* set the new track as the current */
+		setCurrentTrack(getTracksCount()-1);
+		
+		/* set the label. Users will count starting from 1 and not from 0 */
+		trackParameters.setLabel("new Track "+(getCurrentTrackIndex()+1));
+		repaint();
+	}
+	
+	public AudioTrack getTrackAt(int index){
+		return  tracks.get(index);
+	}
+	
+	public AudioTrackParameters getTrackParametersAt(int index){
+		return audioTrackParameters.get(index);
+	}
+	
+	/**
+	 * 
+	 * @return the current track or {@code null} if there are no tracks
+	 * in this arrange window
+	 */
+	public AudioTrack getCurrentTrack(){
+		if(currentTrackIndex == -1)
+			return null;
+		return  tracks.get(currentTrackIndex);
+	}
+	
+	/**
+	 * Sets the track at the specified index as current. 
+	 * 
+	 * @param index the index of the new current track 
+	 * @throws ArrayIndexOutOfBoundsException if {@code index} is lower than 0 or greater or equal to {@code getTracksCount()} 
+	 * 
+	 * 
+	 */
+	public void setCurrentTrack(int index){
+		/* removes this from the listeners of the previous current track */
+		if(currentTrackIndex != -1){
+			AudioTrack previousTrack = getTrackAt(currentTrackIndex);
+			previousTrack.removePropertyChangeListener(this);
+			previousTrack.getSoundWave().removeSoundWaveListener(this);
+			previousTrack.setBorder(BORDER_UNSELECTED); 
+			audioTrackParameters.get(currentTrackIndex).setBorder(BORDER_UNSELECTED);
+		}
+		currentTrackIndex = index;
+		
+		AudioTrack currentTrack = getTrackAt(currentTrackIndex); 
+		/* install listeners in the new audio track */
+		currentTrack.addPropertyChangeListener(this);
+		currentTrack.getSoundWave().addSoundWaveListener(this);
+		/* set the borders */
+		currentTrack.setBorder(BORDER_SELECTED); 
+		audioTrackParameters.get(currentTrackIndex).setBorder(BORDER_SELECTED);
+
+		rule.setAudioTrack(getTrackAt(currentTrackIndex));
+	}
+	
+	/**
+	 * 
+	 * @return the index of the currently selected track or {@code -1} if no track 
+	 * is open in the arrange window
+	 */
+	public int getCurrentTrackIndex(){
+		return currentTrackIndex;
+	}
+	
+	/**
+	 * Returns the number of tracks in this arrange window.
+	 * 
+	 * @return the number of tracks in this arrange window
+	 */
+	public int getTracksCount(){
+		return tracks.size();
+	}
+	
+	public Rule getRule(){
+		return rule;
+	}
+	
+	/**
+	 * 
+	 * On {@code SELECTION_CHANGED} event from the current audio track, updates the other
+	 * tracks accordingly. 
+	 * 
+	 * This enforces a unique selection and cursor position over the whole arrange window  
+	 * 
+	 * @param evt the sound wave event 
+	 * 
+	 */
+	@Override
+	public void update(SoundWaveEvent evt) {
+		
+		if(SoundWaveEvent.SCAN.equals(evt.getType())){
+			for(int i=0; i< getTracksCount(); i++){
+				SoundWave wave = getTrackAt(i).getSoundWave();
+				/* only change selection if it's not the one that triggered the event */
+				if(!wave.equals(evt.getSource())){  
+					wave.scan((Integer)evt.getArgs());
+				}
+			}
+		}
+		
+		if(SoundWaveEvent.POSITION_CHANGED.equals(evt.getType()) ){
+			for(AudioTrack track : tracks){
+				if(!track.getSoundWave().equals(evt.getSource())){
+					track.getSoundWave().setPosition((Integer)evt.getArgs());
+				}
+			}
+			
+			/* this is called from the current audio track sound wave. The * 
+			 * selection is changed in the other audio tracks accordingly  */
+			//SoundWave currentWave = evt.getSource();
+			
+//			for(int i=0; i< getTracksCount(); i++){
+//				SoundWave wave = getTrackAt(i).getSoundWave();
+//				/* only change selection if it's not the one that triggered the event */
+//				if(!wave.equals(currentWave)){  
+//					wave.setSelection((Selection)evt.getArgs());
+//				}
+//			}
+		}
+	}
+	
+	private class MouseInteraction extends MouseAdapter {
+		@Override
+		public void mousePressed(MouseEvent e){
+			Component c = e.getComponent();
+			int index = tracks.lastIndexOf(c);
+			
+			if(index == getCurrentTrackIndex()){
+				return;
+			}else if(index != -1){
+				setCurrentTrack(index);
+				return;
+			}
+			
+			index = audioTrackParameters.lastIndexOf(c);
+			if(index == getCurrentTrackIndex()){
+				return;
+			}else if(index != -1){
+				setCurrentTrack(index);
+				return;
+			}
+			
+		}
+	}
+
+	@Override
+	public void propertyChange(PropertyChangeEvent evt) {
+		switch(evt.getPropertyName()){
+			case "mouseDragSelection" : {
+				Selection selection = (Selection)evt.getNewValue();
+				for(AudioTrack track : tracks){
+					if(!track.equals(evt.getSource())){
+						track.trackInteraction.setMouseSelection(
+								selection.getStart(), selection.getEnd());
+					}
+				}
+			} break;
+		}
+	}
+	
+	
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrack.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,520 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+
+import javax.swing.JPanel;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Chunk;
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
+import uk.ac.qmul.eecs.depic.daw.Wave;
+import uk.ac.qmul.eecs.depic.daw.haptics.HapticViewPort;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+/**
+ * 
+ * 
+ * 
+ * 
+ * This class has the following bound properties : 
+ * <ul>
+ *	<li><b>scaleFactor:</b> the scale factor of each pixel of the sound wave representation. The bigger the 
+ *		scale factor, the lower the resolution. a scale factor ranges from 1, highest resolution supported by 
+ * 		the {@code SoundWave} instance, to the max scale factor supported by the backing {@code SoundWave} model  - {@code int}</li>
+ *	<li><b>cursorPos:</b> the position of the cursor - {@code int} </li>
+ *  <li><b>mouseDragSelection:</b> the range of the selection on this track as the user drags the mouse - {@code SelectionRange} </li>
+ *  <li><b><preferredSize:</b> the preferred size - {@code Dimension}
+ * </ul>
+ * 
+ * 
+ *
+ */
+public class AudioTrack extends JPanel implements SoundWaveListener {
+	private static final long serialVersionUID = 1L;
+	
+	public static final int MAX_TRACK_HEIGHT = 130;
+	public static final int MAX_TRACK_WIDTH = Integer.MAX_VALUE;
+	
+	public static final Color WAVE_COLOR = new Color(7,47,140);
+	public static final Color CURSOR_COLOR = new Color(44,47,56);
+	public static final Color SELECTION_COLOR = new Color(218,218,240);
+	public static final Color OVERLAY_BG_COLOR = new Color(222,222,235);
+	public static final Color VIEW_PORT_COLOR = Color.GREEN;
+	
+	/* these fields are used by friend classes         *
+	 * they're final to protect them from being changed */
+	final AudioTrackInput trackInteraction;
+	final AudioTrackSonification trackSonification;
+	
+	private SequenceGraph automationGraph;
+	private SequenceGraph peakMeterGraph;
+	private final CursorUpdaterOnPlay clipInteraction;
+	protected SoundWave soundWave;
+	
+	private HapticViewPort hapticViewPort;
+	
+	private Selection currentSelection; 
+	private int scaleFactor; // bound property
+	private int cursorPos;   // bound property
+	private float secondsPerPixel;
+	private boolean showHapticViewPort;
+	boolean showAutomationSound;
+	boolean showPeakLevelSound;
+	private boolean showDbWave;
+	
+	public AudioTrack(SoundWave soundWave){
+		if(soundWave == null)
+			throw new IllegalArgumentException("soundWave cannot be null");
+		
+		this.soundWave = soundWave;
+		soundWave.addSoundWaveListener(this);
+		
+		 
+		setBackground(Color.WHITE);
+
+		scaleFactor = 1;
+		currentSelection = new Selection(0,scaleFactor);
+		
+		/* set up sequence graphs */
+		ChangeListener sequenceGraphListener = new ChangeListener(){
+			@Override
+			public void stateChanged(ChangeEvent e) {
+				repaint();
+			}
+		};
+		
+		automationGraph = new SequenceGraph(Color.WHITE, getSize());
+		automationGraph.addChangeListener(sequenceGraphListener);
+		peakMeterGraph = new SequenceGraph(Color.GRAY, getSize());
+		peakMeterGraph.addChangeListener(sequenceGraphListener);
+		
+		clipInteraction = new CursorUpdaterOnPlay();
+		trackInteraction = new AudioTrackInput(this);
+		trackSonification = new AudioTrackSonification(this);
+		hapticViewPort = new HapticViewPort(1);
+		
+		setMaximumSize(new Dimension(MAX_TRACK_WIDTH,MAX_TRACK_HEIGHT));
+		//setMinimumSize(new Dimension(100,TRACK_HEIGHT));
+		setPreferredSize(new Dimension(0,MAX_TRACK_HEIGHT));
+		
+		this.setFocusable(true);
+		
+		addPropertyChangeListener(trackSonification);
+		addMouseListener(trackInteraction);
+		addMouseMotionListener(trackInteraction);
+		addKeyListener(trackInteraction);
+	}
+	
+	@Override
+	public void update(SoundWaveEvent evt) { // FIXME fare file closed
+		String evtType = evt.getType();
+		if(SoundWaveEvent.OPEN.equals(evtType)) {
+			SoundWave wave = evt.getSource();
+			hapticViewPort.setTrackSize(wave.getChunkNum()); // FIXME check
+			_setScaleFactor(wave.getScaleFactor());
+			setCursorPos(0);
+		} else if(SoundWaveEvent.SCALE_FACTOR_CHANGED.equals(evtType)){
+			_setScaleFactor((Integer)evt.getArgs());
+		}else if (SoundWaveEvent.SELECTION_CHANGED.equals(evtType)){
+			Selection oldSelection = currentSelection; 
+			currentSelection = (Selection)evt.getArgs();
+			/* update the mouse interaction object accordingly */
+			trackInteraction.setMouseSelection(currentSelection.getStart(), currentSelection.getEnd());
+			firePropertyChange("mouseDragSelection",oldSelection,currentSelection);
+			repaint();
+			
+		}else if(SoundWaveEvent.POSITION_CHANGED.equals(evtType)){
+			/* update the position according to the Selection object returned by evt.getArgs() * 
+			 * the selection is open. The Selection's scale factor is also taken into account  *
+			 * when calculating the new position. In particular the ratio between the          *
+			 * selction's scale factor and this object current scale factor */
+			setCursorPos((Integer)evt.getArgs());
+			
+			
+		}else if(SoundWaveEvent.START.equals(evtType) ||  
+			  SoundWaveEvent.STOP.equals(evtType)   ||
+			   SoundWaveEvent.PAUSE.equals(evtType)){
+					clipInteraction.update(evt);
+		}else if (SoundWaveEvent.AUTOMATION_CHANGED.equals(evtType)){
+			Automation automation = (Automation)evt.getArgs(); 
+			
+			/* if it's an automation different from NONE, paint the overlay: make the bg darker */
+			if(Automation.NONE_AUTOMATION.equals(automation)){
+				setBackground(Color.WHITE);
+				automationGraph.removeChangeListener(trackSonification);
+			}else{
+				setBackground(OVERLAY_BG_COLOR);
+			}
+			
+			/* resize automation graph, if currently showing any */
+			automationGraph.setMillisecPerPixel(evt.getSource().getMillisecPerChunk());
+			automationGraph.setColor(automation.getColor());
+			automationGraph.setSize(getSize());
+			automationGraph.setSequence(automation);
+			automationGraph.addChangeListener(trackSonification);
+			
+		}else if(SoundWaveEvent.CLOSE.equals(evtType) ||
+				SoundWaveEvent.CUT.equals(evtType) || 
+				SoundWaveEvent.PASTE.equals(evtType) || 
+				SoundWaveEvent.INSERT.equals(evtType)){
+			hapticViewPort.setTrackSize(evt.getSource().getChunkNum());
+			repaint();
+		}else if(SoundWaveEvent.PEAK_METER.equals(evtType)){
+			
+			peakMeterGraph.setMillisecPerPixel(evt.getSource().getMillisecPerChunk());
+			peakMeterGraph.setSize(getSize());
+			/* listens to the changes, so it will repaint after setSequence */
+			peakMeterGraph.setSequence((Sequence)evt.getArgs());
+			
+		}
+	}
+	
+	@Override
+	public void  paintComponent(Graphics g){
+		super.paintComponent(g);
+		Graphics2D g2 = (Graphics2D)g;
+		Color oldColor = g2.getColor();
+		
+		/* get height and width. needed for painting */
+		int height = getHeight();
+		int width = getWidth();
+
+		/* paint the selection grey background*/
+		Selection mouseSelection = trackInteraction.getMouseSelection(); 
+		if(!mouseSelection.isOpen()){
+			g2.setColor(SELECTION_COLOR);
+			g2.fillRect(mouseSelection.getStart(), 0, Math.abs(mouseSelection.getStart()-mouseSelection.getEnd()), height);
+		}
+		
+		/* paint the central line */
+		g2.setColor(CURSOR_COLOR);
+		g2.draw(new Line2D.Float(0f, height/2,width,height/2));
+		
+		/* paint the sound wave, if any */
+		if(soundWave != null ){
+			Wave wave = showDbWave ? soundWave.getDbWave() : soundWave;
+			g2.setColor(WAVE_COLOR);
+			int horizontalPixel = 0;
+			for(int i=0; i<wave.getChunkNum(); i++){
+				Chunk chunk = wave.getChunkAt(i);
+				g2.draw(new Line2D.Float(
+						horizontalPixel,
+						normToHeightConvert(chunk.getNormStart()),
+						horizontalPixel,
+						normToHeightConvert(chunk.getNormEnd())));
+				horizontalPixel++;
+			}
+		}
+		
+		paintHapticViewPort(g2, width, height);
+		/* paint the sequence graphs, if any */
+		automationGraph.draw(g);
+		peakMeterGraph.draw(g);
+		
+		
+		g2.setColor(CURSOR_COLOR);
+		/* draw cursor */
+		g2.drawLine(cursorPos, 0, cursorPos, height);
+
+		if(mouseSelection.isOpen()){  
+			/* paints one line as the selection has no length */ 
+			g2.drawLine(mouseSelection.getStart(), 0, mouseSelection.getStart(), height);
+		}else{  
+			/* paint the selection left and right boundaries */
+			/* draw the boundaries of the selection with CURSOR_COLOR */
+			g2.drawLine(mouseSelection.getStart(), 0, mouseSelection.getStart(), height);
+			g2.drawLine(mouseSelection.getEnd(), 0, mouseSelection.getEnd(), height);
+		}
+		g2.setColor(oldColor);
+	}
+	
+	protected void paintHapticViewPort(Graphics g2, int width, int height){
+		if(!showHapticViewPort)
+			return;
+		
+		Color oldColor = g2.getColor();
+		g2.setColor(VIEW_PORT_COLOR);
+		
+		int leftBound = hapticViewPort.getPosition(0.0f);
+		if(leftBound == 0) // make it visible if it's at 0 
+			leftBound = 1;
+		
+		g2.drawLine(leftBound-1, height/4, leftBound-1, height*3/4);
+		g2.drawLine(leftBound, 0, leftBound, height); // little bar on the left to give the idea of direction
+		int rightBound = hapticViewPort.getPosition(1.0f);
+		g2.drawLine(rightBound, 0, rightBound, height);// little bar on the right to give the idea of direction
+		g2.drawLine(rightBound+1,  height/4, rightBound+1, height*3/4);
+		g2.setColor(oldColor);
+	}
+	
+	public SoundWave getSoundWave(){
+		return soundWave;
+	}
+	
+	public int getScaleFactor() {
+		return scaleFactor;
+	}
+	
+	public void setScaleFactor(int factor) {
+		/* sets the scale factor in the SoundWave to scaleFactor          *
+		 * _setScaleFactor will be called right after as a SoundWaveEvent *
+		 * will be triggered by SoundWave. see update() */
+		soundWave.setScaleFactor(factor);	
+	}
+
+	/* implements scaleFactor body. It's called by update() after the model's scale factor 
+	 * gets changed */
+	private void _setScaleFactor(int factor) {
+		if(factor < 1 || factor > getSoundWave().getMaxScaleFactor() ){
+			Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
+			return;
+		}
+			
+		int oldScaleFactor = scaleFactor;
+		scaleFactor = factor;
+		
+		secondsPerPixel = soundWave.getMillisecPerChunk()/1000;
+		
+		/* update haptic view port */
+		hapticViewPort.setScaleFactor(scaleFactor);
+		
+		/* resize the selection after zooming in/out */
+		Selection oldSelection = currentSelection; 
+		int start = (int) (oldSelection.getStart() * Math.pow(2,oldScaleFactor - factor));
+		if(oldSelection.isOpen())
+			currentSelection = new Selection(start,factor);
+		else{
+			int end = (int) (oldSelection.getEnd() * Math.pow(2,oldScaleFactor - factor));
+			currentSelection = new Selection(start,end,factor);
+		}
+		
+		/* if the selection is open, it will be painted as a one-pixel selection */
+		trackInteraction.setMouseSelection(currentSelection.getStart(), currentSelection.getEnd());
+
+		/* make listeners aware the mouse selection has changed */
+		firePropertyChange("mouseDragSelection",oldSelection,currentSelection);
+		
+		setPreferredSize(new Dimension(soundWave.getChunkNum()+30,MAX_TRACK_HEIGHT));
+		revalidate();
+		
+		setCursorPos(soundWave.getCurrentChunkPosition()); 
+		/* update the sequences  */
+		automationGraph.setSize(getSize());
+		automationGraph.setMillisecPerPixel(AudioTrack.this.soundWave.getMillisecPerChunk());
+		automationGraph.updateSequence();
+		
+		peakMeterGraph.setSize(getSize());
+		peakMeterGraph.setMillisecPerPixel(AudioTrack.this.soundWave.getMillisecPerChunk());
+		peakMeterGraph.updateSequence();
+		
+		repaint();
+		firePropertyChange("scaleFactor", oldScaleFactor, scaleFactor);
+	}	
+
+	public Selection getSelection(){
+		return currentSelection;
+	}
+	
+	/**
+	 * Doesn't change the cursor position of the underlying soundwave
+	 * @param position
+	 */
+	public void setCursorPos(int position) {
+		int oldCursorPos = cursorPos; 
+		cursorPos = position;
+		
+		repaint();
+		scrollRectToVisible(new Rectangle(new Point(position,0)));
+		firePropertyChange("cursorPos", oldCursorPos, position);
+	}
+	
+	public int getCursorPos(){
+		return cursorPos;
+	}
+	
+	public float getSecondsPerPixel(){
+		return secondsPerPixel;
+	}
+	
+	public SequenceGraph getAutomationGraph(){
+		return automationGraph;
+	}
+	
+	public SequenceGraph getPeakLevelGraph(){
+		return peakMeterGraph;
+	}
+	
+	/**
+	 * Makes it available to {@code Rule} class to notify listeners after changing 
+	 * the mouseDragSelection.
+	 * 
+	 * @param property the programmatic name of the property that was changed
+	 * @param oldValue the old value of the property 
+	 * @param newValue the new value of the property
+	 */
+	@Override
+	protected void firePropertyChange(String property, Object oldValue, Object newValue){
+		super.firePropertyChange(property, oldValue, newValue);
+	}
+	
+	public HapticViewPort getHapticViewPort(){
+		return hapticViewPort;
+	}
+	
+	public void showHapticViewPort(boolean show){
+		showHapticViewPort = show;
+		repaint();
+	}
+	
+	public void showAutomationSound(boolean show){
+		showAutomationSound = show;
+		
+		SequenceMapping sonificationSeqMapping = Daw.getSoundEngineFactory().getSharedSonification()
+				  .getSequenceMapping(SoundType.AUTOMATION);
+		if(show){
+			sonificationSeqMapping.renderCurveAt(
+					soundWave.getSequence(),
+					soundWave.getMillisecPerChunk() * cursorPos,
+					SequenceMapping.DURATION_INF);
+		}else{
+			sonificationSeqMapping.renderCurveAt(null, 0.0f, SequenceMapping.DURATION_STOP);			
+		}
+			
+	}
+	
+	public void showPeakLevelSound(boolean show){
+		showPeakLevelSound = show;
+		
+		SequenceMapping sonificationSeqMapping =  Daw.getSoundEngineFactory().getSharedSonification()
+			  .getSequenceMapping(SoundType.PEAK_LEVEL);
+		if(show){
+			sonificationSeqMapping.renderCurveAt(
+					soundWave.getDbWave().getSequence(),
+					soundWave.getMillisecPerChunk() * cursorPos,
+					SequenceMapping.DURATION_INF);
+		}else{
+			sonificationSeqMapping.renderCurveAt(null, 0.0f, SequenceMapping.DURATION_STOP);
+		}		
+	}
+	
+	public void showDbWave(boolean show){
+		showDbWave = show;
+		repaint();
+	}
+	
+	
+	public int normToWidthconvert(float f){
+	    return hapticViewPort.getPosition(f);
+	}
+	/**
+	 * 
+	 * @param s a value sample value ranging from Short.MIN_VALUE to Short.MAX_VALUE
+	 * @param height the height of the graphic component where the track is to be drawn
+	 * @return the Y value in the graphic component resulted from mapping the sample to the graphic component height  
+	 */
+	public int normToHeightConvert(float f){
+		/* in a JPanel pixel y coordinate increases from top to bottom, whereas the *
+		 * in the signed short PCM format y value increased from bottom to top      *
+		 * so the sign of the value is reversed to match it with the JPanel space   */
+		int height = getHeight();
+		return  height - (int)( f *height);
+	}
+	
+	/**
+	 * Returns a point in the {@code Automation} coordinates - x = length in millis, 
+	 * y = range of the automation - corresponding to the point on {@code track} 
+	 * which has coordinates {@code (mouseX, mouseY) }
+	 * 
+	 * 
+	 * @param track
+	 * @param mouseX the {@code x} coordinate of the point on {@code track}
+	 * @param mouseY  the {@code y} coordinate of the point on {@code track}
+	 * @return the point in the {@code Automation} coordinates corresponding to (mouseX,mouseY) 
+	 * or {@code null} if mouseX is bigger than the automation length (in chunks of the track's {@code SoundWave})
+	 * or mouseY is bigger than the track's height, or if either is lower than zero.
+	 * 
+	 */
+	public static Point2D.Float getAutomationCoord(AudioTrack track, int mouseX, int mouseY ){
+		SoundWave wave = track.getSoundWave();
+		Automation autom = wave.getParametersControl().getCurrentAutomation();
+		
+		/* get height and width of the track panel */
+		float height = track.getSize().height;
+		
+		float width = track.getSoundWave().getChunkNum();
+		
+		if(mouseX >= width || mouseY > height || mouseX < 0 || mouseY < 0 ){
+			return null;
+		}
+		
+		/* calculate the automation x (a.k.a. position over time) from            *  
+		 * the mouse click according following relation:                          *
+		 * mouse x / width of the panel = automation x position / length of sample  */
+		float automationX = (float)(autom.getLen()*(mouseX/width));
+		/* calculate the automation y (a.k.a. value int he parameter range) from     *  
+		 * the mouse click according following relation:                           *
+		 * mouse y / height of the panel = automation y position / value range     *    
+		 * since in swing y has 0 at the top the y value is reversed height-mouseY */
+		float automationY = (float)(autom.getRange().lenght() *((height-mouseY)/height));
+		return new Point2D.Float(automationX,automationY+autom.getRange().getStart());
+	}
+	
+
+
+	private class CursorUpdaterOnPlay implements ActionListener {
+		private static final int REFRESH_DELAY = 50;
+		Timer timer = new Timer(REFRESH_DELAY,this);;
+		
+		public void update(SoundWaveEvent evt) {
+			if(SoundWaveEvent.START.equals(evt.getType())){
+				timer.start();
+			}else if(SoundWaveEvent.STOP.equals(evt.getType()) || 
+					SoundWaveEvent.PAUSE.equals(evt.getType())){
+				timer.stop();
+			}
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			int cursorPos = Math.round(soundWave.getTransportControl().getPlayPosition()/soundWave.getMillisecPerChunk()); 
+			setCursorPos(cursorPos); 
+		}
+	} // class ClipInteraction
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackInput.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,465 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.Dimension;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Point2D;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.event.MouseInputAdapter;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.AutomationValue;
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Direction;
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+
+/*
+ * This class handles the interaction with the mouse. When the mouse is dragged around 
+ * the corresponding selection appears on this audio track. When the user releases the 
+ * mouse button (and therefore stops dragging) the selection is assigned to 
+ * the underlying sound wave by a call to setSelection().   
+ */
+ final class AudioTrackInput extends MouseInputAdapter implements KeyListener  {
+	private static final int KEY_SELECTION_INCREMENT = 1;
+	private static final int KEY_PRESS_NO_SELECTION = -1;
+	private int mousePress; // where the user presses the mouse 
+	private int keyPress;
+	/* used to check whether the user is dragging a new selection */
+	private boolean isDragging;
+	private Selection mouseSelection;
+	private AudioTrack track;
+	private SequencePoint dragAutomationPoint;	
+	
+	AudioTrackInput(AudioTrack track){	
+		this.track = track;
+		setAudioTrackActions(track);
+		mouseSelection = Selection.ZERO_SELECTION;
+		keyPress = KEY_PRESS_NO_SELECTION;
+	}
+	
+	private void setAudioTrackActions(AudioTrack track){
+		for(Action action : AutomationGraphActions.getInstance()){
+			Object unique = new Object();
+			
+			track.getInputMap(JComponent.WHEN_FOCUSED).put((KeyStroke)action.getValue(Action.ACCELERATOR_KEY), unique);
+			track.getActionMap().put(unique,action);
+		}
+	}
+	
+	/**
+	 * Sets the mouse selection to a new {@code Selection} ranging from {@code start} to {@code end}.
+	 * 
+	 * @param start
+	 * @param end
+	 */
+	public void setMouseSelection(int start, int end){
+		Selection oldSelection = mouseSelection;
+		if(end == -1) // open Selection
+			mouseSelection = new Selection(start,track.getScaleFactor());
+		else
+			mouseSelection = new Selection(start,end,track.getScaleFactor());
+		
+		track.firePropertyChange("mouseDragSelection",
+				oldSelection,
+				mouseSelection);
+		track.repaint();
+	}
+	
+	/**
+	 * Sets the mouse selection and sets the beginning of the selection action to 
+	 * either {@code start} or {@start end} (according to the value of {@code mouseAtStart}). 
+	 * 
+	 * The beginning of the selection action is where a user presses the mouse click 
+	 * in order to start a dragging action which creates the selections. The click
+	 * point affects how the selection is: if the user drags towards
+	 * left, the selection starts from the current dragging point and ends at the click point;
+	 * conversely if the user drags towards right the selection starts at the click point and
+	 * end at the current dragging point. The current dragging point changes as the user drags the mouse
+	 * around
+	 * 
+	 * @param start the beginning of the selection
+	 * @param end start the end of the selection
+	 * @param mouseAtStart {@code true} if the beginning of the selection action was the 
+	 * selection start, {@code false} if it's the selection end
+	 * 
+	 */
+	private void setMouseSelection(int start, int end, boolean mouseAtStart){ // FIXME remove
+		if(end == -1){ // open selection
+			mousePress = start;
+		}else{
+			mousePress = mouseAtStart ? start : end;
+		}
+		setMouseSelection(start,end);
+	}
+	
+	/**
+	 * Returns the current mouse selection or {@code Selection.VOID_SELECTION} if there is no selection.
+	 * 
+	 * @return the current mouse selection or {@code Selection.VOID_SELECTION} if there is no selection.
+	 */
+	public Selection getMouseSelection(){
+		return mouseSelection;
+	}
+	
+	@Override
+	public void mousePressed(MouseEvent evt){
+		if(SwingUtilities.isLeftMouseButton(evt)){
+			/* if automation is on */
+			if(track.getSoundWave().hasSequence()){
+				for(SequencePoint p : track.getAutomationGraph().getSequencePoints()){
+					if(p.contains(evt.getX(),evt.getY())){
+						dragAutomationPoint = p;
+						return;
+					}
+				}
+			}
+			
+			/* keep track of where the mouse is pressed. If the user drags a selection this is needed     *
+			 * to make the right selection according to the direction of the dragging. see mouseDragged() */
+			mousePress = evt.getX();
+		}
+	}
+	
+	@Override
+	public void	mouseDragged(MouseEvent evt){
+		if(SwingUtilities.isLeftMouseButton(evt)){
+			
+			/* bound the dragging to the space within the sound wave in the track */
+			Dimension trackSize = track.getSize();
+			if(evt.getX()<0 || evt.getX() >= track.getSoundWave().getChunkNum() || evt.getY()<0 || evt.getY() > trackSize.height )
+				return;
+			
+			/* if we are dragging an automation point, change its coordinate and refresh the automation */
+			if(track.getSoundWave().hasSequence() && dragAutomationPoint != null){
+				Point2D.Float pointCoord = AudioTrack.getAutomationCoord(track,evt.getX(),evt.getY());
+				AutomationValue automVal = (AutomationValue)dragAutomationPoint.getSequenceValue(); 
+				automVal.setLocation(pointCoord.x,pointCoord.y);
+
+				return;
+			}
+			
+			/* update the mouse selection as the user drags the mouse. The selection differs     * 
+			 * according to the direction of the dragging. If the user moves right from where    *  
+			 * they pressed, then the start of the selection is the press point, and the end the *
+			 * current dragging point (evt.getX()). Conversely, if the user moves to the left,   *
+			 * the start of the selection is the dragging point and the end is the press point.  *   
+			 * The press point is the value assigned to mousePressX in mousePressed              */
+			
+			isDragging = true;
+			if(evt.getX() > mousePress){
+				setMouseSelection(mousePress,evt.getX());
+			}else{
+				setMouseSelection(evt.getX(),mousePress);
+			}
+		}
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent evt){
+		if(SwingUtilities.isLeftMouseButton(evt)){
+			dragAutomationPoint = null;
+			if(isDragging){
+				/* if the user was dragging update the selection in the sound wave */
+				track.getSoundWave().setSelection(getMouseSelection());
+				isDragging = false;
+			}
+		}
+	}
+	
+	@Override
+	public void mouseClicked(MouseEvent evt){
+		/* left click  : set the cursor position * 
+		 * right click : open pop up menu         */
+		if(SwingUtilities.isLeftMouseButton(evt)){
+			track.getSoundWave().setPosition(evt.getX());
+		}else if(SwingUtilities.isRightMouseButton(evt)){
+			/* show automation popup only if an automation is is displayed */
+			if(track.getSoundWave().hasSequence()){
+				for(Shape point : track.getAutomationGraph().getSequencePoints()){
+					if(point.contains(evt.getX(), evt.getY())){
+						showAutomationPopup(point,evt.getX(),evt.getY());
+						return;
+					}
+				}
+				
+				for(Shape line : track.getAutomationGraph().getSequenceLines()){
+					if(line.contains(evt.getX(), evt.getY())){
+						showAutomationPopup(line,evt.getX(),evt.getY());
+						return;
+					}
+				}
+			}
+			
+			/* gets here only if no automation has been found under the mouse pointer */
+			if(track.getSoundWave().getDbWave().hasSequence()){
+				showPeakLevelPopup(evt.getX(),evt.getY());
+			}
+		}
+	}
+
+	
+	void showAutomationPopup(final Shape selectedShape, final int x, final int y){
+		final SoundWave wave = track.getSoundWave();
+		if(x >= wave.getChunkNum()){
+			/* don't show if the user clicks past the sound wave */
+			return;
+		}
+		
+		JPopupMenu pop = new JPopupMenu();
+		if(selectedShape instanceof SequencePoint){
+			pop.add(new AbstractAction("Remove automation point"){
+				private static final long serialVersionUID = 1L;
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					SequencePoint point = (SequencePoint)selectedShape;
+					Automation automation = wave.getParametersControl().getCurrentAutomation(); 
+					automation.remove((AutomationValue)point.getSequenceValue());
+				}
+			});
+		}else{ // Line2D
+			pop.add(new AbstractAction("Add automation point"){
+				private static final long serialVersionUID = 1L;
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					Automation automation = wave.getParametersControl().getCurrentAutomation(); 
+					Point2D.Float p = AudioTrack.getAutomationCoord(track,x,y); 
+					automation.add(p.x, p.y); 
+				}
+			});
+		}
+		
+		pop.add(new AbstractAction("Reset Automation"){
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				AutomationGraphActions.getInstance().resetAutomationAction.handleActionPerformed(track);
+			}
+		});
+		
+		Action listenToAutomation = new AbstractAction("Listen to Automation"){
+			private static final long serialVersionUID = 1L;
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				AutomationGraphActions.getInstance().listenWholeAutomationAction.handleActionPerformed(track);
+			}
+		}; 
+		listenToAutomation.putValue(Action.ACCELERATOR_KEY, 
+				AutomationGraphActions.getInstance().listenWholeAutomationAction.getValue(Action.ACCELERATOR_KEY));
+		pop.add(listenToAutomation);
+		
+		Action stopListenToAutomation = new AbstractAction("Stop Listen To Automation"){
+			private static final long serialVersionUID = 1L;
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				AutomationGraphActions.getInstance().stopListenWholeAutomationAction.handleActionPerformed(track);
+			}
+		};
+		stopListenToAutomation.putValue(Action.ACCELERATOR_KEY, 
+				AutomationGraphActions.getInstance().stopListenWholeAutomationAction.getValue(Action.ACCELERATOR_KEY));
+		pop.add(stopListenToAutomation);
+		
+		Action switchListenAutomation = new AbstractAction("Switch Listen Automation "+
+				(AutomationGraphActions.getInstance().switchListenAutomationAction.isSwitchOn() ? "off" : "on")){
+			private static final long serialVersionUID = 1L;
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				AutomationGraphActions.getInstance().switchListenAutomationAction.handleActionPerformed(track);
+			}
+		};
+		switchListenAutomation.putValue(Action.ACCELERATOR_KEY, 
+				AutomationGraphActions.getInstance().switchListenAutomationAction.getValue(Action.ACCELERATOR_KEY));
+		pop.add(switchListenAutomation);
+		pop.show(track, x, y);
+	}
+
+	void showPeakLevelPopup(int x , int y){
+		final SoundWave wave = track.getSoundWave();
+		if(x >= wave.getChunkNum()){
+			/* don't show if the user clicks past the sound wave */
+			return;
+		}
+		
+		JPopupMenu pop = new JPopupMenu();
+		
+		pop.add(new AbstractAction("Switch listen peak level " + 
+			(AutomationGraphActions.getInstance().switchListenPeakLevelAction.isSwitchOn() ? "off" : "on")){
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				AutomationGraphActions.getInstance().switchListenPeakLevelAction.handleActionPerformed(track);
+			}			
+		});
+		
+		pop.add(new AbstractAction("Listen to Peak Level"){
+			private static final long serialVersionUID = 1L;
+			
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				AutomationGraphActions.getInstance().listenWholePeakLevelAction.handleActionPerformed(track);
+			}
+		});
+		
+		pop.show(track,x,y);
+	}
+	
+	@Override
+	public void keyPressed(KeyEvent evt) {
+		/* adjust the selection */
+		if(evt.isShiftDown() && evt.isControlDown() && 
+				!mouseSelection.equals(Selection.ZERO_SELECTION )){			
+			Direction d = Direction.NONE;
+			if(evt.getKeyCode() == KeyEvent.VK_LEFT){
+				d = Direction.LEFT;
+			}else if(evt.getKeyCode() == KeyEvent.VK_RIGHT){
+				d = Direction.RIGHT;
+			} else {
+				return;
+			}
+			
+			SoundWave wave = track.getSoundWave();
+			int cursorPos = wave.getCurrentChunkPosition();
+			if(cursorPos == mouseSelection.getStart()){
+				if(d == Direction.LEFT){ // left 
+					wave.setSelection(new Selection(
+							mouseSelection.getStart()-KEY_SELECTION_INCREMENT,
+							mouseSelection.getEnd(),
+							wave.getScaleFactor()));
+					wave.scan(cursorPos-KEY_SELECTION_INCREMENT);
+				}else{ // right 
+					if(mouseSelection.getEnd() - mouseSelection.getStart() <= KEY_SELECTION_INCREMENT){
+						/* cannot push it too right so as to trespass the end of the selection */
+						Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
+					}else{
+						wave.setSelection(new Selection(
+								mouseSelection.getStart()+KEY_SELECTION_INCREMENT,
+								mouseSelection.getEnd(),
+								wave.getScaleFactor()));
+						wave.scan(cursorPos+KEY_SELECTION_INCREMENT);
+					}
+				}
+			}else if (cursorPos == mouseSelection.getEnd()){
+				if(d == Direction.LEFT){ // left 
+					if(mouseSelection.getEnd() - mouseSelection.getStart() <= KEY_SELECTION_INCREMENT){
+						/* cannot push it too keft so as to trespass the start of the selection */
+						Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
+					}else{
+						wave.setSelection(new Selection(
+								mouseSelection.getStart(),
+								mouseSelection.getEnd()-KEY_SELECTION_INCREMENT,
+								wave.getScaleFactor()));
+						wave.scan(cursorPos-KEY_SELECTION_INCREMENT);
+					}
+				}else{ // right 
+					wave.setSelection(new Selection(
+							mouseSelection.getStart(),
+							mouseSelection.getEnd()+KEY_SELECTION_INCREMENT,
+							wave.getScaleFactor()));
+					wave.scan(cursorPos+KEY_SELECTION_INCREMENT);
+				}
+			}
+			
+		/* make selection with right arrow key */
+		} else if(evt.getKeyCode() == KeyEvent.VK_RIGHT && evt.isShiftDown() ){
+			if(keyPress == KEY_PRESS_NO_SELECTION){ // new selection 
+				keyPress = track.getSoundWave().getCurrentChunkPosition();
+				setMouseSelection(keyPress, keyPress+KEY_SELECTION_INCREMENT);
+				/* scrub along with the selection right end */
+				track.getSoundWave().scan(getMouseSelection().getEnd());
+				/* start the selection sound */
+			}else{ // user is adjusting the selection 
+				/* if mouseSelection.getStart == keypress, it means the selection is at the       *
+				 * right side of where the user started to select. Conversely id mouse selection  *
+				 * .getEnd == keypress, the selection is at the left of the point where the user   *
+				 * started to select. In the former case pressing the right key will expand       *
+				 * the selection to the right, whereas in latter case pressing the right key will  *
+				 * shrink the selection to the right.                                             */
+				
+				if(mouseSelection.getStart() == keyPress){
+					setMouseSelection(keyPress,mouseSelection.getEnd()+KEY_SELECTION_INCREMENT);
+					track.getSoundWave().scan(getMouseSelection().getEnd());
+				}else{ // getEnd == keyPress
+					setMouseSelection(mouseSelection.getStart()+KEY_SELECTION_INCREMENT,keyPress);
+					track.getSoundWave().scan(getMouseSelection().getStart());
+				}
+			}
+			
+		/* make selection with left arrow key */	
+		}else if(evt.getKeyCode() == KeyEvent.VK_LEFT && evt.isShiftDown()){
+			if(keyPress == KEY_PRESS_NO_SELECTION){
+				keyPress = track.getSoundWave().getCurrentChunkPosition();
+				
+				/* keep the selection after the beginning - 0 - of the audio track  */
+				int start = keyPress-KEY_SELECTION_INCREMENT > 0 ? keyPress-KEY_SELECTION_INCREMENT : 0; 
+				setMouseSelection(start,keyPress);
+				/* scrub along with the selection left end */
+				track.getSoundWave().scan(getMouseSelection().getStart());
+			}else{ // user is adjusting the selection
+				/* See selectio adjustment for the right key.              *
+				 * The idea is the same, just with inverted left and right */
+				if(mouseSelection.getEnd() == keyPress){
+					setMouseSelection(mouseSelection.getStart()-KEY_SELECTION_INCREMENT,keyPress);
+					track.getSoundWave().scan(getMouseSelection().getStart());
+				}else{ // getStart == keyPress
+					setMouseSelection(keyPress,mouseSelection.getEnd()-KEY_SELECTION_INCREMENT);
+					track.getSoundWave().scan(getMouseSelection().getEnd());
+				}
+			}
+			
+		/* jump to beginning of selection, if any */	
+		}else if(evt.getKeyCode() == KeyEvent.VK_F2 || evt.getKeyCode() == KeyEvent.VK_F3){
+			if(mouseSelection.equals(Selection.ZERO_SELECTION)){
+				Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
+			}else if (evt.getKeyCode() == KeyEvent.VK_F2){
+				track.getSoundWave().setPosition(mouseSelection.getStart());
+			}else{
+				track.getSoundWave().setPosition(mouseSelection.getEnd()); 
+			}
+		}
+	}
+
+	@Override
+	public void keyReleased(KeyEvent evt) {
+		/* create selection in the model when user releases shift key  */
+		if(evt.getKeyCode() == KeyEvent.VK_SHIFT && keyPress != KEY_PRESS_NO_SELECTION){
+			keyPress = KEY_PRESS_NO_SELECTION;
+			track.getSoundWave().setSelection(getMouseSelection());
+		}
+	}
+
+	@Override
+	public void keyTyped(KeyEvent evt) { 	}	
+} // class AudioTrackMouseInteraction
+ 
+ 
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackParameters.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,281 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.JToggleButton;
+import javax.swing.ListCellRenderer;
+import javax.swing.border.MatteBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Parameter;
+import uk.ac.qmul.eecs.depic.daw.Parameter.Type;
+import uk.ac.qmul.eecs.depic.daw.ParametersControl;
+import uk.ac.qmul.eecs.depic.patterns.SequenceEvent;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+
+public class AudioTrackParameters extends JPanel {
+	private static final long serialVersionUID = 1L;
+	private final static int SLIDER_SCALE = 100;
+	
+	private AudioTrack track;
+	public final JLabel nameLabel;
+	public final JLabel gainLabel;
+	public final JToggleButton muteButton;
+	public final JToggleButton soloButton;
+	public final JButton fxButton;
+	public final JComboBox<Parameter.Type> automationComboBox;
+	public final JSlider gainSlider;
+	public final JLabel panLabel;
+	public final JSlider panSlider;
+	
+	/**
+	 * Create the panel.
+	 */
+	public AudioTrackParameters(AudioTrack track, int index) {
+		this.track = track;
+		
+		this.setPreferredSize(new Dimension(247, 130)); //FIXME const values 
+		this.setMaximumSize(new Dimension(247, 130));
+		
+		
+		nameLabel = new JLabel("New Track "+index);
+		nameLabel.setBounds(6, 8, 233, 17);
+		nameLabel.setBorder(new MatteBorder(0, 0, 1, 0, (Color) new Color(0, 0, 0)));
+		
+		soloButton = new JToggleButton("S");
+		soloButton.setBounds(205, 84, 36, 20);
+		soloButton.getAccessibleContext().setAccessibleName("Solo");
+		soloButton.setMargin(new Insets(0, 0, 0, 0));
+		soloButton.setMinimumSize(new Dimension(20, 20));
+		soloButton.setMaximumSize(new Dimension(20, 20));
+		soloButton.setPreferredSize(new Dimension(20, 20));
+		
+		muteButton = new JToggleButton("M");
+		muteButton.setBounds(166, 84, 38, 20);
+		muteButton.getAccessibleContext().setAccessibleName("Mute");
+		muteButton.setMargin(new Insets(0, 0, 0, 0));
+		muteButton.setFont(new Font("Tahoma", Font.PLAIN, 11));
+		muteButton.setPreferredSize(new Dimension(20, 20));
+		muteButton.setMinimumSize(new Dimension(20, 20));
+		muteButton.setMaximumSize(new Dimension(20, 20));
+		
+		fxButton = new JButton("Fx");
+		fxButton.setBounds(12, 30, 40, 20);
+		fxButton.setPreferredSize(new Dimension(20, 20));
+		fxButton.setMinimumSize(new Dimension(20, 20));
+		fxButton.setMaximumSize(new Dimension(20, 20));
+		fxButton.setMargin(new Insets(0, 0, 0, 0));
+		fxButton.setFont(new Font("Tahoma", Font.PLAIN, 11));
+		
+		automationComboBox = new JComboBox<>();
+		automationComboBox.setBounds(166, 29, 73, 18);
+		
+		JLabel lblAutomation = new JLabel("Automation:");
+		lblAutomation.setBounds(95, 30, 65, 16);
+		lblAutomation.setLabelFor(automationComboBox);
+		
+		gainLabel = new JLabel("");
+		gainLabel.setBounds(6, 106, 87, 16);
+		setLayout(null);
+		add(nameLabel);
+		add(fxButton);
+		add(gainLabel);
+		add(lblAutomation);
+		add(automationComboBox);
+		add(muteButton);
+		add(soloButton);
+		
+		gainSlider = new JSlider();
+		gainSlider.setBounds(103, 103, 136, 21);
+		add(gainSlider);
+		
+		panLabel = new JLabel("Pan: 0.0 ");
+		panLabel.setBounds(6, 84, 61, 16);
+		add(panLabel);
+		
+		panSlider = new JSlider();
+		panSlider.setBounds(68, 84, 78, 21);
+		add(panSlider);
+		
+		initComponentsValues();
+		addListeners();
+	}
+	
+	private void addListeners(){
+		gainSlider.addChangeListener(new GainListener());
+		panSlider.addChangeListener(new PanListener());
+		automationComboBox.addItemListener(new AutomationComboBoxListener());
+		
+	}
+	
+	private void initComponentsValues(){
+		ParametersControl parametersControl = track.getSoundWave().getParametersControl();
+		
+		/* gain slider */
+		Parameter volume = parametersControl.getGainParameter();
+		gainSlider.setMinimum((int)(volume.getRange().getStart()*SLIDER_SCALE));
+		gainSlider.setMaximum((int)(volume.getRange().getEnd()*SLIDER_SCALE));
+		gainSlider.setValue((int)volume.getValue()*SLIDER_SCALE);
+		
+		gainLabel.setText(volume.getType().getLabel()+(volume.getInitialValue()+" "+volume.getType().getUnitofMeasurment()));
+		gainLabel.setLabelFor(gainSlider);
+		
+		Parameter pan = parametersControl.getPanParameter();
+		panSlider.setMinimum((int)(pan.getRange().getStart()*SLIDER_SCALE));
+		panSlider.setMaximum((int)(pan.getRange().getEnd()*SLIDER_SCALE));
+		panSlider.setValue((int)pan.getValue()*SLIDER_SCALE);
+		
+		
+		float panValue = panSlider.getValue();
+		panLabel.setText(pan.getType().getLabel()+(panValue/SLIDER_SCALE));
+		panLabel.setLabelFor(panSlider);
+		/* automation combo box */
+		
+		class ParameterTypeRenderer extends JLabel implements ListCellRenderer<Parameter.Type> {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public Component getListCellRendererComponent(
+					JList<? extends Type> list, Type value, int index,
+					boolean isSelected, boolean cellHasFocus) {
+				if (isSelected) {
+		            setBackground(list.getSelectionBackground());
+		            setForeground(list.getSelectionForeground());
+		        } else {
+		            setBackground(list.getBackground());
+		            setForeground(list.getForeground());
+		        }
+
+				setText("  "+value.getLabel());
+				
+				return this;
+			}
+			
+		}
+		
+		automationComboBox.setModel(new DefaultComboBoxModel<Parameter.Type>(
+				new Parameter.Type[] {Parameter.NONE_PARAMETER.getType(), Parameter.GAIN_TYPE, Parameter.PAN_TYPE}));
+				
+		automationComboBox.setRenderer(new ParameterTypeRenderer());
+	}
+	
+	public void setLabel(String label){
+		nameLabel.setText(label);
+		
+		track.getAccessibleContext().setAccessibleName(label);
+	}
+	
+	/**
+	 * Returns the text of this component label
+	 * 
+	 * @see #setLabel(String)
+	 * @return the text of this component label
+	 */
+	@Override
+	public String toString(){
+		return nameLabel.getText();
+	}
+	
+	private class GainListener implements ChangeListener {
+		@Override
+		public void stateChanged(ChangeEvent evt) {
+			JSlider gain = (JSlider)evt.getSource();
+			
+			float newValue = (float)gain.getValue()/SLIDER_SCALE;
+			Parameter gainParam = track.getSoundWave().getParametersControl().getGainParameter();
+			gainLabel.setText(gainParam.getType().getLabel()+(newValue)+" "+gainParam.getType().getUnitofMeasurment());
+			gainParam.setValue(newValue);
+		}
+	}
+	
+	private class PanListener implements ChangeListener {
+		@Override
+		public void stateChanged(ChangeEvent evt) {
+			JSlider pan = (JSlider)evt.getSource();
+			
+			float newValue = (float)pan.getValue()/SLIDER_SCALE;
+			Parameter panParam = track.getSoundWave().getParametersControl().getPanParameter();
+			panParam.setValue(newValue);
+			panLabel.setText(panParam.getType().getLabel()+(newValue));
+		}
+	}
+	
+	private class AutomationComboBoxListener implements ItemListener { 
+		@Override
+		public void itemStateChanged(ItemEvent evt) {
+			if(evt.getStateChange() == ItemEvent.SELECTED){
+				Parameter.Type type = (Parameter.Type)evt.getItem();
+				track.getSoundWave().getParametersControl().setCurrentAutomation(type);
+				
+				if(Parameter.GAIN_TYPE.equals(type)){
+					/* get the now current autiomation */
+					Automation a = track.getSoundWave().getParametersControl().getCurrentAutomation();
+					a.addSequenceListener(new SequenceListener(){
+
+						@Override
+						public <T extends SequenceEvent> void sequenceUpdated(T t) {
+							if(t.getSource().getValuesNum() != 0){
+								gainSlider.setEnabled(false);
+							}else {
+								gainSlider.setEnabled(true);
+								float newValue = t.getSource().getBegin() * SLIDER_SCALE;
+								gainSlider.setValue((int)newValue);
+							}
+						}
+						
+					});
+				}else if(Parameter.PAN_TYPE.equals(type)) {
+					/* get the now current autiomation */
+					Automation a = track.getSoundWave().getParametersControl().getCurrentAutomation();
+					a.addSequenceListener(new SequenceListener(){
+
+						@Override
+						public <T extends SequenceEvent> void sequenceUpdated(T t) {
+							if(t.getSource().getValuesNum() != 0){
+								panSlider.setEnabled(false);
+							}else {
+								panSlider.setEnabled(true);
+								float newValue = (float)t.getSource().getBegin() * SLIDER_SCALE;
+								panSlider.setValue((int) newValue);
+							}
+						}
+						
+					});
+				}
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackSonification.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,114 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+
+class AudioTrackSonification implements PropertyChangeListener, ChangeListener {
+	private Sonification sonification = Daw.getSoundEngineFactory().getSharedSonification();
+	private AudioTrack track;
+	
+	AudioTrackSonification(AudioTrack track){
+		this.track  =  track;
+	}
+	
+	@Override
+	public void propertyChange(PropertyChangeEvent evt) {
+		if("cursorPos".equals(evt.getPropertyName())){
+			AudioTrack track = (AudioTrack)evt.getSource();
+			SoundWave soundWave = track.getSoundWave();
+			int newPos = (Integer)evt.getNewValue();
+			
+			if(soundWave.hasSequence()){ 
+				/* check if the cursor stumbled upon or left an automation point */
+				for(SequencePoint p : track.getAutomationGraph().getSequencePoints()){
+					if(p.isXCentredAt(newPos)){
+						sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(p.getSequenceValue()); //FIXME remove comments
+					}
+				}
+				
+				if(track.showAutomationSound){
+					sonification.getSequenceMapping(SoundType.AUTOMATION).renderCurveAt(
+							soundWave.getSequence(),
+							soundWave.getMillisecPerChunk() * newPos,
+							Float.POSITIVE_INFINITY);
+				}
+			}
+
+			if(soundWave.getDbWave().hasSequence()){
+				/* check if the cursor stumbled upon or left an automation point */
+//				for(SequencePoint p : track.getPeakLevelGraph().getSequencePoints()){
+//					if(p.isXCentredAt(newPos)){
+//						sonification.getSequenceMapping(GuiSoundType.PEAK_LEVEL).renderValue(p.getSequenceValue());
+//					}
+//				}
+				
+				if(track.showPeakLevelSound){
+					sonification.getSequenceMapping(SoundType.PEAK_LEVEL).renderCurveAt(
+							soundWave.getDbWave().getSequence(),
+							soundWave.getMillisecPerChunk() * newPos,
+							Float.POSITIVE_INFINITY);
+				}
+			}
+			
+			if(track.trackInteraction.getMouseSelection().isOpen()){
+				return;
+			}
+			
+			/* play selection sound if within selection, stop it if without */
+			if(track.trackInteraction.getMouseSelection().contains(newPos)){
+				sonification.withinSelection(1.0f, 0.0f);
+			}else {
+				sonification.outsideSelection();
+			}
+			
+			if(track.trackInteraction.getMouseSelection().getStart() == newPos){
+				sonification.play(SoundType.SHIFT_START);
+			}else if(track.trackInteraction.getMouseSelection().getEnd() == newPos){
+				sonification.play(SoundType.SHIFT_END);
+			}
+		}else if("mouseDragSelection".equals(evt.getPropertyName())){
+			if( ((Selection)evt.getNewValue()).isOpen() ){
+				sonification.outsideSelection();
+			}
+		}
+	}
+
+	@Override
+	public void stateChanged(ChangeEvent evt) {
+		if(track.showAutomationSound){
+			SoundWave soundWave = track.getSoundWave();
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderCurveAt(
+					soundWave.getSequence(),
+					soundWave.getMillisecPerChunk() * soundWave.getCurrentChunkPosition(),
+					Float.POSITIVE_INFINITY);
+		}
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/AutomationGraphActions.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,478 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.geom.Point2D;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.prefs.Preferences;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JComponent;
+import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.AutomationValue;
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.BeadsSonification;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+/**
+ * 
+ * Actions for the manipulation of the automation graph. 
+ * 
+ *
+ */
+class AutomationGraphActions implements Iterable<Action> {
+	private static AutomationGraphActions singleton; 
+	private SequencePoint heldPoint;
+	
+	public final AddAutomationValueAction addAutomationValueAction;
+	public final RemoveAutomationValueAction removeAutomationValueAction;
+	public final MoveAutomationValueAction moveAutomationValueUpAction;
+	public final MoveAutomationValueAction moveAutomationValueLeftAction;
+	public final MoveAutomationValueAction moveAutomationValueDownAction;
+	public final MoveAutomationValueAction moveAutomationValueRightAction;
+	public final ResetAutomationValuesAction resetAutomationAction;
+	public final ListenWholeAutomationAction listenWholeAutomationAction;
+	public final StopListenWholeAutomationAction stopListenWholeAutomationAction;
+	public final SwitchListenAutomationAction switchListenAutomationAction;
+	public final SwitchListenPeakLevelAction switchListenPeakLevelAction;
+	public final ListenWholePeakLevelAction listenWholePeakLevelAction;
+	
+	private Sonification sonification;
+
+	static AutomationGraphActions getInstance(){
+		if(singleton == null)
+			singleton = new AutomationGraphActions();
+		return singleton;
+	}
+	
+	private AutomationGraphActions (){ 
+		addAutomationValueAction = new AddAutomationValueAction();
+		removeAutomationValueAction = new RemoveAutomationValueAction();
+		moveAutomationValueUpAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl UP"),0.0f,0.05f);
+		moveAutomationValueDownAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl DOWN"),0.0f,-0.05f);
+		moveAutomationValueLeftAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl LEFT"),-50.0f,0.0f);
+		moveAutomationValueRightAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl RIGHT"),50.0f,0.0f);
+		resetAutomationAction = new ResetAutomationValuesAction();
+		listenWholeAutomationAction = new ListenWholeAutomationAction();
+		stopListenWholeAutomationAction = new StopListenWholeAutomationAction();
+		switchListenAutomationAction = new SwitchListenAutomationAction();
+		switchListenPeakLevelAction = new SwitchListenPeakLevelAction();
+		listenWholePeakLevelAction = new ListenWholePeakLevelAction();
+		
+		sonification = Daw.getSoundEngineFactory().getSharedSonification();
+	}
+	
+	/**
+	 * Create a new Actions than plays the automation value sound when it is at zero point, 
+	 * that is in the middle of the automation range  
+	 * 
+	 * @param val
+	 * @return
+	 */
+	private static Action createZeroValAction(final Sequence.Value val){
+		return new AbstractAction() {
+			private static final long serialVersionUID = 1L;
+			private boolean doneOnce = false;
+			@Override
+			public void actionPerformed(ActionEvent evt){
+				/* only play feedback once. This action remains until a new action 
+				 * for the same keystroke is installed. A new action is installed 
+				 * when the user presses ctrl + up/down/left/right arrow (see MoveAutomation 
+				 * Feedback as to be given only once by each zeroAction, otherwise 
+				 * the sonification will be played each time the user releases a ctrl + 
+				 * arrow action, regardless of whether they actually moved an automation or nor */
+				if(doneOnce){
+					return;
+				}else{
+					doneOnce = true;
+				}
+				Daw.getSoundEngineFactory().getSharedSonification().getSequenceMapping(SoundType.AUTOMATION).renderValue(
+					new Sequence.Value() {
+						@Override
+						public int index() {
+							return val.index();
+						}
+						
+						@Override
+						public float getValue() {
+							Range<Float> range = val.getSequence().getRange();
+							return range.getStart() + range.lenght()/2;
+						}
+						
+						@Override
+						public float getTimePosition() {
+							return val.getTimePosition();
+						}
+						
+						@Override
+						public Sequence getSequence() {
+							return val.getSequence();
+						}
+					});
+			}
+		};
+	}
+
+	@Override
+	public Iterator<Action> iterator(){
+		return new Iterator<Action>(){
+			private int index = 0;
+			private Action [] actions = new Action [] {
+					addAutomationValueAction,
+					removeAutomationValueAction,
+					moveAutomationValueUpAction,
+					moveAutomationValueLeftAction,
+					moveAutomationValueDownAction,
+					moveAutomationValueRightAction,
+					resetAutomationAction,
+					listenWholeAutomationAction,
+					stopListenWholeAutomationAction,
+					switchListenAutomationAction,
+					switchListenPeakLevelAction,
+					listenWholePeakLevelAction
+			};
+			
+			@Override
+			public boolean hasNext() {
+				return index < actions.length;
+			}
+
+			@Override
+			public Action next() {
+				if(index == actions.length)
+					throw new NoSuchElementException();
+				return actions[index++];
+			}
+
+			@Override
+			public void remove() {
+				throw new UnsupportedOperationException("remove not supported");
+			}
+			
+		};
+	}
+	
+	static abstract class AutomationGraphAction extends AbstractAction {
+		private static final long serialVersionUID = 1L;
+		
+		AutomationGraphAction(KeyStroke keyStroke){
+			putValue(Action.ACCELERATOR_KEY,keyStroke);
+		}
+		
+		@Override 
+		public void actionPerformed(ActionEvent evt){
+			handleActionPerformed((AudioTrack)evt.getSource());
+		}
+		
+		public abstract void handleActionPerformed(AudioTrack track);
+			
+	}
+	
+	class AddAutomationValueAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+		
+		public AddAutomationValueAction() {
+			super(KeyStroke.getKeyStroke("ctrl INSERT"));
+		}
+
+		@Override
+		public void handleActionPerformed(AudioTrack track) {
+			if(track == null || !track.getSoundWave().hasSequence()) {
+				return;
+			}
+			
+			SoundWave wave = track.getSoundWave();
+			int cursorPos = wave.getCurrentChunkPosition();
+			
+			Point2D.Float p = AudioTrack.getAutomationCoord(track, cursorPos, track.getHeight()/2);
+			
+			/* if user is trying to create an automation outside the bounds of the automation, notify the error */
+			if(p == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			/* if a new point is created when moving another point the held point will still be the old one        * 
+			 * by setting it to null the hel point will become the new one as soon as the user will try to move it */
+			heldPoint = null;
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(
+					wave.getParametersControl().getCurrentAutomation().add(p.x, p.y));
+		}
+	} // class AddAutomationAction
+	
+	class MoveAutomationValueAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+		private float deltaX;
+		private float deltaY;
+		
+		public MoveAutomationValueAction(KeyStroke keyStroke, float deltaX, float deltaY){
+			super(keyStroke);
+			this.deltaX = deltaX;
+			this.deltaY = deltaY;
+			heldPoint = null;
+		}
+		
+		@Override
+		public void handleActionPerformed(final AudioTrack track) {
+			if(track == null || !track.getSoundWave().hasSequence()){
+				heldPoint = null;
+				return;
+			}
+			
+			int cursorPos = track.getSoundWave().getCurrentChunkPosition();
+			
+			/* if ctrl is held (heldPoint != null) it means the user is still moving around a  * 
+			 * point that they grabbed previously. So the check that the point be under the    *
+			 * cursor must not be done, and the point can be moved and played anyway           */
+			if(heldPoint != null){
+				changeAutomation((AutomationValue)heldPoint.getSequenceValue(),track);
+				return;
+			}
+			
+			/* find the automaton value under the cursor position if any */
+			for(SequencePoint itr : track.getAutomationGraph().getSequencePoints()){
+				if( itr.isXCentredAt(cursorPos) ){
+					/* p is now the heldPoint, until the user releaser the ctrl key this point will be referenced * 
+					 * in next move action even if it's not under the the cursor is not under the cursor anymore  */
+					heldPoint = itr;
+					/* set up an action that will stop scrubbing free the held point when the user releases the ctrl key */
+					track.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0 , true), "heldPoint");
+					track.getActionMap().put("heldPoint", new AbstractAction(){
+						private static final long serialVersionUID = 1L;
+						@Override
+						public void actionPerformed(ActionEvent evt) {
+								heldPoint = null;
+								/* stop scrubbing */
+								track.getSoundWave().scan(SoundWave.STOP_SCANNING);
+								/* de-register itself from the track registered actions */
+								track.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0 , true), "none");
+								track.getActionMap().remove("heldPoint");
+						}
+					});
+					
+					changeAutomation((AutomationValue)itr.getSequenceValue(),track);
+					break;
+				}
+			}
+		}
+		
+		protected void changeAutomation(AutomationValue val, AudioTrack track){
+			val.setLocation(val.getTimePosition()+deltaX, val.getValue()+deltaY);
+			
+			/* install the action that plays the zero value when the key is released */
+			KeyStroke thisActionKeyStroke = (KeyStroke)getValue(Action.ACCELERATOR_KEY);
+			KeyStroke zeroValKeyStroke = KeyStroke.getKeyStroke(thisActionKeyStroke.getKeyCode(),thisActionKeyStroke.getModifiers(),true);
+			
+			if(Preferences.userNodeForPackage(BeadsSonification.class).getBoolean("render_val.ref", false)){
+				track.getInputMap(JComponent.WHEN_FOCUSED).put(zeroValKeyStroke, "zeroVal");
+				track.getActionMap().put("zeroVal", createZeroValAction(val));
+			}
+			
+			/* besides moving the automation, scrub the cursor along */
+			int scanTo = (int)(val.getTimePosition() / track.getSoundWave().getMillisecPerChunk());
+			track.getSoundWave().scan(scanTo);
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(heldPoint.getSequenceValue());
+		}
+	} // class MoveAutomationValueAction
+
+	class RemoveAutomationValueAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+
+		public RemoveAutomationValueAction() {
+			super(KeyStroke.getKeyStroke("ctrl DELETE"));
+		}
+
+		@Override
+		public void handleActionPerformed(AudioTrack track) {
+			if(track == null || !track.getSoundWave().hasSequence()){
+				return;
+			}
+			
+			int cursorPos = track.getSoundWave().getCurrentChunkPosition();
+			
+			/* find the automaton value */
+			SequencePoint p = null;
+			for(SequencePoint itr : track.getAutomationGraph().getSequencePoints()){
+				float dist = Math.abs(cursorPos - (int)itr.getCenterX() );
+				
+				if( dist  <  SequencePoint.SIZE ){
+					if(p == null || dist < Math.abs((int)p.getCenterX() - (int)itr.getCenterX())){
+						p = itr;
+						/* don't break as it keeps looking for other points, which might be closer to the cursor */
+					}
+				}
+			}
+			
+			/* if found, then remove it and play it */
+			if(p != null){
+				((Automation)p.getSequenceValue().getSequence()).remove((AutomationValue)p.getSequenceValue());
+				sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(p.getSequenceValue());
+			}
+		}		
+		
+	} // class RemoveAutomationAction
+	
+	class ResetAutomationValuesAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+
+		public ResetAutomationValuesAction() {
+			super(KeyStroke.getKeyStroke("ctrl R "));
+		}
+
+		@Override
+		public void handleActionPerformed(AudioTrack track){
+			if(track == null || !track.getSoundWave().hasSequence()){ 
+				return;
+			}
+			
+			int confirmation = JOptionPane.showConfirmDialog(track, 
+					"Are you sure you want to reset the automation ?", // FIXME bundle 
+					"Confirmation Dialog",
+					JOptionPane.YES_NO_OPTION
+					);
+			
+			if(confirmation == JOptionPane.YES_OPTION){
+				track.getSoundWave().getParametersControl().getCurrentAutomation().reset();
+			}			
+		}
+	} // class ClearAutomationValuesAction 
+	
+	class ListenWholeAutomationAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+
+		public ListenWholeAutomationAction() {
+			super(KeyStroke.getKeyStroke("ctrl shift L"));
+		}
+
+		@Override
+		public void handleActionPerformed(AudioTrack track){
+			if(track == null || (!track.getSoundWave().hasSequence()) ){ 
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			track.getSoundWave().getTransportControl().rew();
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderCurve(
+					track.getSoundWave().getParametersControl().getCurrentAutomation(),0.0f);			
+			track.getSoundWave().getTransportControl().play();
+		}
+	} // class SonifyAutomationAction
+	
+	class StopListenWholeAutomationAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+		
+		public StopListenWholeAutomationAction(){
+			super(KeyStroke.getKeyStroke("ctrl K"));
+		}
+		
+		@Override
+		public void handleActionPerformed(AudioTrack track){
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderCurve(
+					null,-100.0f);
+			sonification.getSequenceMapping(SoundType.PEAK_LEVEL).renderCurve(
+					null,-100.0f);
+			track.soundWave.getTransportControl().stop(); 
+		}
+	}
+	
+	class SwitchListenAutomationAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+		private boolean isOn;
+		
+		public SwitchListenAutomationAction(){
+			super(KeyStroke.getKeyStroke("ctrl L"));
+			isOn = false;
+		}
+		
+		@Override
+		public void handleActionPerformed(AudioTrack track){
+			if(track.getSoundWave().hasSequence()){
+				isOn = !isOn;
+				
+				track.showAutomationSound(isOn);
+				sonification.play(SoundType.OK);
+			}else{
+				sonification.play(SoundType.ERROR);
+			}
+		}
+		
+		public boolean isSwitchOn(){
+			return isOn;
+		}
+	} // class SwitchListenAutomation
+	
+	class SwitchListenPeakLevelAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+		private boolean isOn;
+		
+		SwitchListenPeakLevelAction(){
+			super(KeyStroke.getKeyStroke("ctrl P"));
+			isOn = false;
+		}
+		
+		@Override
+		public void handleActionPerformed(AudioTrack track){
+			if( track == null || (!track.getSoundWave().getDbWave().hasSequence())){
+				Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
+				return;
+			}
+			isOn = !isOn;
+					
+			track.showPeakLevelSound(isOn);
+			Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.OK);
+		}
+		
+		public boolean isSwitchOn(){
+			return isOn;
+		}
+	} // SwitchListenPeakLevelAction
+	
+	class ListenWholePeakLevelAction extends AutomationGraphAction {
+		private static final long serialVersionUID = 1L;
+		
+		ListenWholePeakLevelAction(){
+			super(KeyStroke.getKeyStroke("ctrl shift P"));
+		}
+		
+		@Override
+		public void handleActionPerformed(AudioTrack track){
+			if(track == null || (!track.getSoundWave().getDbWave().hasSequence()) ){ 
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			track.getSoundWave().getTransportControl().rew();
+			sonification.getSequenceMapping(SoundType.PEAK_LEVEL).renderCurve(
+					track.getSoundWave().getDbWave().getSequence(),0.0f);			
+			track.getSoundWave().getTransportControl().play();
+		}
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/MainFrame.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,276 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.Action;
+import javax.swing.Box;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UIManager.LookAndFeelInfo;
+import javax.swing.UnsupportedLookAndFeelException;
+
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.gui.actions.Actions;
+
+
+public class MainFrame extends JFrame {
+	private static final long serialVersionUID = 1L;
+	private Actions actions;
+	private ArrangeWindow arrangeWindow;
+	private JToolBar transportBar;
+
+	public MainFrame(){	
+		super("Cross-Modal DAW Prototype");
+		/* makes the overwritten getContentPane() correct */
+		setContentPane(new JPanel());
+
+		/* try and set system look and feel */
+
+		try {
+			for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
+		        if ("Nimbus".equals(info.getName())) {
+		            UIManager.setLookAndFeel(info.getClassName());
+		            break;
+		        }
+		    }
+		} catch (Exception e) {
+			    // If Nimbus is not available, you can set the GUI to another look and feel.
+			try {
+				UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+				SwingUtilities.updateComponentTreeUI(this);
+			} catch (ClassNotFoundException|InstantiationException|IllegalAccessException|UnsupportedLookAndFeelException e1) {
+				// do nothing and stick with java look&feel
+			} 
+		}
+		
+		setDefaultCloseOperation(EXIT_ON_CLOSE); // FIXME make own close operation
+		
+		JPanel contentPane = (JPanel)getContentPane();
+		contentPane.setLayout(new BorderLayout());
+		
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+		int screenWidth = (int)screenSize.getWidth();
+		int screenHeight = (int)screenSize.getHeight();
+
+		setLocation(screenWidth / 16, screenHeight / 16);
+		contentPane.setPreferredSize(new Dimension(
+				screenWidth * 5 / 8, screenHeight * 5 / 8));
+		
+		arrangeWindow = new ArrangeWindow();
+		transportBar = new JToolBar(){
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected JButton createActionComponent(Action a){
+				/* add the accessible name to the button using the action name */
+				JButton b = super.createActionComponent(a);
+				
+				
+				b.getAccessibleContext().setAccessibleName((String)a.getValue(Action.NAME));
+				return b;
+			}
+		};
+		contentPane.add(transportBar,BorderLayout.NORTH);
+		/* arrange window scroll contains the tracks and the Track parameters. The * 
+		 * scroll bar appears when the number of tracks exceeds the screen size  */
+		JScrollPane arrangeWindowScroll = new JScrollPane(arrangeWindow);
+		arrangeWindowScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+		arrangeWindowScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+		contentPane.add(arrangeWindowScroll,BorderLayout.CENTER);
+
+		
+		/* remove actions for scrolling left and right: the content of this scroll pane only * 
+		 * grows in height when track are added. Moreover this actions conflict with the     *
+		 * scrubbing through the audio track with right and left arrow keys                  */
+		InputMap im = arrangeWindowScroll.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);  
+		im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"none");
+		im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),"none");
+		
+		
+		getArrangeWindow().addTrack(new AudioTrack(Daw.getSoundEngineFactory().createSoundWave()));
+		
+		actions = new Actions(this);
+		initMenu(actions);
+		initTransportBar(actions);
+		initScrubActions();
+	}
+	
+	protected void initMenu(Actions actions){
+		JMenuBar menuBar = new JMenuBar();
+		this.setJMenuBar(menuBar);
+		
+		/* -- File Menu -- */
+		JMenu fileMenu = new JMenu(actions.getVoidAction("file"));
+		menuBar.add(fileMenu);
+		
+		fileMenu.add( new JMenuItem(actions.open()));
+		fileMenu.add(new JMenuItem(actions.close()));
+		fileMenu.addSeparator();
+		
+		fileMenu.add(new JMenuItem(actions.properties()));
+		
+		fileMenu.addSeparator();
+		fileMenu.add(new JMenuItem(actions.exit()));
+		
+		
+		/* -- Edit Menu -- */ 
+		JMenu editMenu = new JMenu(actions.getVoidAction("edit"));
+		menuBar.add(editMenu);
+		
+		editMenu.add(new JMenuItem(actions.cut()));
+		editMenu.add(new JMenuItem(actions.copy()));
+		editMenu.add(new JMenuItem(actions.paste()));
+		editMenu.add(new JMenuItem(actions.insert()));
+		
+		/* -- View Menu -- */
+		JMenu viewMenu = new JMenu(actions.getVoidAction("view"));
+		menuBar.add(viewMenu);
+		
+		viewMenu.add(new JMenuItem(actions.zoomIn()));
+		viewMenu.add(new JMenuItem(actions.zoomOut()));
+		viewMenu.add(new JMenuItem(actions.showDb()));
+		viewMenu.add(new JMenuItem(actions.generatePeaks()));
+		viewMenu.add(new JMenuItem(actions.findNextPoint()));
+		viewMenu.add(new JMenuItem(actions.findPreviousPoint()));
+		
+		/* -- Track Menu --*/
+		JMenu trackMenu = new JMenu(actions.getVoidAction("track"));
+		menuBar.add(trackMenu);
+		
+		trackMenu.add(new JMenuItem(actions.addTrack()));
+		trackMenu.add(new JMenuItem(actions.removeSelection()));
+		trackMenu.add(new JMenuItem(actions.switchAutomation()));
+		
+		/* -- Haptics Menu -- */
+		JMenu hapticsMenu = new JMenu(actions.getVoidAction("haptics"));
+		menuBar.add(hapticsMenu);
+		
+		hapticsMenu.add(new JMenuItem(actions.startPhantom()));
+		hapticsMenu.add(new JMenuItem(actions.showHaptics()));
+		hapticsMenu.add(new JMenuItem(actions.toggleScrub()));
+	}
+	
+	protected void initTransportBar(Actions actions){
+		/* removes space as a trigger for JButtons when they are focused * 
+		 * this way space can be used to play/stop the current track     */
+
+		/* action for play/stop the current track */
+		JComponent contentPane = (JComponent)getContentPane(); 
+		contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_DOWN_MASK), "play");
+		contentPane.getActionMap().put("play", actions.play());
+		contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, InputEvent.CTRL_DOWN_MASK), "rew");
+		contentPane.getActionMap().put("rew", actions.rew());
+		
+		
+		/* remove left and right arrow key from transport bar in order not to conflict with the cursor scrubbing  */
+		transportBar.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "none");
+		transportBar.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "none");
+		transportBar.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "none");
+		transportBar.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "none");
+		
+		/* set up transport bar buttons */
+		JToggleButton playButton = new JToggleButton(actions.play());
+		playButton.setHideActionText(true);
+		playButton.getAccessibleContext().setAccessibleName((String)playButton.getAction().getValue(Action.NAME));
+		/* add(Action a) creates a button with the specified action. it is overwritten *  
+		 * in order to add a name to the accessible context - see constructor          */
+		transportBar.add(playButton);
+		transportBar.add(actions.stop());
+		transportBar.add(actions.rew());
+		transportBar.add(actions.forward());
+		
+		transportBar.add(Box.createRigidArea(new Dimension(20,0)));
+		JToggleButton loopButton = new JToggleButton(actions.loop());
+		loopButton.setHideActionText(true);
+		loopButton.getAccessibleContext().setAccessibleName((String)loopButton.getAction().getValue(Action.NAME));
+		
+		transportBar.add(loopButton);
+	}
+	
+	protected void initScrubActions(){
+		JComponent contentPane = (JComponent)getContentPane(); 
+		
+		/* install the keystroke for left and right keys which will be mapperd respectively to scrub left and right */
+		contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0), "scrub forw");
+		contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0), "scrub backw");
+		contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F4,0), "scrub play");
+		contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F4,0,true), "scrub release");
+		
+
+		/* install the keystrokes for stopping the scrub all the modifiers are installed as well to prevent           *
+		 * a sequence of actions such as : [press right, press ctrl, release right, release ctrl] to leave the        *
+		 * scrubbing on. This would happen because press right will have no modifiers, whereas release right          *
+		 * would have ctrl modifier and it would not match a simple KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0,true)  */
+		for(int modifier : new int []{
+				0, // no modifiers
+				InputEvent.SHIFT_DOWN_MASK,
+				InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK, 
+			    InputEvent.META_DOWN_MASK,
+			    InputEvent.ALT_DOWN_MASK,
+			    InputEvent.ALT_GRAPH_DOWN_MASK,
+			    InputEvent.CTRL_DOWN_MASK}){
+			contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,modifier,true), "scrub release");			
+			contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,modifier,true), "scrub release");
+		}		
+
+		/* install the actions: scrub left - scrub right - stop scrub release - stop scrub right */
+		contentPane.getActionMap().put("scrub backw",actions.scrubAction(Actions.ScrubType.SCRUB_BACKWARDS));
+		contentPane.getActionMap().put("scrub forw",actions.scrubAction(Actions.ScrubType.SCRUB_FORWARDS));
+		contentPane.getActionMap().put("scrub release" ,actions.scrubAction(Actions.ScrubType.STOP_SCRUB));
+		contentPane.getActionMap().put("scrub play" ,actions.scrubAction(Actions.ScrubType.PLAY_SCRUB));
+		
+	}
+	
+	@Override
+	public JPanel getContentPane(){
+		return (JPanel)super.getContentPane();
+	}
+	
+	public ArrangeWindow getArrangeWindow(){
+		return arrangeWindow;
+	}
+	
+	public JToolBar getTransportBar(){
+		return transportBar;
+	}
+	
+	public Actions getActions(){
+		return actions;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/PreferencesDialog.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,96 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+
+
+public class PreferencesDialog extends JDialog {
+	private static final long serialVersionUID = 1L;
+	private final JTabbedPane content = new JTabbedPane();
+	private ArrayList<PreferencesPanel> panels;
+	
+	private ActionListener actionListener = new ActionListener(){
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			if("OK".equals(evt.getActionCommand())){
+				savePrefs();
+				dispose();
+			}else if("Cancel".equals(evt.getActionCommand())){
+				dispose();
+			}
+		}
+	};
+
+	/**
+	 * Create the dialog.
+	 */
+	public PreferencesDialog(Frame owner) {
+		super(owner,"Preferences",true);
+		panels = new ArrayList<>();
+		
+		setBounds(100, 100, 387, 269);
+		getContentPane().setLayout(new BorderLayout());
+		//content.setBorder(new EmptyBorder(5, 5, 5, 5));
+		getContentPane().add(content, BorderLayout.CENTER);
+
+		{
+			JPanel buttonPane = new JPanel();
+			buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
+			buttonPane.setBorder(BorderFactory.createMatteBorder(1,0,0,0,Color.GRAY));
+			getContentPane().add(buttonPane, BorderLayout.SOUTH);
+			{
+				JButton okButton = new JButton("OK");
+				okButton.setActionCommand("OK");
+				okButton.addActionListener(actionListener);
+				buttonPane.add(okButton);
+				getRootPane().setDefaultButton(okButton);
+			}
+			{
+				JButton cancelButton = new JButton("Cancel");
+				cancelButton.setActionCommand("Cancel");
+				cancelButton.addActionListener(actionListener);
+				buttonPane.add(cancelButton);
+			}
+		}
+	}
+	
+	private void savePrefs(){
+		for(PreferencesPanel p : panels) 
+			p.savePrefs();
+		}
+	
+	public void addPanel(PreferencesPanel p){
+		panels.add(p);
+		content.add(p,p.getTitle());
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/PreferencesPanel.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,32 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import javax.swing.JPanel;
+
+public abstract class PreferencesPanel extends JPanel {
+	private static final long serialVersionUID = 1L;
+
+	public abstract void savePrefs();
+	
+	public abstract String getTitle();
+	
+	public abstract String [] getPrefsList();
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/Rule.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,313 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Line2D;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DecimalFormat;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.event.MouseInputAdapter;
+
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+
+
+public class Rule extends JComponent implements PropertyChangeListener , Accessible {
+	private static final long serialVersionUID = 1L;
+	public static final int DEFAULT_TICK_INTERVAL = 50;
+	public static final int TICK_HEIGHT = 10;
+	public static final int HEIGHT = 40;
+	public static final int CURSOR_HEIGHT = 10;
+	public static final int CURSOR_WIDTH = 20;
+	public static final Color LOOP_MARKERS_COLOR = new Color(10,100,30);
+	public static final Color CURSOR_COLOR = AudioTrack.WAVE_COLOR;
+	private static final DecimalFormat TICK_FORMAT = new DecimalFormat("###.#");
+	
+	private AudioTrack track;
+	private int scaleFactor;
+	private Polygon cursor;
+	private Polygon loopStartMark;
+	private Polygon loopEndMark;
+	
+
+	public Rule(){
+		Dimension size = new Dimension(AudioTrack.MAX_TRACK_WIDTH,HEIGHT);
+		setMaximumSize(size);
+		
+		setPreferredSize(new Dimension(0,HEIGHT));
+		setBorder(BorderFactory.createMatteBorder(0,0,1,0,Color.black));
+		
+		/* a triangle initially centred on 0*/
+		cursor = new Polygon(
+					new int[]{0,-CURSOR_WIDTH/2,CURSOR_WIDTH/2}, 
+					new int[] {HEIGHT,HEIGHT-CURSOR_HEIGHT,HEIGHT-CURSOR_HEIGHT}, 
+					3);
+		loopStartMark = new Polygon();
+		loopEndMark = new Polygon();
+
+		MouseInteraction mouseInteraction = new MouseInteraction();
+		addMouseListener(mouseInteraction);
+		addMouseMotionListener(mouseInteraction);
+	}
+	
+	@Override
+	public void paintComponent(Graphics g){
+		Graphics2D g2 = (Graphics2D)g;
+		Color oldColor = g2.getColor();
+		
+		g2.setColor(getBackground());
+        g2.fillRect(0, 0, getWidth(), getHeight());
+        g2.setColor(getForeground());
+		
+		Line2D line = new Line2D.Float();
+		int height = getHeight();
+		/* draw rule's ticks */
+		
+		if(track.getSecondsPerPixel() == 0){
+			for(int i=DEFAULT_TICK_INTERVAL; i< getSize().width; i += DEFAULT_TICK_INTERVAL ){
+				line.setLine(i, height, i, height-TICK_HEIGHT);
+				g2.draw(line);
+			}
+		}else{
+			int tickInterval = (int) ( (1/track.getSecondsPerPixel() / 2 ));
+			for(int i=tickInterval, j=1; i< getSize().width; i += tickInterval, j++ ){
+				if(j % 2 == 1){
+					line.setLine(i, height, i, height-TICK_HEIGHT/3);
+					g2.draw(line);
+				}else{
+					line.setLine(i, height, i, height-TICK_HEIGHT);
+					g2.draw(line);
+					String tick = TICK_FORMAT.format(j/2);//track.getSecondsPerPixel()*i);
+					int stringLen = (int)g2.getFontMetrics().getStringBounds(tick, g2).getWidth(); 
+					g2.drawString(tick, i-(stringLen/2), height-TICK_HEIGHT-3);
+				}
+			}
+		}
+		
+		
+		/* draw the cursor in WAVE_COLOR */
+		g2.setColor(CURSOR_COLOR);
+		g2.fill(cursor);
+		
+		/* draw the loop markers, if any */
+		g2.setColor(LOOP_MARKERS_COLOR);
+		g2.fill(loopStartMark);
+		g2.fill(loopEndMark);
+		if(loopEndMark.npoints != 0){
+			g2.drawLine(loopStartMark.xpoints[0], 
+					HEIGHT - (loopStartMark.getBounds().height/2), 
+					loopEndMark.xpoints[0]-1,// with -1 it's drawn it better  
+					HEIGHT - (loopEndMark.getBounds().height/2)
+				);
+		}
+		
+		g2.setColor(oldColor);
+	}
+	
+	public void setAudioTrack(AudioTrack t){
+		/* removes itself as a listener to the previous track, if any */
+		if(track != null){
+			track.removePropertyChangeListener(this);
+		}
+		/* adds itself as a listener to the new track */
+		if(t != null){
+			t.addPropertyChangeListener(this);
+			scaleFactor = t.getScaleFactor();
+		}else {
+			scaleFactor = 1;
+		}
+		track = t;
+	}
+	
+	public int getCursorPos() {
+		Rectangle bounds = cursor.getBounds();
+		return bounds.x + bounds.width/2;
+	}
+
+	public void setCursorPos(int position) {
+		Rectangle bounds = cursor.getBounds();
+		int currentCenterX = bounds.x + bounds.width/2;
+		cursor.translate(position-currentCenterX, 0);
+	}
+
+	@Override
+	public void propertyChange(PropertyChangeEvent evt) {
+		switch(evt.getPropertyName()){
+			case "cursorPos" : {
+				setCursorPos((Integer)evt.getNewValue());
+				repaint();
+				break;
+			}
+			case "scaleFactor" : {
+				scaleFactor = (Integer)evt.getNewValue();
+				repaint();
+				break;
+			}
+			case "mouseDragSelection" : {
+				Selection selection = (Selection)evt.getNewValue();
+				if(selection.isOpen() ){ // no loop
+					loopStartMark.reset();
+					loopEndMark.reset();
+				}else{
+					loopStartMark.reset();
+					/* first point is the actual marker */
+					loopStartMark.addPoint(selection.getStart(), HEIGHT-(CURSOR_HEIGHT));
+					loopStartMark.addPoint(selection.getStart()+(CURSOR_WIDTH/2), HEIGHT-(CURSOR_HEIGHT*2));
+					loopStartMark.addPoint(selection.getStart()+(CURSOR_WIDTH/2),HEIGHT);
+					
+					loopEndMark.reset();
+					/* first point is the actual marker */
+					loopEndMark.addPoint(selection.getEnd(),HEIGHT-(CURSOR_HEIGHT));
+					loopEndMark.addPoint(selection.getEnd()-(CURSOR_WIDTH/2),HEIGHT);
+					loopEndMark.addPoint(selection.getEnd()-(CURSOR_WIDTH/2),HEIGHT-(CURSOR_HEIGHT*2));
+				}
+				repaint();
+				break;
+			}
+			case "preferredSize" : {
+				/* resize the width acording to the new value of the listened component */
+				Dimension d = (Dimension)evt.getNewValue();
+				setPreferredSize(new Dimension(d.width,HEIGHT));
+				break;
+			}
+		}
+	}
+	
+	@Override
+	public AccessibleContext getAccessibleContext(){
+		if(accessibleContext == null){
+			accessibleContext = new AccessibleRule();
+		}
+		return accessibleContext;
+	}
+	
+	private class MouseInteraction extends MouseInputAdapter {
+		private Polygon clickedMark; // either loopStartMark or loopEndMark
+		int clickOffset;
+		
+		@Override
+		public void mousePressed(MouseEvent evt){
+			if(loopStartMark.contains(evt.getX(),evt.getY())){ // click on the loop start mark 
+				clickedMark = loopStartMark;
+				clickOffset = evt.getX() - loopStartMark.xpoints[0];
+			}else if(loopEndMark.contains(evt.getX(),evt.getY())){ // click on the loop end mark
+				clickedMark = loopEndMark;
+				clickOffset = evt.getX() - loopEndMark.xpoints[0];
+			}else if(cursor.contains(evt.getX(),evt.getY())){
+				clickedMark = cursor;
+				clickOffset = evt.getX() - cursor.xpoints[0];
+			}
+		}
+		
+		@Override
+		public void	mouseDragged(MouseEvent evt){
+			/* check again that clickedMark is != null and not reset         *
+			 * things might change in the middle of the mouse interaction    *
+			 * if the property change listener is called from another source */
+			if(clickedMark == null)
+				return;
+			
+			if(clickedMark != cursor && clickedMark.npoints > 0 && track != null){ // loop marks
+				int mouseSelectionX = 0;
+				int mouseSelectionY = 0;
+				if(clickedMark == loopStartMark){ // dragging the start mark 
+					if(evt.getX() > loopEndMark.xpoints[0] - 1 || (evt.getX()-clickOffset < 0) )
+						return; // can't drag start mark too right, past the end mark or too left past the screen
+					mouseSelectionX = evt.getX()-clickOffset;
+					mouseSelectionY = loopEndMark.xpoints[0];
+				}else{ // dragging the end mark 
+					if(evt.getX() < loopStartMark.xpoints[0] + 1)
+						return; // can't drag end mark too left, past the end mark
+					mouseSelectionX = loopStartMark.xpoints[0];
+					mouseSelectionY = evt.getX()-clickOffset;
+				}
+				scrollRectToVisible(new Rectangle(evt.getX(), evt.getY(), 1, 1));
+				track.trackInteraction.setMouseSelection(mouseSelectionX, mouseSelectionY);
+			} else if (clickedMark == cursor){  // cursor mark
+				track.getSoundWave().scan(evt.getX());
+			}
+		}
+		
+		@Override
+		public void mouseReleased(MouseEvent evt){
+			if(clickedMark != null){
+				if(clickedMark == cursor){
+					track.getSoundWave().scan(SoundWave.STOP_SCANNING); // stop scrubbing 
+				}else
+					track.getSoundWave().setSelection(track.trackInteraction.getMouseSelection());
+			}
+			clickedMark = null;
+			clickOffset = 0;
+		}
+		
+		@Override
+		public void mouseMoved(MouseEvent evt){
+			if(cursor.contains(evt.getPoint())){
+				setCursor(new Cursor(Cursor.E_RESIZE_CURSOR));
+			}else if(loopStartMark.contains(evt.getPoint())){
+				setCursor(new Cursor(Cursor.E_RESIZE_CURSOR));
+			}else if(loopEndMark.contains(evt.getPoint())){
+				setCursor(new Cursor(Cursor.W_RESIZE_CURSOR));
+			}else{
+				setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+			}
+		}
+	}
+	
+	protected class AccessibleRule extends AccessibleJComponent {
+		private static final long serialVersionUID = 1L;
+		
+		protected AccessibleRule(){
+			this.setAccessibleName("Time Bar");
+			this.setAccessibleDescription( "Measures he time of tracks");
+		}
+		
+		@Override
+		public AccessibleRole getAccessibleRole(){
+			return AccessibleRole.RULER;
+		}
+		
+		@Override
+		public AccessibleStateSet getAccessibleStateSet(){
+			AccessibleStateSet states = super.getAccessibleStateSet();
+			
+			states.add(AccessibleState.HORIZONTAL);
+			states.add(AccessibleState.VISIBLE);
+			
+			return states;
+		}
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/SequenceGraph.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,262 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.Line2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.SequenceEvent;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+public class SequenceGraph implements SequenceMapping, SequenceListener {
+	private static final float SEQUENCE_LINE_WIDTH = 4.0f;
+	private static final int   SEQUENCE_POINTS_CAPACITY = 30;
+	
+	private Sequence sequence;
+	private Color color;
+	private  List<SequencePoint>  sequencePoints;
+	private  List<Shape> sequenceLines;
+	private Dimension size;
+	private Collection<ChangeListener> listeners;
+	private float millisecPerPixel;
+	
+	public SequenceGraph(Color color, Dimension size) {
+		sequencePoints = new ArrayList<>(SEQUENCE_POINTS_CAPACITY);
+		sequenceLines = new ArrayList<>(SEQUENCE_POINTS_CAPACITY-1);
+		listeners = new ArrayList<>();
+		this.color = color;
+		this.size = size;
+	}
+	
+	public Dimension getSize(){
+		return size;
+	}
+	
+	public void setSize(Dimension size){
+		this.size = size;
+	}
+	
+	public void setColor(Color c){
+		this.color = c;
+	}
+	
+	public float getMillisecondsPerPixel(){
+		return millisecPerPixel;
+	}
+	
+	public void setMillisecPerPixel(float millisecPerPixel){
+		this.millisecPerPixel = millisecPerPixel;
+	}
+	
+	public Color getColor(){
+		return color; 
+	}
+	
+	public List<SequencePoint> getSequencePoints(){
+		return sequencePoints;
+	}
+	
+	public List<Shape> getSequenceLines(){
+		return sequenceLines;
+	}
+
+	public void draw(Graphics g ){
+		if(sequenceLines.isEmpty()){
+			return;
+		}
+		
+		Graphics2D g2 = (Graphics2D)g;
+		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		Color oldColor = g2.getColor();
+		
+		g2.setColor(color);
+		
+		/* draw sequence points and lines */
+		for(Shape point : sequencePoints){
+			g2.fill(point);
+		}
+				
+		for(Shape line : sequenceLines){
+			g2.fill(line);
+		}
+		/* restores previous state */
+		g2.setColor(oldColor);
+		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+	}
+
+	public void addChangeListener(ChangeListener l){
+		listeners.add(l);
+	}
+	
+	public void removeChangeListener(ChangeListener l){
+		listeners.remove(l);
+	}
+	
+	protected void fireChangeListeners(){
+		ChangeEvent evt = new ChangeEvent(this);
+		for(ChangeListener l : listeners){
+			l.stateChanged(evt);
+		}
+	}
+	
+	@Override 
+	public <T extends SequenceEvent> void sequenceUpdated(T evt) {
+		updateSequence();
+	}
+	
+	/**
+	 * 
+	 * Sets a new sequence to be listened to. If the argument is {@code null} 
+	 * it stops listening to the previously set sequence, if any,m and clears all 
+	 * sequence points and lines. it calls {@code updateSequence}.
+	 * 
+	 * @param s the new sequence. 
+	 */
+	public void setSequence(Sequence s){
+		/* remove this from the old sequence if any and add this to the new sequence */
+		if(sequence != null){
+			sequence.removeSequenceListener(this);
+		}
+		
+		if(s != null){
+			s.addSequenceListener(this);
+		}
+		
+		sequence = s;
+		updateSequence();
+	}
+	
+	public Sequence getSequence(){
+		return sequence;
+	}
+	
+	/** 
+	 * Recompute the sequence points and lines. It should be called whenever a change is done to this 
+	 * object. For example after {@code setSize} and {@code setMilliseconsPerPixel}. 
+	 * 
+	 */
+	public void updateSequence(){
+		/* clear previous values. will rebuild them if type is not NONE */
+		sequencePoints.clear();
+		sequenceLines.clear();
+		
+		if(sequence == null){
+			/* no sequence represented any more. Fire listeners with * 
+			 * sequencePoints and sequenceLines cleared              */
+			fireChangeListeners();
+			return;
+		}
+		
+		/* height and width of this panel in order to draw proportionately */
+		int height = getSize().height;
+		int width = getSize().width;
+		
+		/* get the sequence range */
+		MathUtils.Scale scale = new MathUtils.Scale(sequence.getRange(),new Range<Float>(0.0f,(float)height));
+		BasicStroke stroke = new BasicStroke(SEQUENCE_LINE_WIDTH);
+
+		/* if there are no sequence values, draw a straight line from the beginning to the end of the track */
+		if(sequence.getValuesNum() == 0){
+			sequenceLines.add(stroke.createStrokedShape(
+					new Line2D.Float(
+							0f,
+							height - scale.linear(sequence.getBegin()),/* height is reversed as in swing the top is 0 */
+							width, 
+							height - scale.linear(sequence.getEnd()))/* height is reversed as in swing the top is 0 */
+					));
+		}else{
+			Sequence.Value previous = null;
+			/*previousX and previousY are the centre of sequence values calculated in the previous cycle */
+			int previousX = 0;
+			int previousY = 0;
+			
+			for(int i=0; i<sequence.getValuesNum();i++){
+				Sequence.Value current = sequence.getValueAt(i);
+				int currentX = (int)(current.getTimePosition()/millisecPerPixel) - (SequencePoint.SIZE/2) ;
+				/* height is reversed as in swing the top is 0 */
+				int currentY = height - (int)(scale.linear(current.getValue())) - (SequencePoint.SIZE/2) ;
+				/* add the point */
+				sequencePoints.add(new SequencePoint(current,currentX,currentY));
+				if(previous == null){
+					/* add the line from the beginning to the first point */
+					sequenceLines.add(stroke.createStrokedShape(
+							new Line2D.Float(
+									0f,
+									height - scale.linear(sequence.getBegin()), /* height is reversed as in swing the top is 0 */
+									currentX + (SequencePoint.SIZE/2), 
+									currentY + (SequencePoint.SIZE/2) )));
+				}else{
+					/* add the line with previous point */
+					sequenceLines.add(stroke.createStrokedShape(
+							new Line2D.Float(
+									previousX + (SequencePoint.SIZE/2),
+									previousY + (SequencePoint.SIZE/2),
+									currentX + (SequencePoint.SIZE/2), 
+									currentY + (SequencePoint.SIZE/2) )));
+				}
+				/* prepare for next cycle */
+				previous = current;
+				previousX = currentX;
+				previousY = currentY;
+			}
+			
+			/* add the line from the last point to the end */
+			sequenceLines.add(stroke.createStrokedShape(
+					new Line2D.Float(
+							previousX + (SequencePoint.SIZE/2),
+							previousY + (SequencePoint.SIZE/2),
+							width, 
+							height - scale.linear(sequence.getEnd()) ))); /* height is reversed as in swing the top is 0 */
+		}
+		
+		fireChangeListeners();
+	}
+
+	@Override
+	public void renderValue(Sequence.Value val) {
+		throw new UnsupportedOperationException("Only update() implemented");
+	}
+
+	@Override
+	public void renderCurve(Sequence m, float startTime) {
+		throw new UnsupportedOperationException("Only update() implemented");
+	}
+
+	@Override
+	public void renderCurveAt(Sequence m, float time, float duration) {
+		throw new UnsupportedOperationException("Only update() implemented");
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/SequencePoint.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,56 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui;
+
+import java.awt.geom.Ellipse2D;
+
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+public class SequencePoint extends Ellipse2D.Float {
+	private static final long serialVersionUID = 1L;
+	public final static int SIZE = 9;
+	private Sequence.Value seqValue;
+	
+	
+	
+	public SequencePoint(Sequence.Value sequenceValue, float x, float y) {
+		super(x, y, SIZE, SIZE);
+		this.seqValue = sequenceValue;
+	}
+
+	public Sequence.Value getSequenceValue(){
+		return seqValue;
+	}
+	
+	@Override
+	public String toString(){
+		return "Sequence point ["+super.x+","+super.y+"]";
+		
+	}
+	
+	public boolean isXCentredAt(int p){
+		return MathUtils.equal(p, x+SIZE/2, 0.2f);
+	}
+	
+	public boolean isYCentredAt(int p){
+		return MathUtils.equal(p, y+SIZE/2, 0.2f);
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/actions/Actions.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,951 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui.actions;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.InputStream;
+import java.util.ResourceBundle;
+import java.util.concurrent.ExecutionException;
+import java.util.prefs.Preferences;
+
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.swing.Action;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.ProgressMonitor;
+import javax.swing.SwingWorker;
+import javax.swing.filechooser.FileNameExtensionFilter;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Direction;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.BeadsSequenceMapping;
+import uk.ac.qmul.eecs.depic.daw.beads.sonification.SonificationPrefsPanel;
+import uk.ac.qmul.eecs.depic.daw.gui.ArrangeWindow;
+import uk.ac.qmul.eecs.depic.daw.gui.AudioTrack;
+import uk.ac.qmul.eecs.depic.daw.gui.AudioTrackParameters;
+import uk.ac.qmul.eecs.depic.daw.gui.MainFrame;
+import uk.ac.qmul.eecs.depic.daw.gui.PreferencesDialog;
+import uk.ac.qmul.eecs.depic.daw.haptics.DawHapticListener;
+import uk.ac.qmul.eecs.depic.daw.haptics.HapticTrigger;
+import uk.ac.qmul.eecs.depic.daw.haptics.HapticTrigger.Command;
+import uk.ac.qmul.eecs.depic.daw.haptics.HaptificationPrefsPanel;
+import uk.ac.qmul.eecs.depic.daw.referencesound.GridSound;
+import uk.ac.qmul.eecs.depic.jhapticgui.HapticDevice;
+import uk.ac.qmul.eecs.depic.jhapticgui.HapticDeviceFactory;
+import uk.ac.qmul.eecs.depic.jhapticgui.HapticDeviceWinFactory;
+import uk.ac.qmul.eecs.depic.jhapticgui.HapticListener;
+import uk.ac.qmul.eecs.depic.jhapticgui.Haptics;
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+
+public class Actions  {
+	public enum ScrubType {
+		STOP_SCRUB,
+		SCRUB_FORWARDS,
+		SCRUB_BACKWARDS,
+		PLAY_SCRUB
+	};
+	
+	
+	private MainFrame frame;
+	private String currentDirectory;
+	private Haptics haptics;
+	private Sonification sonification;
+	
+	private EditActions editActions;
+	private TransportControlActions transportCtrlActions;
+	
+	private Action openAction;
+	private Action closeAction;
+	private Action exitAction;
+	private Action preferencesAction;
+	private Action zoomInAction;
+	private Action zoomOutAction;
+	private Action addTrackAction;
+	private Action removeSelection;
+	private Action switchAutomation;
+	
+	private Action startPhantomAction;
+	private ShowHapticsAction showHapticsAction;
+	private PauseHapticsAction pauseHapticsAction;
+	private Action rotateHapticsYAction;
+	private Action rotateHapticsZAction;
+	private Action scrubForwardsAction;
+	private Action scrubBackwardsAction;
+	private Action stopScrubAction;
+	private Action playScrubAction;
+	private Action generatePeaksAction;
+	private Action showDbAction;
+	private Action findNextPointAction;
+	private Action findPreviousPointAction;
+	private Action toggleScrubAction;
+	private int currentFindPosition;
+	
+	public Actions(MainFrame f){
+		frame = f;
+		sonification = Daw.getSoundEngineFactory().getSharedSonification();
+		
+		editActions = new EditActions(f);
+		transportCtrlActions = new TransportControlActions(f);
+		
+		openAction = new OpenAction();
+		closeAction = new CloseAction();
+		preferencesAction = new PreferencesAction();
+		exitAction = new ExitAction();
+		zoomInAction = new ZoomAction("view.zoom_in",-1);
+		zoomOutAction = new ZoomAction("view.zoom_out",1);
+		addTrackAction = new AddTrackAction();
+		removeSelection = new RemoveSelectionAction();
+		switchAutomation = new SwitchAutomationAction();
+		
+		startPhantomAction = new StartHapticDeviceAction();
+		
+		showHapticsAction = new ShowHapticsAction();
+		pauseHapticsAction = new PauseHapticsAction();
+		rotateHapticsYAction = new RotateHapticsAction("y");
+		rotateHapticsZAction = new RotateHapticsAction("z");
+		
+		scrubForwardsAction = new ScrubAction("track.scrub_forwards",ScrubAction.DEFAULT_DELTA);
+		scrubBackwardsAction = new ScrubAction("track.scrub_backwards",-ScrubAction.DEFAULT_DELTA);
+		stopScrubAction = new ScrubAction("track.stop_scrub",null);
+		playScrubAction = new ScrubAction("track.stop_scrub",0);
+		
+		toggleScrubAction = new ToggleScrubAction();
+		
+		generatePeaksAction = new GeneratePeaksAction();
+		showDbAction = new ShowDbViewAction();
+		
+		findNextPointAction = new FindNextPointAction(Direction.RIGHT);
+		findPreviousPointAction = new FindNextPointAction(Direction.LEFT);
+		currentFindPosition = -1;
+	}
+	
+	public Action getVoidAction(String resource){
+		return new MenuAction(resource){
+			private static final long serialVersionUID = 1L;
+			@Override
+			public void actionPerformed(ActionEvent evt) {	}
+		};
+	}
+	
+	public Action open(){
+		return openAction;
+	}
+	
+	public Action close(){
+		return closeAction;
+	}
+	
+	public Action properties(){
+		return preferencesAction;
+	}
+	
+	public Action exit(){
+		return exitAction;
+	}
+	
+	public Action zoomIn(){
+		return zoomInAction;
+	}
+	
+	public Action zoomOut(){
+		return zoomOutAction;
+	}
+	
+	public Action showDb(){
+		return showDbAction;
+	}
+	
+	public Action findNextPoint(){
+		return findNextPointAction;
+	}
+	
+	public Action findPreviousPoint(){
+		return findPreviousPointAction;
+	}
+	
+	public Action generatePeaks(){
+		return generatePeaksAction;
+	}
+
+	public Action cut(){
+		return editActions.cutAction;
+	}
+	
+	public Action paste(){
+		return editActions.pasteAction;
+	}
+	
+	public Action copy(){
+		return editActions.copyAction;
+	}
+	
+	public Action insert(){
+		return editActions.insertAction;
+	}
+	
+	public Action play(){
+		return transportCtrlActions.playAction;
+	}
+	
+	/**
+	 * 
+	 * The loop can be set on and of setting the {@code Action.SELECTED_KEY}, property
+	 * 
+	 * @return
+	 */
+	public Action loop(){
+		return transportCtrlActions.loopAction;
+	}
+	
+	public Action stop(){
+		return transportCtrlActions.stopAction;
+	}
+	
+	public Action rew(){
+		return transportCtrlActions.rewAction;
+	}
+	
+	public Action forward(){
+		return transportCtrlActions.fwdAction;
+	}
+	
+	
+	public Action addTrack(){
+		return addTrackAction;
+	}
+	
+	public Action removeSelection(){
+		return removeSelection;
+	}
+	
+	public Action switchAutomation(){
+		return switchAutomation;
+	}
+	
+	public Action startPhantom(){
+		return startPhantomAction;
+	}
+	
+	public Action showHaptics(){
+		return showHapticsAction;
+	}
+	
+	public Action pauseHaptics(){
+		return pauseHapticsAction;
+	}
+	
+	public Action rotateHaptics(boolean y){
+		if(y)
+			return rotateHapticsYAction;
+		else
+			return rotateHapticsZAction;
+	}
+	
+	/**
+	 * Returns an action for scrubbing through the current sound wave 
+	 * 
+	 * @param determines the action effect: forwards scrub forwards if {@code true}, backwards id {@code false} and stop scrubbing 
+	 * if {@code null}
+	 * 
+	 * @return the {@code Action} instance for scrubbing through the current sound wave. Multiple calls 
+	 * to this methods with the same value of {@code forwards} would return the same object.
+	 */
+	public Action scrubAction(ScrubType type){
+		switch(type) {
+		case STOP_SCRUB : 
+			return stopScrubAction;
+		case SCRUB_FORWARDS :
+			return scrubForwardsAction;
+		case SCRUB_BACKWARDS :
+			return scrubBackwardsAction;			
+		case PLAY_SCRUB : 
+			return playScrubAction;
+		default :
+			throw new IllegalArgumentException("Wrong scrub action type " + type );
+		}
+	}
+	
+	
+	public Action toggleScrub(){
+		return toggleScrubAction;
+	}
+	
+	/* --- ACTION CLASSES --- */
+	private class OpenAction extends MenuAction implements  PropertyChangeListener {
+		private static final long serialVersionUID = 1L;
+		
+		private ProgressMonitor progressMonitor;
+
+		OpenAction() { 
+			super("file.open");
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+				if(track == null)
+					return;
+			
+			/* open a file chooser */
+			JFileChooser fileChooser = new JFileChooser(currentDirectory);
+			AudioFileFormat.Type[] fileTypes = AudioSystem.getAudioFileTypes();
+			String[] extensions = new String[fileTypes.length];
+			for(int i=0; i< fileTypes.length; i++){
+				extensions[i] = fileTypes[i].getExtension();
+			}
+			
+			/* filter the selection by audio file extensions only */
+			fileChooser.setFileFilter(new FileNameExtensionFilter("Audio Files",extensions));
+			
+			int retVal = fileChooser.showOpenDialog(frame);
+			if(retVal != JFileChooser.APPROVE_OPTION)
+				return;
+			File selectedFile = fileChooser.getSelectedFile();
+			/* saves the current directoy to re-open the file chooser next time */
+			currentDirectory = selectedFile.getParent();
+			progressMonitor = new ProgressMonitor(frame,"Loading File", "", 0, SoundWave.FILE_LOAD_TOTAL_PROGRESS); 
+			
+			track.getSoundWave().loadAudioData(selectedFile, this);
+		}
+
+		/**
+		 * Monitors the progress of file loading 
+		 */
+		@Override
+		public void propertyChange(PropertyChangeEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null)
+				return;
+				
+			if("progress".equals(evt.getPropertyName())){	
+				if(progressMonitor.isCanceled()){
+					track.getSoundWave().stopLoading(true);
+				}
+				else if((Integer)evt.getNewValue() == SoundWave.FILE_LOAD_TOTAL_PROGRESS){ // complete
+					closeAction.setEnabled(true); // now can be closed
+					progressMonitor.close();
+				}
+				else
+					progressMonitor.setProgress((Integer)evt.getNewValue());
+            }else if(SoundWave.FILE_ERROR_PROPERTY.equals(evt.getPropertyName())){
+            	//dialog.dispose();
+            	JOptionPane.showMessageDialog(frame, "Could not open the file: "+evt.getNewValue());            	
+            }
+		}
+	} // class OpenAction
+		
+	private class CloseAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+
+		CloseAction(){
+			super("file.close");
+			setEnabled(false);
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt){
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+				if(track != null)
+					track.getSoundWave().close();
+		}
+	} // class CloseAction
+	
+	private class PreferencesAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		PreferencesAction(){
+			super("file.properties");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			PreferencesDialog dialog = new PreferencesDialog(frame);
+			
+//			dialog.addPanel(new GranularPrefsPanel(frame.getArrangeWindow())); FIXME line to remove 
+			dialog.addPanel(SonificationPrefsPanel.getInstance());
+			dialog.addPanel(HaptificationPrefsPanel.getInstance());
+			
+			dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+			dialog.setVisible(true);
+		}	
+	} // class PropertiesAction
+	
+	private class ExitAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+
+		ExitAction() {
+			super("file.exit");
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			if(haptics != null)
+				haptics.dispose();
+			System.exit(0);
+		}	
+	} // class ExitAction
+
+	private class ZoomAction extends MenuAction{
+		private static final long serialVersionUID = 1L;
+		private int zoomFactor;
+
+		ZoomAction(String resource, int zoomFactor){
+			super(resource);
+			this.zoomFactor = zoomFactor;
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			for(int i=0; i<arrangeWindow.getTracksCount();i++){
+				AudioTrack track = arrangeWindow.getTrackAt(i);
+				if(track.getScaleFactor()+zoomFactor == 0 || 
+						track.getScaleFactor()+zoomFactor > track.getSoundWave().getMaxScaleFactor()){
+					Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR);
+					return;
+				}
+				track.setScaleFactor(track.getScaleFactor()+zoomFactor);
+			}
+			
+			
+		}
+	} // class ZoomAction
+	
+	
+	
+	private class AddTrackAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+
+		AddTrackAction(){
+			super("track.add_track");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			AudioTrack newTrack = new AudioTrack(Daw.getSoundEngineFactory().createSoundWave());
+			frame.getArrangeWindow().addTrack(newTrack);
+			newTrack.scrollRectToVisible(newTrack.getBounds());
+		}
+	}// class AddTrackAction
+	
+	private class SwitchAutomationAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		SwitchAutomationAction() {
+			super("track.switch_automation");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e){
+			int trackIndex = frame.getArrangeWindow().getCurrentTrackIndex();
+			if(trackIndex == -1)
+				return;
+			
+			resetFindIndex();
+			AudioTrackParameters parameters = frame.getArrangeWindow().getTrackParametersAt(trackIndex);
+			int itemCount = parameters.automationComboBox.getItemCount(); 
+			int selectedIndex = parameters.automationComboBox.getSelectedIndex();
+			parameters.automationComboBox.setSelectedIndex((selectedIndex+1)%itemCount);
+		}
+		
+	} // class SwitchAutomationAction
+	
+	private class StartHapticDeviceAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		private HapticDeviceFactory hapticBuilder;
+		private boolean hapticStarted;
+		
+		StartHapticDeviceAction(){
+			super("haptics.falcon_start");
+			hapticBuilder = new HapticDeviceWinFactory();
+			hapticStarted = false;
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e ){
+			if(! hapticStarted) {
+				new SwingWorker<Haptics,Void>(){
+					
+					@Override
+					protected Haptics doInBackground() throws Exception {
+						/* init the haptic device in a background thread */
+						HapticListener listener = new DawHapticListener(frame);
+						InputStream  PantomLib = DawHapticListener.class.getResourceAsStream("DawHaptics.dll");
+						//Haptics haptics = hapticBuilder.getHapticDevice(PantomLib, listener, frame.getSize());
+						Haptics haptics = hapticBuilder.getHapticDevice(PantomLib, listener, new Dimension(1470, 918));
+						return haptics;
+					}
+					
+					@Override
+					protected void done(){
+						/* init finished. If successful add the haptic device to the SoundWave listeners */
+						try {
+							haptics = get();
+							/* set the parent component of message dialogs */
+	
+							showHapticsAction.setEnabled(true);
+							toggleScrubAction.setEnabled(true);
+							putValue(MenuAction.NAME,
+									ResourceBundle.getBundle(MenuAction.class.getName()).getString("haptics.falcon_stop.text"));
+							hapticStarted = true;
+							Thread.sleep(2000);
+							frame.requestFocus();
+						} catch (InterruptedException | ExecutionException e) {
+							JOptionPane.showMessageDialog(frame, "failed to initialize haptic device ("+e.getMessage()+")");
+							e.printStackTrace();
+						}
+					}
+				}.execute();
+			}else{ // stop haptic device
+				haptics.dispose();
+				haptics = null;
+				putValue(MenuAction.NAME,ResourceBundle.getBundle(MenuAction.class.getName()).getString("haptics.falcon_start.text"));
+				hapticStarted = false;
+			}
+		}
+	} // class StartPhantomAction
+	
+	private class ShowHapticsAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		private UniqueHapticTrigger uniqueHapticTrigger;
+		
+		
+		public ShowHapticsAction() {
+			super("haptics.show");
+			
+			uniqueHapticTrigger = new UniqueHapticTrigger();
+			
+			
+			/* enabled when haptics is created */
+			setEnabled(false);
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			/* sets a new track to listen to for the haptics */
+			
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			AudioTrack track = arrangeWindow.getCurrentTrack();
+			
+			if(track == null){
+				haptics.sendMessage(HapticTrigger.Command.DISPLAY_NONE, "", 0);
+				Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.OK);
+				return;
+			}
+
+			/* set the track  the haptic commands will apply to */
+			((DawHapticListener)haptics.getListener()).setTrack(track);
+			if(haptics  instanceof HapticDevice){
+				
+				float hapticViewPortMs = track.getSoundWave().getMillisecPerChunk() * 
+						track.getHapticViewPort().getWidth();
+				if(track.getSoundWave().getDbWave().hasSequence()){
+					/* peaks view doesn't allow normal scrubbing */
+					uniqueHapticTrigger.set(new HapticTrigger((HapticDevice)haptics, HapticTrigger.DisplayType.DISPLAY_PEAKS));
+					uniqueHapticTrigger.addToListener(track.getSoundWave());
+
+					haptics.sendMessage(Command.DISPLAY_PEAKS, "", 0);
+					
+					/* the argument is a sequence of peak levels */ 
+					Sequence sequence = track.getSoundWave().getDbWave().getSequence();
+					
+					for(int i=0; i< sequence.getValuesNum(); i++){
+						Sequence.Value seqVal = sequence.getValueAt(i);
+						Preferences prefs = Preferences.userNodeForPackage(BeadsSequenceMapping.class); // FIXME remove beads dependency 
+						float borderThreshold =  prefs.getFloat("borders.threshold", 0.01f);
+						if( sequence.getRange().getEnd() - seqVal.getValue() <= borderThreshold){  
+//							if(seqVal.getValue() >= sequence.getRange().getEnd() - borderThreshold){	
+							haptics.sendMessage(Command.SEQUENCE_VALUE_ADD, 
+									Float.toString(seqVal.getTimePosition()/hapticViewPortMs),  
+									seqVal.hashCode());
+						}
+					}
+				}else if(track.getSoundWave().hasSequence()){ // Automation 
+					/* automation view allows scrubbing as well */
+					uniqueHapticTrigger.set(new HapticTrigger((HapticDevice)haptics, HapticTrigger.DisplayType.DISPLAY_SEQUENCE));
+					uniqueHapticTrigger.addToListener(track.getSoundWave());
+					
+					Sequence sequence = (Sequence)track.getSoundWave().getSequence();	
+					uniqueHapticTrigger.addToListener(sequence); 
+					
+					Preferences prefs = Preferences.userNodeForPackage(HaptificationPrefsPanel.class);
+					String gridType = prefs.get("grid_type", HaptificationPrefsPanel.GRID_TYPES[0]);
+					int id = -1;
+					for (int i=0; i<HaptificationPrefsPanel.GRID_TYPES.length; i++){
+						if(HaptificationPrefsPanel.GRID_TYPES[i].equals(gridType)){
+							id = i;
+							break;
+						}
+					}
+					
+					if(id == -1)
+						throw new RuntimeException();
+					
+					/* set the grid sound */
+					String soundType = prefs.get("sound_type", HaptificationPrefsPanel.SOUND_TYPES[0].toString());
+					for(GridSound.Type t : HaptificationPrefsPanel.SOUND_TYPES){
+						if(t.toString().equals(soundType)){
+							((DawHapticListener)haptics.getListener()).setGridSound(GridSound.get(t));
+							break;
+						}
+					}
+					
+					
+					
+					haptics.sendMessage(Command.DISPLAY_SEQUENCE,  
+							sequence.getLen()+" "+ // width in milliseconds 
+							new MathUtils.Scale(sequence.getRange(),Range.NORMALIZED_RANGE_F).linear(sequence.getBegin())+" "+  // initialYValue in normalized form 
+							hapticViewPortMs,
+							id
+						);
+					
+					
+					for(int i=0; i< sequence.getValuesNum(); i++){
+						Sequence.Value automValue = sequence.getValueAt(i);
+						haptics.sendMessage(Command.SEQUENCE_VALUE_ADD, automValue.getTimePosition()+" "+
+								new MathUtils.Scale(sequence.getRange(),Range.NORMALIZED_RANGE_F).linear(automValue.getValue()), automValue.hashCode());
+					}
+				}else{
+					/* soundWave friction */
+					uniqueHapticTrigger.set(new HapticTrigger((HapticDevice)haptics, HapticTrigger.DisplayType.DISPLAY_RENDER_VALUE));
+					uniqueHapticTrigger.addToListener(track.getSoundWave());
+					haptics.sendMessage(Command.DISPLAY_RENDER_VALUE, "", 0);
+				}
+				
+			}
+			
+			/* give feedback */
+			track.showHapticViewPort(true);
+			Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.OK);
+		}		
+	} // class ShowHapticsAction
+	
+	class PauseHapticsAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		PauseHapticsAction(){
+			super("haptic.pause");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			if(showHapticsAction.uniqueHapticTrigger.isNull()) // FIXME check pause haptics
+				haptics.sendMessage(HapticTrigger.Command.DISPLAY_NONE, "", 0);
+		}
+	}
+	
+	private class RotateHapticsAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		private String axis;
+
+		RotateHapticsAction(String axis){
+			super("haptics.rotate."+axis);
+			this.axis = axis;
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			AudioTrack track = arrangeWindow.getCurrentTrack();
+			
+			if(haptics  instanceof HapticDevice){
+				if("y".equals(axis))
+					haptics.sendMessage(HapticTrigger.Command.ROTATE_Y, "", track.hashCode());
+				else if("x".equals(axis)){
+					haptics.sendMessage(HapticTrigger.Command.ROTATE_X, "", track.hashCode());
+				}
+				else
+					haptics.sendMessage(HapticTrigger.Command.ROTATE_Z, "", track.hashCode());
+			}
+			Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.OK);
+		}
+		
+	} // class RotateAction
+	
+	private class ScrubAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		static final int DEFAULT_DELTA = 1;
+		private Integer delta; // delta = 0 => stop scrubbing
+		
+		/**
+		 * Constructs a new ScrubAction
+		 * 
+		 * @param resourceName the resource name from the action properties 
+		 * @param delta the amount of scrubbing. Positive value will shift to the right, negative
+		 * value will scrub to the left and a 0 value will stop scrubbing.  
+		 */
+		public ScrubAction(String resourceName, Integer delta) {
+			super(resourceName);
+			this.delta = delta;
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null)
+				return;
+			
+			SoundWave wave = track.getSoundWave();
+			
+			if(delta == null){ // stop scrubbing 
+				wave.scan(SoundWave.STOP_SCANNING);
+				return;
+			}
+
+			if(wave.getCurrentChunkPosition()+delta < 0) // do nothing if the cursor hits the 0 
+				return;
+			
+			wave.scan(wave.getCurrentChunkPosition()+delta);
+		}
+	} // class ScrubAction
+	
+	private class GeneratePeaksAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		boolean visible;
+		
+		GeneratePeaksAction(){
+			super("view.peaks.show");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null){
+				return;
+			}
+				
+			SoundWave wave = track.getSoundWave();
+			
+			visible = !visible;
+			setText("view.peaks."+(visible ? "dont_show" : "show"));
+			wave.generatePeakMeter(visible);
+		}
+	} // class GeneratePeaksAction
+	
+	
+	private class ShowDbViewAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		private boolean visible;
+		
+		public ShowDbViewAction() {
+			super("view.db.show");
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null){
+				return;
+			}
+			
+			visible = !visible;
+			resetFindIndex();
+			setText("view.db."+ (visible ? "dont_show" : "show"));
+			track.showDbWave(visible);
+			
+		}
+	} // class ShowDbViewAction
+	
+	private class RemoveSelectionAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+
+		RemoveSelectionAction(){
+			super("track.cancel_selection");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track != null){
+				track.getSoundWave().setSelection(null);// null = no selection
+			}
+		}
+	} // RemoveSelectionAction
+	
+	private class FindNextPointAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		private final Direction direction;
+		
+		FindNextPointAction(Direction d){
+			super(d == Direction.RIGHT ? "view.find.next_seq_val" : "view.find.prev_seq_val");
+			direction = d;
+			resetFindIndex();
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			
+			if(track == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			SoundWave wave = track.getSoundWave();
+			
+			if(wave.hasSequence()){
+				Automation automation = wave.getParametersControl().getCurrentAutomation();
+				if(automation.getValuesNum() == 0){
+					sonification.play(SoundType.ERROR);
+					return;
+				}
+				
+				if(direction == Direction.RIGHT){
+					if(currentFindPosition == -1){
+						currentFindPosition = 0;
+					}else{
+						currentFindPosition = (currentFindPosition + 1) % automation.getValuesNum();
+						if(currentFindPosition == 0){
+							sonification.play(SoundType.LOOP);
+						}
+					}
+				}else if (direction == Direction.LEFT){
+					if(currentFindPosition == -1){
+						currentFindPosition = automation.getValuesNum() - 1;
+						sonification.play(SoundType.LOOP);
+					}else{
+						currentFindPosition = currentFindPosition - 1;
+						if(currentFindPosition == -1){
+							currentFindPosition = automation.getValuesNum() - 1;
+							sonification.play(SoundType.LOOP);
+						}
+					}
+				}
+				
+				Sequence.Value automVal = automation.getValueAt(currentFindPosition);
+				wave.setPosition((int) (automVal.getTimePosition()/wave.getMillisecPerChunk()));
+				if( showHapticsAction.uniqueHapticTrigger.isNull()) {// FIXME more elegant 
+					haptics.sendMessage(Command.SEQUENCE_VALUE_FIND, Integer.toString(currentFindPosition) , 0);
+				}
+				
+			}else if(wave.getDbWave().hasSequence()){
+				Sequence peakLevelMeter = wave.getDbWave().getSequence();
+				if(peakLevelMeter.getValuesNum() == 0){
+					sonification.play(SoundType.ERROR);
+					return;
+				}
+				
+				Preferences prefs = Preferences.userNodeForPackage(BeadsSequenceMapping.class); // FIXME remove beads dependency 
+				float borderThreshold =  prefs.getFloat("borders.threshold", 0.01f);
+				
+				if(direction == Direction.RIGHT){					
+					if(currentFindPosition == -1 ){
+						currentFindPosition = 0;
+						Sequence.Value seqVal = peakLevelMeter.getValueAt(currentFindPosition);
+//						if(seqVal.getValue() > peakLevelMeter.getRange().getEnd() - borderThreshold ) {
+						if( peakLevelMeter.getRange().getEnd() - seqVal.getValue() <= borderThreshold ) {
+							wave.setPosition((int) (seqVal.getTimePosition()/wave.getMillisecPerChunk()));
+							return;
+						}
+					}
+					
+					int stopIndex = currentFindPosition;						
+					
+					do {
+						currentFindPosition = (currentFindPosition + 1) % peakLevelMeter.getValuesNum();
+						Sequence.Value seqVal = peakLevelMeter.getValueAt(currentFindPosition);
+						if(peakLevelMeter.getRange().getEnd() - seqVal.getValue() <= borderThreshold  ) { //   FIXME 
+							if(currentFindPosition < stopIndex){
+								sonification.play(SoundType.LOOP);
+							}
+							wave.setPosition((int) (seqVal.getTimePosition()/wave.getMillisecPerChunk()));
+							return;
+						}
+					}while(currentFindPosition != stopIndex);
+					
+					/* clipping not found */
+					sonification.play(SoundType.ERROR);
+					
+					
+				}else if (direction == Direction.LEFT){
+					if(currentFindPosition == -1 ){
+						currentFindPosition = peakLevelMeter.getValuesNum()-1;
+						Sequence.Value seqVal = peakLevelMeter.getValueAt(currentFindPosition);
+						if(seqVal.getValue() > peakLevelMeter.getRange().getEnd() - 0.02f ) {
+							sonification.play(SoundType.LOOP);
+							wave.setPosition((int) (seqVal.getTimePosition()/wave.getMillisecPerChunk()));
+							return;
+						}
+					}
+					
+					int stopIndex = currentFindPosition;
+					
+					do {
+						currentFindPosition = (currentFindPosition - 1);
+						if(currentFindPosition == -1){
+							currentFindPosition = peakLevelMeter.getValuesNum()-1;
+						}
+						Sequence.Value seqVal = peakLevelMeter.getValueAt(currentFindPosition);
+						if(seqVal.getValue() > peakLevelMeter.getRange().getEnd() - 0.02f ) { // clipping FIXME 
+							if(currentFindPosition > stopIndex){
+								sonification.play(SoundType.LOOP);
+							}
+							wave.setPosition((int) (seqVal.getTimePosition()/wave.getMillisecPerChunk()));
+							return;
+						}
+					}while(currentFindPosition != stopIndex);
+					
+					/* clipping not found */
+					sonification.play(SoundType.ERROR);
+				}
+			}else{
+				sonification.play(SoundType.ERROR);
+			}
+		}
+	}
+	
+	private class ToggleScrubAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		private boolean scrub = true;
+		
+		ToggleScrubAction(){
+			super("haptics.toggle_scrub");
+			setEnabled(false);
+			
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent arg0) {
+			scrub = !scrub;
+			((DawHapticListener)haptics.getListener()).setScrubEnabled(scrub);
+			
+		}
+	}
+	
+	private void resetFindIndex(){
+		currentFindPosition = -1;
+	}
+} // class Actions
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/actions/EditActions.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,139 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.Action;
+
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.gui.AudioTrack;
+import uk.ac.qmul.eecs.depic.daw.gui.MainFrame;
+
+class EditActions {
+	MainFrame frame;
+	Action cutAction;
+	Action pasteAction;
+	Action copyAction;
+	Action insertAction;
+	Sonification sonification;
+	
+	public EditActions(MainFrame frame) {
+		this.frame = frame;
+		sonification = Daw.getSoundEngineFactory().getSharedSonification();
+		
+		cutAction = new CutAction();
+		copyAction = new CopyAction();
+		pasteAction = new PasteAction();
+		insertAction = new InsertAction();
+	}
+
+	class CutAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		public CutAction() {
+			super("edit.cut");
+		}
+
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			boolean copied = track.getSoundWave().getEditor().cut(track.getSoundWave(), track.getSelection());
+			
+			sonification.play(copied ? SoundType.OK : SoundType.ERROR);
+		}
+	} // class CutAction
+	
+	class CopyAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		public CopyAction(){
+			super("edit.copy");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			boolean copied = track.getSoundWave().getEditor().copy(track.getSoundWave(), track.getSelection());
+			
+			sonification.play(copied ? SoundType.OK : SoundType.ERROR);
+		}
+	} // CopyAction
+	
+	class PasteAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		public PasteAction(){
+			super("edit.paste");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			boolean pasted = track.getSoundWave().getEditor().paste(track.getSoundWave(), track.getSoundWave().getCurrentChunkPosition());
+			if(!pasted){
+				sonification.play(SoundType.ERROR);
+			}else{
+				sonification.play(SoundType.OK);
+			}
+		}
+	}
+	
+	class InsertAction extends MenuAction {
+		private static final long serialVersionUID = 1L;
+		
+		public InsertAction(){
+			super("edit.insert");
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent evt) {
+			AudioTrack track = frame.getArrangeWindow().getCurrentTrack();
+			if(track == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			boolean inserted = track.getSoundWave().getEditor().insert(track.getSoundWave(), track.getSoundWave().getCurrentChunkPosition());
+			if(!inserted){
+				sonification.play(SoundType.ERROR);
+			}else{
+				sonification.play(SoundType.OK);
+			}
+		}
+	}
+	
+} // class EditAction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/actions/MenuAction.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,59 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui.actions;
+
+import java.awt.event.KeyEvent;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.AbstractAction;
+import javax.swing.KeyStroke;
+
+
+abstract class MenuAction extends AbstractAction {
+	private static final long serialVersionUID = 1L;
+
+	public MenuAction(String resourceName){
+		ResourceBundle bundle = ResourceBundle.getBundle(MenuAction.class.getName());
+		/* set the menu label */
+		String name = bundle.getString(resourceName+".text");
+		putValue(AbstractAction.NAME,name);
+		/* try and set the accelerator and mnemonic */ 
+		try{
+			String accelerator = bundle.getString(resourceName+".accelerator");
+			putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accelerator));
+		}catch(MissingResourceException mre){
+			// do nothing, accelerator just won't be set
+		}
+		
+		try{
+			String mnemonic = bundle.getString(resourceName+".mnemonic");
+			putValue(AbstractAction.MNEMONIC_KEY,KeyEvent.getExtendedKeyCodeForChar(mnemonic.charAt(0)));
+		}catch(MissingResourceException mre){
+			// do nothing, mnmonic just won't be set
+		}
+	}
+	
+	public void setText(String resourceName){
+		ResourceBundle bundle = ResourceBundle.getBundle(MenuAction.class.getName());
+		String name = bundle.getString(resourceName+".text");
+		putValue(AbstractAction.NAME,name);
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/actions/MenuAction.properties	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,116 @@
+
+### FILE ###
+file.text=File
+file.mnemonic=F
+
+file.open.text=Open
+file.open.accelerator=ctrl O
+file.open.mnemonic=O
+
+file.close.text=Close
+file.close.mnemonic=C
+
+file.properties.text=Properties
+file.properties.mnemonic=P
+
+file.exit.text=Exit
+file.exit.menmonic=x
+
+### EDIT ### 
+
+edit.text=Edit
+edit.mnemonic=E
+
+edit.cut.text=Cut
+edit.cut.accelerator=ctrl X
+
+edit.copy.text=Copy
+edit.copy.accelerator=ctrl C
+
+edit.paste.text=Paste
+edit.paste.accelerator=ctrl V
+
+edit.insert.text=Insert
+edit.insert.accelerator=ctrl I
+
+
+### VIEW ###
+
+view.text=View
+view.mnemonic=V
+
+view.zoom_in.text=Zoom In
+view.zoom_in.accelerator=ctrl EQUALS
+view.zoom_in.mnemonic=I
+
+view.zoom_out.text=Zoom Out
+view.zoom_out.accelerator=ctrl MINUS
+view.zoom_out.mnemonic=O
+
+view.peaks.show.text=Generate Peaks Curve
+view.peaks.dont_show.text=Remove Peaks Curve
+
+view.find.next_seq_val.text=Find next point
+view.find.next_seq_val.accelerator=ctrl F
+view.find.next_seq_val.mnemonic=F
+
+view.find.prev_seq_val.text=Find previous point
+view.find.prev_seq_val.accelerator=ctrl shift F
+view.find.prev_seq_val.mnemonic=p
+
+view.db.show.text=Show Db wave
+view.db.dont_show.text=Show Amplitude wave
+
+### TRACK ### 
+
+track.text=Track
+track.mnemonic=T
+
+track.add_track.text=Add Track
+track.add_track.mnemonic=A
+
+track.cancel_selection.text=Cancel Selection
+track.cancel_selection.mnemonic=C
+track.cancel_selection.accelerator=ESCAPE
+
+track.switch_automation.text=Switch Automation
+track.switch_automation.mnemonic=S
+track.switch_automation.accelerator=ctrl A
+
+track.scrub_forwards.text=Scrub Forwards
+#track.scrub_forwards.accelera
+
+track.scrub_backwards.text=Scrub Backwards
+
+track.stop_scrub.text=Stop Scrubbing
+
+
+### HAPTICS ###
+
+haptics.text=Haptics
+haptics.mnemonic=H
+
+haptics.falcon_start.text=Start Haptic Device
+haptics.falcon_start.mnemonic=F
+haptics.falcon_stop.text = Stop Haptic Device
+
+
+haptics.show.text=Activate Current view in Haptics
+haptics.show.mnemonic=A
+haptics.show.accelerator=ctrl H
+
+haptic.pause.text=Pause Haptic Device
+haptic.pause.mnemonic=P
+haptic.pause.accelerator=ctrl J
+
+haptics.rotate.y.text=Rotate Haptics to Depth
+haptics.rotate.y.mnemonic = D
+
+haptics.rotate.z.text=Rotate Haptics to Vertical
+haptics.rotate.z.mnemonic = V
+
+haptics.rotate.x.text=Rotate Haptics to Horizontal
+haptics.rotate.x.mnemonic = H
+
+haptics.toggle_scrub.text = Toggle Scrub
+haptics.toggle_scrub.accelerator = F1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/actions/TransportControlActions.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,168 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ImageIcon;
+
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.gui.ArrangeWindow;
+import uk.ac.qmul.eecs.depic.daw.gui.AudioTrack;
+import uk.ac.qmul.eecs.depic.daw.gui.MainFrame;
+
+public class TransportControlActions {
+	private MainFrame frame;
+	private boolean isPlaying; 
+	
+	Action playAction;
+	Action stopAction;
+	Action rewAction;
+	Action fwdAction;
+	Action loopAction;
+	
+	public TransportControlActions(MainFrame frame) {
+		this.frame = frame;
+		
+		playAction = new PlayAction();
+		stopAction = new StopAction();
+		rewAction = new RewAction();
+		fwdAction = new FwdAction();
+		loopAction = new LoopAction();
+	}
+	
+	class PlayAction extends AbstractAction {
+		private static final long serialVersionUID = 1L;
+		
+		PlayAction(){
+			super("play",new ImageIcon(TransportControlActions.class.getResource("play.png")));
+			
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow(); 
+
+			isPlaying = !isPlaying;
+			
+			if(isPlaying) {
+				AudioTrack currentTrack = arrangeWindow.getCurrentTrack();
+				/* set all the tracks at the ame position */
+				for(int i=0; i<arrangeWindow.getTracksCount();i++){
+					if(arrangeWindow.getTrackAt(i) != currentTrack){
+						arrangeWindow.getTrackAt(i).setCursorPos(currentTrack.getCursorPos());
+					}
+				}
+			}
+			
+			for(int i=0; i<arrangeWindow.getTracksCount();i++){
+				if(isPlaying){
+					arrangeWindow.getTrackAt(i).getSoundWave().getTransportControl().play();
+				}else{
+					arrangeWindow.getTrackAt(i).getSoundWave().getTransportControl().pause();
+				}
+			}
+			
+			putValue(Action.SELECTED_KEY,isPlaying);
+		}
+		
+	} // class PlayAction
+	
+	class StopAction extends AbstractAction {
+		
+		StopAction(){
+			super("stop",new ImageIcon(TransportControlActions.class.getResource("stop.png")));
+		}
+		
+		private static final long serialVersionUID = 1L;
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			for(int i=0;i<arrangeWindow.getTracksCount();i++){
+				arrangeWindow.getTrackAt(i).getSoundWave().getTransportControl().stop();
+			}
+			/* set the state of play action to stop */
+			isPlaying = false;
+			/* set the label to the action performed nect time the button is pressed */
+			playAction.putValue(Action.SELECTED_KEY, false);
+		}
+	} // class StopAction
+	
+
+	class RewAction extends AbstractAction {
+		private static final long serialVersionUID = 1L;
+		
+		RewAction(){
+			super("rew",new ImageIcon(TransportControlActions.class.getResource("rew.png")));
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			for(int i=0;i<arrangeWindow.getTracksCount();i++){
+				AudioTrack track = arrangeWindow.getTrackAt(i); 
+				track.getSoundWave().setPosition(0);
+			}
+			Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.LOOP);
+		}
+	}; // class RewAction 
+	
+	class FwdAction extends AbstractAction {
+		private static final long serialVersionUID = 1L;
+		
+		FwdAction(){
+			super("forward",new ImageIcon(TransportControlActions.class.getResource("fwd.png")));
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			for(int i=0;i<arrangeWindow.getTracksCount();i++){
+				AudioTrack track = arrangeWindow.getTrackAt(i);
+				track.getSoundWave().setPosition(track.getSoundWave().getChunkNum());
+			}
+		}
+		
+	}; // class FwdAction
+	
+	class LoopAction extends AbstractAction {
+		private static final long serialVersionUID = 1L;
+		private boolean selected;
+
+		LoopAction(){
+			super("loop",new ImageIcon(TransportControlActions.class.getResource("loop.png")));
+		}
+		
+		@Override
+		public void actionPerformed(ActionEvent e) {
+			selected = !selected;
+			putValue(Action.SELECTED_KEY,selected);
+			
+			ArrangeWindow arrangeWindow = frame.getArrangeWindow();
+			for(int i=0;i<arrangeWindow.getTracksCount();i++){
+				AudioTrack track = arrangeWindow.getTrackAt(i);
+				track.getSoundWave().getTransportControl().setLoop(selected);
+			}
+		}
+		
+	} // class LoopAction
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/gui/actions/UniqueHapticTrigger.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,70 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.gui.actions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.haptics.HapticTrigger;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+class UniqueHapticTrigger {
+	private HapticTrigger trigger;
+	private UniqueHapticTrigger uniqueTrigger;
+	private List<Object> observed = new ArrayList<>(2);
+		
+	public void set(HapticTrigger t){
+		
+		/* remove the trigger from the observed objects */
+		for(Object o : observed){
+			if(o instanceof Sequence){
+				((Sequence) o).removeSequenceListener(trigger);
+			}else{
+				((SoundWave) o).removeSoundWaveListener(trigger);
+			}
+		}
+		
+		observed.clear();
+		trigger = t;
+	}
+	
+	public UniqueHapticTrigger get(){
+		return uniqueTrigger;
+	}
+	
+	UniqueHapticTrigger(){
+	
+	}
+	
+	public void addToListener(Sequence s){
+		s.addSequenceListener(trigger);
+		observed.add(s);
+	}
+	
+	public void addToListener(SoundWave sw){
+		sw.addSoundWaveListener(trigger);
+		observed.add(sw);
+	}
+	
+	public boolean isNull() {
+		return uniqueTrigger == null;
+	}
+	
+}
Binary file src/uk/ac/qmul/eecs/depic/daw/gui/actions/fwd.png has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/gui/actions/loop.png has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/gui/actions/play.png has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/gui/actions/rew.png has changed
Binary file src/uk/ac/qmul/eecs/depic/daw/gui/actions/stop.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/haptics/DawHapticListener.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,287 @@
+package uk.ac.qmul.eecs.depic.daw.haptics;
+
+import java.awt.geom.Point2D;
+
+import javax.swing.SwingUtilities;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.AutomationValue;
+import uk.ac.qmul.eecs.depic.daw.Daw;
+import uk.ac.qmul.eecs.depic.daw.Sonification;
+import uk.ac.qmul.eecs.depic.daw.SoundType;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.gui.AudioTrack;
+import uk.ac.qmul.eecs.depic.daw.gui.MainFrame;
+import uk.ac.qmul.eecs.depic.daw.referencesound.GridSound;
+import uk.ac.qmul.eecs.depic.jhapticgui.HapticListener;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+
+public class DawHapticListener extends HapticListener {
+	public static final boolean SUPPORT_VIEW_PORT = false; 
+	public static final String SCRUB_OFF_MSG = "scrub.off";
+	public static final String SCRUB_ON_MSG = "scrub.on";
+	public static final String SCRUB_SHIFT = "scrub.shift";
+	
+	public static final String ADD_SEQ_VAL = "seq.val.add" ;
+	public static final String REM_SEQ_VAL = "seq.val.rem" ;
+	public static final String MOVE_SEQ_VAL= "seq.val.move" ;
+	public static final String PICK_SEQ_VAL = "seq.val.pick"; 
+	public static final String DROP_SEQ_VAL = "seq.val.drop";
+	
+	public static final String LINE_TOUCHED = "line.touched";
+	public static final String POINT_TOUCHED = "point.touched";
+	
+	private MainFrame frame;
+	private Sonification sonification = Daw.getSoundEngineFactory().getSharedSonification();
+	private GridSound gridSound;
+	private Sequence.Value line;
+	private float lineValue;
+	private AudioTrack track;
+	private boolean scrubEnabled;
+	
+	long lastTouchTime = 0;
+
+	public DawHapticListener(MainFrame frame){
+		this.frame = frame;
+		gridSound = GridSound.get(GridSound.Type.NONE);
+		scrubEnabled = true;
+		final Sequence sequence = new Sequence () {
+
+			@Override
+			public float getBegin() {
+				return 0;
+			}
+
+			@Override
+			public float getEnd() {
+				return 0;
+			}
+
+			@Override
+			public int getValuesNum() {
+				return 1;
+			}
+
+			@Override
+			public Value getValueAt(int index) {
+				return line;
+			}
+
+			@Override
+			public Range<Float> getRange() {
+				return new Range<Float>(0.0f, 30.0f);
+			}
+
+			@Override
+			public void addSequenceListener(SequenceListener l) {}
+
+			@Override
+			public void removeSequenceListener(SequenceListener l) {}
+
+			@Override
+			public float getLen() {
+				return 0;
+			}
+			
+		};
+		
+		line = new Sequence.Value() {
+			@Override
+			public int index() {
+				return 0;
+			}
+			
+			@Override
+			public float getValue() {
+				return lineValue;
+			}
+			
+			@Override
+			public float getTimePosition() {
+				return 0;
+			}
+			
+			@Override
+			public Sequence getSequence() {
+				return sequence;
+			}
+		};
+	}
+	
+	public void setGridSound(GridSound  gs){
+		System.out.println("setGridSound " + gs);
+		gridSound = gs;
+	}
+	
+	public void setScrubEnabled(boolean enabled){
+		scrubEnabled = enabled;
+		
+		if(track != null)
+			track.getSoundWave().scan(SoundWave.STOP_SCANNING);
+		
+		sonification.play(SoundType.OK); // give feedback
+	}
+	
+	public void setTrack(AudioTrack track){
+		this.track = track;
+	}
+	
+	
+	/**
+	 * 
+	 * executes the messages in the Event Dispatching Thread
+	 * 
+	 */
+	@Override
+	protected void fireMessageReceived(final Message m){
+		SwingUtilities.invokeLater(new Runnable(){
+			@Override
+			public void run(){
+				messageReceived(m.command, m.args, m.ID);
+			}
+		});
+	}
+	
+	
+	@Override
+	public void messageReceived(String msg, String args, long ID) {		
+		if(track == null)
+			return;
+		
+		switch(msg)
+		{
+		
+		case SCRUB_ON_MSG : {
+			if(!scrubEnabled)
+				return;
+			
+			SoundWave wave = track.getSoundWave();
+			/* position of the haptic proxy in the horizontal *  
+			 * scrub line of the haptic space                 */
+			float hapticPos = Float.parseFloat(args);
+			wave.scan(track.getHapticViewPort().getPosition(hapticPos));
+			break;
+		}
+		
+		case  SCRUB_OFF_MSG : {
+			track.getSoundWave().scan(SoundWave.STOP_SCANNING);
+			break;
+		}
+		
+		case SCRUB_SHIFT : {
+			/* calculate the step size in pixels starting from the step size in milliseconds */
+			SoundWave wave = track.getSoundWave();
+			float timeShift = Float.parseFloat(args);
+			
+			float chunkShift = timeShift/wave.getMillisecPerChunk();
+			/* position to set for the cursor in the view port of the sound wave */
+			track.getHapticViewPort().shift(chunkShift);
+			if(SUPPORT_VIEW_PORT){
+				/* reduce the frequency of sound feedback to once every 200 ms */
+				long now = System.currentTimeMillis(); 
+				if( now - lastTouchTime >= 200){
+					sonification.play(SoundType.HAPTIC_PORT_TOUCH, 1.0f, chunkShift>0 ? 1 : -1);
+					lastTouchTime = now;
+				}
+			}
+			break;
+		}
+		
+		case ADD_SEQ_VAL : {
+			String [] hapticCoord = args.split(" ");
+			int chunkPos = track.normToWidthconvert(Float.parseFloat(hapticCoord[0]));
+			int rangePos = track.normToHeightConvert(Float.parseFloat(hapticCoord[1]));
+			
+			
+			Point2D.Float p = AudioTrack.getAutomationCoord(track, chunkPos, rangePos);
+			
+			/* if user is trying to create an automation outside the bounds of the automation, notify the error */
+			if(p == null){
+				sonification.play(SoundType.ERROR);
+				return;
+			}
+			
+			SoundWave wave = track.getSoundWave();
+			
+			
+			//p.y = new MathUtils.Interpolate(wave.getParametersControl().getCurrentAutomation())
+			//	.linear(p.y);
+			
+			/* add the automation point to the model and sonify it */
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(
+					wave.getParametersControl().getCurrentAutomation().add(p.x, p.y));
+			break;
+		}
+		
+		case REM_SEQ_VAL : {
+			/* find the automaton value with this hash code */
+			Sequence s = track.getSoundWave().getSequence();
+			AutomationValue aVal = null;
+			for(int i=0 ; i< s.getValuesNum(); i++){
+				if(s.getValueAt(i).hashCode() == ID){
+					aVal = (AutomationValue)s.getValueAt(i);
+					break;
+				}
+			}
+			
+			((Automation)s).remove(aVal);
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(aVal);
+//			sonification.play(SoundType.ERROR);
+			break;
+		}
+		
+		case MOVE_SEQ_VAL : {
+			String [] hapticCoord = args.split(" ");
+			
+			/* get the automation point */
+			Sequence s = track.getSoundWave().getSequence();
+			AutomationValue aVal = fetchAutomationValue(ID);
+			
+			int chunkPos = track.normToWidthconvert(Float.parseFloat(hapticCoord[0]));
+			int rangePos = track.normToHeightConvert(Float.parseFloat(hapticCoord[1]));
+				
+			Point2D.Float pointCoord = AudioTrack.getAutomationCoord(track,chunkPos,rangePos);
+				
+			if(pointCoord != null){
+				aVal.setLocation(pointCoord.x, pointCoord.y);				
+				track.getSoundWave().scan(chunkPos);
+				
+			}else{
+				sonification.play(SoundType.ERROR);
+			}
+
+			break;
+		}
+		
+		
+		case LINE_TOUCHED : {
+			lineValue = ID;
+			gridSound.sound(line);
+			break;
+		}
+		
+		case POINT_TOUCHED : {
+			AutomationValue aVal = fetchAutomationValue(ID);
+			
+			sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(aVal);
+		}
+		
+		
+		}
+	}
+	
+	private AutomationValue fetchAutomationValue(long ID){
+		/* find the automaton value with this hash code */
+		Sequence s = track.getSoundWave().getSequence();
+		for(int i=0 ; i< s.getValuesNum(); i++){
+			if(s.getValueAt(i).hashCode() == ID){
+				return (AutomationValue)s.getValueAt(i);
+			}
+		}
+		
+		return null;
+	}
+	
+}
\ No newline at end of file
Binary file src/uk/ac/qmul/eecs/depic/daw/haptics/DawHaptics.dll has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/haptics/HapticTrigger.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,158 @@
+package uk.ac.qmul.eecs.depic.daw.haptics;
+
+import uk.ac.qmul.eecs.depic.daw.Chunk;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
+import uk.ac.qmul.eecs.depic.jhapticgui.Haptics;
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.Sequence.Value;
+import uk.ac.qmul.eecs.depic.patterns.SequenceEvent;
+import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+
+public class HapticTrigger implements SequenceMapping, SequenceListener,  SoundWaveListener  {
+	public enum DisplayType { 
+		DISPLAY_SEQUENCE,
+		DISPLAY_NONE,
+		DISPLAY_PEAKS,
+		DISPLAY_RENDER_CURVE_AT,
+		DISPLAY_RENDER_VALUE
+	}
+	
+	public abstract class Command {
+		/**
+		 * Message to display a graph 
+		 * 
+		 * Arguments: intitial y value, max y value, min y value, max x value, x viewport size 
+		 */
+		public static final String DISPLAY_SEQUENCE   = "display.seq"; 
+		public static final String DISPLAY_RENDER_CURVE_AT  = "display.curve_at";
+		public static final String DISPLAY_RENDER_VALUE  = "display.value";
+		/**
+		 * display nothing. takes no args 
+		 */
+		public static final String DISPLAY_NONE   = "display.none";
+		public static final String DISPLAY_PEAKS   = "display.peaks";
+		
+		/**
+		 * Arguments : x time position, y in normalized form  
+		 */
+		public static final String SEQUENCE_VALUE_ADD  = "seq.value.add";
+		public static final String SEQUENCE_VALUE_CHANGE  = "seq.value.change";
+		public static final String SEQUENCE_VALUE_REMOVE  = "seq.value.rem";
+		public static final String SEQUENCE_VALUE_FIND  = "seq.value.find";
+		public static final String SEQUENCE_SHIFT  = "seq.shift";
+		public static final String SEQUENCE_BEGIN  = "seq.begin";
+		
+		public static final String RENDER_VALUE = "render_value";
+		public static final String RENDER_CURVE_AT = "render_curve_at";
+		/**
+		 * Message to rotate the viscosity scrub line in the haptic space  
+		 * 
+		 * Argument is the degree of rotation  
+		 */
+		public static final String ROTATE_Z   = "rotate.z";
+		public static final String ROTATE_Y   = "rotate.y";
+		public static final String ROTATE_X   = "rotate.x";
+	}
+	
+	private DisplayType displayType;
+	private Haptics haptics;
+	
+	public HapticTrigger(Haptics haptics, DisplayType type){
+		if(haptics == null)
+			throw new IllegalArgumentException("haptics cannot be null");
+		this.haptics = haptics;
+		displayType = type;
+		haptics.sendMessage(Command.DISPLAY_NONE,"",0);
+	}
+	
+	@Override
+	public void renderValue(Value val) {
+		/* sends a normalized value of the chunk size */
+		haptics.sendMessage(Command.RENDER_VALUE, Float.toString(val.getValue()) , val.hashCode());
+	}
+
+	@Override
+	public void renderCurve(Sequence m, float startTime) {
+		throw new UnsupportedOperationException("Only update implemented");
+	}
+
+	@Override
+	public void renderCurveAt(Sequence sequence, float time, float duration) {
+		float val = new MathUtils.Interpolate(sequence).linear(time);
+		
+		/* normalize the value to [0-1] range */
+		float normalizedValue = new MathUtils.Scale(sequence.getRange(), new Range<Float>(0.0f,1.0f)).linear(val);
+		haptics.sendMessage(Command.RENDER_CURVE_AT, ""+normalizedValue, sequence.hashCode());
+	}
+
+	@Override
+	public <T extends SequenceEvent> void sequenceUpdated(T t) {
+		SequenceEvent.What evtType = t.getWhat();
+		
+		if(displayType == DisplayType.DISPLAY_SEQUENCE) {
+			if(SequenceEvent.What.VALUE_ADDED.equals(evtType)){
+				Sequence.Value aVal = t.getValue();
+				
+				/* since the haptic graph y value are in normalized form normalize first */
+				float verticalValue = new MathUtils.Scale(t.getSource().getRange(), Range.NORMALIZED_RANGE_F).linear(aVal.getValue());
+				
+				haptics.sendMessage(Command.SEQUENCE_VALUE_ADD, aVal.getTimePosition()+" "+verticalValue, aVal.hashCode());
+			}else if(SequenceEvent.What.VALUE_CHANGED.equals(evtType)){
+				Sequence.Value aVal = t.getValue();
+				
+				float verticalValue = new MathUtils.Scale(t.getSource().getRange(), Range.NORMALIZED_RANGE_F).linear(aVal.getValue());
+				
+				haptics.sendMessage(Command.SEQUENCE_VALUE_CHANGE, aVal.getTimePosition() +" "+verticalValue, aVal.hashCode());
+			}else if(SequenceEvent.What.VALUE_REMOVED.equals(evtType)){
+				Sequence.Value aVal = t.getValue();
+				haptics.sendMessage(Command.SEQUENCE_VALUE_REMOVE, "", aVal.hashCode());
+			}else if(SequenceEvent.What.BEGIN_CHANGED.equals(evtType)){
+				Sequence sequence = t.getSource();
+				
+				haptics.sendMessage(Command.SEQUENCE_BEGIN,   
+						""+new MathUtils.Scale(sequence.getRange(),Range.NORMALIZED_RANGE_F).linear(sequence.getBegin()),
+						0
+					);
+			}
+		}
+		
+	}
+	
+	/**
+	 * Gets an update from a {@code SoundWave} this class is a listener of, and send a message to the {@code HapticDevice}
+	 * thread accordingly.
+	 * 
+	 * @param evt the new event  
+	 */
+	@Override
+	public void update(SoundWaveEvent evt) {
+		String evtType = evt.getType();
+		
+		if(SoundWaveEvent.POSITION_CHANGED.equals(evtType)){
+			SoundWave wave = evt.getSource();
+			int pos = (Integer)evt.getArgs();
+			
+			if(pos < wave.getChunkNum()){
+				Chunk chunk = wave.getChunkAt(pos);
+				float chunkSize = chunk.getNormEnd() - chunk.getNormStart();
+				/* sends a normalized value of the chunk size */
+				haptics.sendMessage(Command.RENDER_VALUE, Float.toString(chunkSize) , chunk.hashCode());
+			}else{
+				/* if the scub goes past the audio wave just send wave amp = 0 */
+				haptics.sendMessage(Command.RENDER_VALUE, "0", 0);
+			}
+		}else if(SoundWaveEvent.CLOSE.equals(evtType)){
+			/* removes itself from listeners */
+			evt.getSource().removeSoundWaveListener(this);
+		}
+	}
+	
+	
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/haptics/HapticViewPort.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,78 @@
+package uk.ac.qmul.eecs.depic.daw.haptics;
+
+public class HapticViewPort {
+	public static final int DEFAULT_WIDTH = 2320; // 2 secs at scale factor =1 
+												  //with 8000 Hz frame rate. x = 4000/frameRate * minChunksize => 2 = 4000/8000 * 4 
+	public final static float STEP_SIZE = 5.0f;// in milliseconds 
+	private static final double LOG_2 = Math.log(2);
+	
+	/* the width of the viewport */
+	private int width;
+	private float offset;
+	private int trackSize;
+	private int scaleFactor;
+	
+	public HapticViewPort(int scaleFactor){
+		offset = 0.0f;
+		width = DEFAULT_WIDTH;
+		this.scaleFactor = scaleFactor;
+		trackSize = 0;
+	}
+	
+
+	/**
+	 * 
+	 * @param amount the amount of shift in pixels (or chunks)
+	 */
+	public void shift(float amount){
+		if(width > trackSize) // shifts only if viewport is smaller than the whole track
+			return;
+
+		offset += amount;
+		if(offset < 0) // cannot shift more left than 0 
+			offset = 0;
+		else if(offset > (trackSize - width)) // cannot more right than the track size-VIEWPORT_SIZE 
+			offset = (trackSize - width);     // (as the offset counts for the viewport start)
+	}
+	
+	/**
+	 * Returns the position in the haptic view port that is mapped to {@code ratio}. 
+	 * {@code ratio} is a normalized value ranging from 0 to 1. 
+	 * 
+	 * 
+	 * @param ratio the position on the haptic view port as a ratio of the haptic view port width  
+	 * @return the the position on the haptic view port as chunk/pixel position  
+	 */
+	public int getPosition(float ratio){
+		return (int)(offset+(width*ratio));
+	}
+	
+	public void setTrackSize(int trackSize){
+		this.trackSize = trackSize;
+	}
+	
+	public int getScaleFactor() {
+		return scaleFactor;
+	}
+	
+	public int getWidth(){
+		return width;
+	}
+
+	public void setScaleFactor(int scaleFactor) {
+		if(scaleFactor <= 0)
+			throw new IllegalArgumentException("scaleFactor must be greater than 0");
+		this.scaleFactor = scaleFactor;
+		
+		/* this formula makes the viewport size grow linearly-ish * 
+		 * It grows as a function of scaleFactor : (2 * (2^(scaleFactor - 1)) / scaleFactor) + 2*log_2(scaleFactor)) */
+		double w = (DEFAULT_WIDTH / scaleFactor ) + (log2(scaleFactor) * DEFAULT_WIDTH/Math.pow(2, scaleFactor-1)) ;
+		width = (int)w;
+	}
+
+	public static double log2(double x) {
+		return Math.log(x)/LOG_2;
+	}
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/haptics/HaptificationPrefsPanel.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,131 @@
+package uk.ac.qmul.eecs.depic.daw.haptics;
+
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.util.prefs.Preferences;
+
+import javax.swing.JLabel;
+
+import uk.ac.qmul.eecs.depic.daw.gui.PreferencesPanel;
+import uk.ac.qmul.eecs.depic.daw.referencesound.GridSound;
+
+import javax.swing.JComboBox;
+
+public class HaptificationPrefsPanel extends PreferencesPanel {
+	
+	public static GridSound.Type [] SOUND_TYPES = {
+		GridSound.Type.NONE, 
+		GridSound.Type.PITCH,
+		GridSound.Type.PITCH_ONE_REF,
+		GridSound.Type.PITCH_MUL_REF 
+	};
+	
+	public static String [] GRID_TYPES = {
+		"freeform","grid0","grid1", "grid3"
+	};
+	
+	private static String [] keys = new String [] {
+		"sound_type",
+		"grid_type",
+	};
+
+	private static final long serialVersionUID = 1L;
+
+	
+	private static HaptificationPrefsPanel singleton;
+	private JComboBox<GridSound.Type> soundComboBox;
+	private JComboBox<String> gridComboBox;
+	public static HaptificationPrefsPanel getInstance(){
+		if(singleton == null)
+			singleton = new HaptificationPrefsPanel();
+		return singleton;
+	}
+
+	/**
+	 * Create the panel.
+	 */
+	private HaptificationPrefsPanel() {
+		
+		GridBagLayout gridBagLayout = new GridBagLayout();
+		gridBagLayout.columnWidths = new int[]{30, 70, 78, 189, 0, 0};
+		gridBagLayout.rowHeights = new int[]{30, 18, 0, 0, 0, 0, 0};
+		gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE};
+		gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
+		setLayout(gridBagLayout);
+		
+		JLabel selectionFilterLabel = new JLabel("Sound Type:");
+		GridBagConstraints gbc_selectionFilterLabel = new GridBagConstraints();
+		gbc_selectionFilterLabel.anchor = GridBagConstraints.WEST;
+		gbc_selectionFilterLabel.insets = new Insets(0, 0, 5, 5);
+		gbc_selectionFilterLabel.gridx = 1;
+		gbc_selectionFilterLabel.gridy = 1;
+		add(selectionFilterLabel, gbc_selectionFilterLabel);
+		
+		/* prefs are used to initialize the spinners and the grain ugens */
+		Preferences prefs = Preferences.userNodeForPackage(this.getClass());
+		
+		soundComboBox = new JComboBox<>(SOUND_TYPES);
+		soundComboBox.setSelectedItem(prefs.get(keys[1], SOUND_TYPES[0].toString()));
+		GridBagConstraints gbc_soundComboBox = new GridBagConstraints();
+		gbc_soundComboBox.fill = GridBagConstraints.HORIZONTAL;
+		gbc_soundComboBox.insets = new Insets(0, 0, 5, 5);
+		gbc_soundComboBox.gridx = 3;
+		gbc_soundComboBox.gridy = 1;
+		add(soundComboBox, gbc_soundComboBox);
+		
+		JLabel borderThreshLabel = new JLabel("Grid Type :"); // FIXME boudles 
+		GridBagConstraints gbc_borderThreshLabel = new GridBagConstraints();
+		gbc_borderThreshLabel.anchor = GridBagConstraints.WEST;
+		gbc_borderThreshLabel.insets = new Insets(0, 0, 5, 5);
+		gbc_borderThreshLabel.gridx = 1;
+		gbc_borderThreshLabel.gridy = 2;
+		add(borderThreshLabel, gbc_borderThreshLabel);
+		
+		gridComboBox = new JComboBox<>(GRID_TYPES);
+		gridComboBox.setSelectedItem(prefs.get(keys[1], GRID_TYPES[0]));
+		GridBagConstraints gbc_gridComboBox = new GridBagConstraints();
+		gbc_gridComboBox.insets = new Insets(0, 0, 5, 5);
+		gbc_gridComboBox.fill = GridBagConstraints.HORIZONTAL;
+		gbc_gridComboBox.gridx = 3;
+		gbc_gridComboBox.gridy = 2;
+		add(gridComboBox, gbc_gridComboBox);
+		
+		/* sets all the strings for the screen reader  */
+		configureAccessibleContext();
+	}
+
+	
+	private void configureAccessibleContext(){
+	}
+	
+	@Override
+	public void savePrefs() {
+		Preferences prefs = Preferences.userNodeForPackage(this.getClass());
+		
+		prefs.put(getPrefsList()[0], soundComboBox.getItemAt(soundComboBox.getSelectedIndex()).toString());
+		prefs.put(getPrefsList()[1], gridComboBox.getItemAt(gridComboBox.getSelectedIndex()));
+				
+	}
+	
+	public Preferences getPrefs(){
+		Preferences prefs = Preferences.userNodeForPackage(this.getClass());
+		
+		prefs.put(getPrefsList()[0], prefs.get(getPrefsList()[0], SOUND_TYPES[0].toString()));
+		prefs.put(getPrefsList()[1], prefs.get(getPrefsList()[1], GRID_TYPES[0]));
+				
+		return prefs;
+	}
+	
+	@Override
+	public String getTitle(){
+		return "Haptics";
+	}
+
+	@Override
+	public String[] getPrefsList() {
+		return keys;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/referencesound/BeadsSequenceMapping.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,315 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.referencesound;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.Bead;
+import net.beadsproject.beads.ugens.Gain;
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.Sequence.Value;
+import uk.ac.qmul.eecs.depic.patterns.SequenceMapping;
+
+/**
+ * Implements the sequence mapping using the Beads sound engine
+ * 
+ * 
+ *
+ */
+abstract class BeadsSequenceMapping implements SequenceMapping {
+	public static final float DEFAULT_VALUE_LEN_MS = 50.0f; 
+	public static final float DEFAULT_VALUE_REF_LEN_MS = 50.0f; 
+	public static final float DEFAULT_VALUE_AMP = .5f; 
+	public static boolean verboseMode = true;
+	
+	protected static final float RATE_STEP = 0.1f; 
+	
+	protected AudioContext ac;
+	/* the master gain */
+	protected Gain masterGain;
+	
+	private PitchedUGen renderCurveAtUGen;
+	private Gain renderCurveGain;
+	private float renderValueLen;
+	private float renderValueRefLen;
+	private float renderValueAmp;
+	
+	private Bead lastTrail;
+	
+	
+	/**
+	 * 
+	 * @param ac an audio context. The audio context must be started outside this class 
+	 */
+	protected BeadsSequenceMapping(AudioContext ac){
+		this.ac = ac;
+		masterGain = new Gain(ac, 1, 1.0f);
+		ac.out.addInput(masterGain);
+		renderValueLen = DEFAULT_VALUE_LEN_MS;
+		renderValueRefLen = DEFAULT_VALUE_REF_LEN_MS;
+		
+		renderValueAmp = DEFAULT_VALUE_AMP;
+		
+	}
+	
+
+	/**
+	 * 
+	 * returns the ugen used to render a sequence value 
+	 * 
+	 * @return
+	 */
+	protected abstract PitchedUGen createRenderValueUGen();
+	
+	
+	/**
+	 * returns a 
+	 * @return
+	 */
+	protected abstract PitchedUGen createRenderCurveUGen();
+	
+	protected abstract Range<Float> getRenderCurvePitchSpan();
+	
+	protected abstract Range<Float> getRenderValuePitchSpan();
+	
+	public void setRenderValueRefLen(float renderValueRefLen) {
+		this.renderValueRefLen = renderValueRefLen;
+	}
+
+
+	public float getRenderValueRefLen() {
+		return renderValueRefLen;
+	}
+
+
+	public void setRenderValueLen(float milliseconds){
+		renderValueLen = milliseconds;
+	}
+	
+	public float getRenderValueLen(){
+		return renderValueLen;
+	}
+	
+	public float getRenderValueAmp() {
+		return renderValueAmp;
+	}
+
+	public void setRenderValueAmp(float renderValueAmp) {
+		this.renderValueAmp = renderValueAmp;
+	}
+
+	
+	@Override
+	public void renderValue(Value val) {
+		
+		if(lastTrail != null){
+			lastTrail.kill();
+			lastTrail = null;
+		}
+		
+		float v = val.getValue();
+		
+		Sequence sequence = val.getSequence();
+		Range<Float> range = sequence.getRange();
+		
+		/* Map the value to the pitch: */ 
+		MathUtils.Scale scale =  new MathUtils.Scale(
+				range, 
+				getRenderValuePitchSpan()
+				);
+		
+		float pitch = scale.exponential(v);
+		
+		PitchedUGen valuePlayer = createRenderValueUGen(); 
+		valuePlayer.setLen(renderValueAmp, renderValueLen);
+		valuePlayer.setPitch(pitch);  
+		
+		masterGain.addInput(valuePlayer);
+	}
+	
+	public void renderValueOneRef(Value val){
+		if(lastTrail != null){
+			lastTrail.kill();
+			lastTrail = null;
+		}
+		
+		Range<Float> r = val.getSequence().getRange();
+		float v = r.getStart() + (r.getEnd() - r.getStart())/2 ;
+		
+		Sequence sequence = val.getSequence();
+		Range<Float> range = sequence.getRange();
+		
+		/* Map the value to the pitch: */ 
+		MathUtils.Scale scale =  new MathUtils.Scale(
+				range, 
+				getRenderValuePitchSpan()
+				);
+		
+		float pitch = scale.exponential(v);
+		
+		PitchedUGen valuePlayer = createRenderValueUGen(); 
+		valuePlayer.setLen(renderValueAmp, renderValueLen);
+		valuePlayer.setPitch(pitch);  
+		
+		masterGain.addInput(valuePlayer);
+	}
+	
+	public void renderValueMulRef(Value val, float resolution) {
+		
+		if(lastTrail != null){
+			lastTrail.kill();
+			lastTrail = null;
+		}
+		
+		Range<Float> r = val.getSequence().getRange();
+		float reference = r.getStart() + (r.getEnd() - r.getStart())/2;
+		
+		float v = val.getValue();
+		Sequence sequence = val.getSequence();
+		Range<Float> range = sequence.getRange();
+
+		if(!rangeContains(range,v) || !rangeContains(range,reference)){
+			throw new IllegalArgumentException("One or more values not in range val:"+ v + " reference:" +reference);
+		}
+		
+		if(Float.compare(resolution, 0) <= 0){
+			throw new IllegalArgumentException("Resolution must be positive. resolution:"+resolution);
+		}
+		
+		/* if the step is bigger than the start and end make the step = end-start */
+		if(resolution > Math.abs(v-reference) ){
+			resolution = Math.abs(v-reference);
+		}
+		
+		/* Map the value to the pitch: */ 
+		MathUtils.Scale scale =  new MathUtils.Scale(
+				range, 
+				getRenderValuePitchSpan()
+			);
+		
+		final PitchedUGen valuePlayer = createRenderValueUGen();
+		
+		boolean onePitchOnly = true;
+		if(v > reference){ // scale from v down to reference 
+			/* goes all the way from v-resolution to the last value before reaching the 
+			 * reference, each step is of resolution  */
+			for(float i=v-resolution; i>=reference; i = i-resolution){
+				final float nextPitch = scale.exponential(i);
+
+				if(i == v-resolution){ // first run
+					valuePlayer.setPitch(nextPitch);
+				}else{
+					onePitchOnly = false;
+					valuePlayer.addLen(renderValueAmp, renderValueRefLen,new Bead(){
+						@Override
+						public void messageReceived(Bead bead){
+							valuePlayer.setPitch(nextPitch);
+						}
+					});
+				}
+			}
+			
+			/* add the last bit of envelope that will kill the UGen */
+			/* if the envelope has many pitches already used addLen which, if env is != null 
+			 * will bring it to 0. Otherwise use setLen that brings the envelope to 0 */
+			if(onePitchOnly){
+				valuePlayer.setLen(renderValueAmp, renderValueRefLen);
+			}else{
+				valuePlayer.addLen(renderValueAmp, renderValueRefLen);				
+			}
+			
+			lastTrail = valuePlayer;
+			masterGain.addInput(valuePlayer);
+		}else if(v < reference){ // scale from v up to reference
+			
+			for(float i=v+resolution; i<=reference; i = i+resolution){
+				final float nextPitch = scale.exponential(i);
+
+				if(i == v+resolution){ // first run
+					valuePlayer.setPitch(nextPitch);
+				}else{
+					onePitchOnly = false;
+					valuePlayer.addLen(renderValueAmp, renderValueRefLen,new Bead(){
+						@Override
+						public void messageReceived(Bead bead){
+							valuePlayer.setPitch(nextPitch);
+						}
+					});
+				}
+			}
+			
+			/* add the last bit of envelope that will kill the UGen */
+			/* if the envelope has many pitches already used addLen which, if env is != null 
+			 * will bring it to 0. Otherwise use setLen that brings the envelope to 0 */
+			if(onePitchOnly){
+				valuePlayer.setLen(renderValueAmp, renderValueRefLen);
+			}else{
+				valuePlayer.addLen(renderValueAmp, renderValueRefLen);				
+			}
+			
+			lastTrail = valuePlayer;
+			masterGain.addInput(valuePlayer);
+			
+		}
+	}
+	
+	private static boolean rangeContains(Range<Float> range, float f){
+		return (Float.compare(f, range.getStart()) >= 0 && Float.compare(f, range.getEnd()) <= 0); 
+	}
+	
+	@Override
+	public void renderCurveAt(Sequence sequence, float time, float duration){
+		/* negative value means stop now */
+		if(duration < 0.0f ){
+			if(renderCurveAtUGen != null)
+				renderCurveAtUGen.kill();
+			renderCurveAtUGen = null;
+			return;
+		}		
+		
+		/* look for the two sequence values, whose times contain time in between */
+		float valueAtTime = new MathUtils.Interpolate(sequence).linear(time);
+		float pitch = new MathUtils.Scale(sequence.getRange(),getRenderCurvePitchSpan()).linear(valueAtTime);
+		
+		/* infinite duration means play until renderCurveAt is called again with duration = -1 */
+		if(Float.isInfinite(duration)){
+			if(renderCurveAtUGen == null){
+				renderCurveAtUGen = createRenderCurveUGen();
+				renderCurveAtUGen.setPitch(pitch);
+				masterGain.addInput(renderCurveAtUGen);
+				renderCurveAtUGen.start();
+			}else{
+				renderCurveAtUGen.setPitch(pitch);
+			}
+		}else if(duration > 0.0f){
+				if(renderCurveAtUGen == null){
+					renderCurveAtUGen = createRenderCurveUGen();
+					renderCurveAtUGen.setPitch(pitch);
+					renderCurveAtUGen.setLen(duration*0.8f, duration*0.2f);
+					ac.out.addInput(renderCurveAtUGen);
+					renderCurveAtUGen.start();
+				}else{
+					renderCurveAtUGen.setLen(duration*0.8f, duration*0.2f);
+				}
+		}
+	}
+	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/referencesound/BeadsWaveTableSequenceMapping.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,199 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.referencesound;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.Bead;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.data.Buffer;
+import net.beadsproject.beads.ugens.Envelope;
+import net.beadsproject.beads.ugens.Gain;
+import net.beadsproject.beads.ugens.WavePlayer;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+
+/**
+ * Implements a sequence mapping using beads engine and simple wave tables ugens 
+ * 
+ *
+ */
+class BeadsWaveTableSequenceMapping extends BeadsSequenceMapping { 
+	private Range<Float> renderCurvePitchSpan;
+	private Range<Float> renderValuePitchSpan;
+	private static final float ATTACK_TIME_MS = 5.0f;
+	
+	static {
+		/* loads the buffer for the PeakLevelPitchedUgen and stores it into staticBuffs */
+		int bufferLen = 4096;
+		Buffer pulse = new Buffer(bufferLen);
+		int pulseWidth = bufferLen/10;
+		
+		for(int i=0; i<pulseWidth;i++){
+			pulse.buf[i] = 1.0f;
+		}
+		
+		for(int i=pulseWidth; i<bufferLen; i++){
+			pulse.buf[i] = -1.0f;
+		}
+		
+		Buffer.staticBufs.put("Pulse", pulse);
+	}
+
+	public BeadsWaveTableSequenceMapping(AudioContext ac, Range<Float> curvePitchSpan, Range<Float> valuePitchSpan){
+		super(ac);
+		renderCurvePitchSpan = curvePitchSpan;
+		renderValuePitchSpan = valuePitchSpan;
+	}
+	
+	@Override
+	protected PitchedUGen createRenderValueUGen(){
+		return new BufferPitchedUGen(ac, renderValuePitchSpan, Buffer.SINE);
+	}
+	
+	@Override
+	protected PitchedUGen createRenderCurveUGen(){
+		return new BufferPitchedUGen(ac, renderCurvePitchSpan, Buffer.SINE);
+	}
+	
+
+	@Override
+	protected Range<Float> getRenderValuePitchSpan() {
+		return renderValuePitchSpan;
+	}
+
+	@Override
+	protected Range<Float> getRenderCurvePitchSpan() {
+		return renderCurvePitchSpan;
+	}
+	
+	private class BufferPitchedUGen extends PitchedUGen {
+		private Gain gain;
+		private WavePlayer player;
+		private float maxFreq;
+		private float minFreq;
+		private Envelope env;
+		
+		/**
+		 * 
+		 * Pulse TriangularBuffer Square  Saw Noise Sine
+		 * 
+		 * @param ac
+		 * @param pitchSpan
+		 * @param buffer
+		 */
+		public BufferPitchedUGen(AudioContext ac, Range<Float> pitchSpan, Buffer buffer) {
+			super(ac);
+			if(maxFreq < minFreq){
+				throw new IllegalArgumentException("minFreq must be lower than maxFreq. minFreq: "+minFreq+" maxFreq:"+maxFreq );
+			}
+			
+			this.minFreq = pitchSpan.getStart();
+			this.maxFreq = pitchSpan.getEnd();
+			gain = new Gain(ac, 1, 0.5f);
+			player = new WavePlayer(ac,minFreq,buffer);
+			
+			gain.addInput(player);
+			
+			addToChainOutput(gain);
+		}
+		
+		@Override
+		public void setPitch(float pitch){
+			/* set the frequency for both the actual curve sound and the threshold sound */
+			player.setFrequency(pitch);
+			
+		}
+		
+		@Override
+		public void setPitch(UGen pitch) {
+			player.setFrequency(pitch);
+		}
+		
+		public float getFrequency(float freq){
+			return player.getFrequency();
+		}
+		
+		@Override
+		public void start(){
+			player.start();
+		}
+		
+			
+		@Override
+		public void setLen(float sustainValue, float decayTime){
+			/* if decayTime is long enough, smooth out the attack to avoid glitchy sound */
+			if(Float.compare(decayTime,ATTACK_TIME_MS) > 0){
+				env = new Envelope(context,0.0f); // starts from 0
+				env.addSegment(sustainValue,ATTACK_TIME_MS,2); // attack
+				env.addSegment(0.0f,decayTime-ATTACK_TIME_MS, this);  // decay
+			}else{
+				env = new Envelope(context,sustainValue);
+				env.addSegment(0.0f, decayTime, this); // decay
+			}
+			gain.setGain(env);
+		}
+		
+		@Override
+		public void addLen(float sustainValue, float duration, final Bead bead){
+			if(env == null){
+				if(Float.compare(duration,ATTACK_TIME_MS) > 0){
+					env = new Envelope(context,0.0f); // starts from 0
+					env.addSegment(sustainValue,ATTACK_TIME_MS,2); // attack
+					env.addSegment(sustainValue,duration-ATTACK_TIME_MS, bead);  // decay
+				}else{
+					env = new Envelope(context,sustainValue);
+					env.addSegment(0.0f, duration, bead); // decay
+				}			
+				gain.setGain(env);
+			}else{	
+				env.addSegment(sustainValue,duration, bead);
+			}
+			
+		}
+		
+		@Override
+		public void addLen(float sustainValue, float duration ){
+			if(env == null){
+				if(Float.compare(duration,ATTACK_TIME_MS) > 0){
+					env = new Envelope(context,0.0f); // starts from 0
+					env.addSegment(sustainValue,ATTACK_TIME_MS,2); // attack
+					env.addSegment(sustainValue,duration-ATTACK_TIME_MS, this);  // decay
+				}else{
+					env = new Envelope(context,sustainValue);
+					env.addSegment(sustainValue, duration, this); // decay
+				}			
+				gain.setGain(env);
+			}else{
+				env.addSegment(0.0f,duration, this);
+			}
+			
+		}
+		
+		@Override
+		public void messageReceived(Bead b){
+			kill();
+		}
+		
+	}
+
+	@Override
+	public void renderCurve(Sequence m, float startTime) {
+		throw new UnsupportedOperationException();
+	}
+};;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/referencesound/GridSound.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,140 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.referencesound;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import net.beadsproject.beads.core.AudioContext;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+import uk.ac.qmul.eecs.depic.patterns.Sequence;
+import uk.ac.qmul.eecs.depic.patterns.Sequence.Value;
+
+public abstract class GridSound  {
+	public static final long REFERENCE_DELAY_MS = 200; 
+	protected BeadsSequenceMapping mapping;
+	protected Timer timer;
+	public enum Type {
+		NONE("none"),
+		PITCH("pitch"),
+		PITCH_ONE_REF("pitch one ref"),
+		PITCH_MUL_REF("pitch mul ref");
+		
+		String toStr;
+		
+		Type(String s){
+			toStr = s;
+		}
+		
+		@Override
+		public String toString(){
+			return toStr;
+		}
+	}
+	
+	public static GridSound get(GridSound.Type type){
+		
+		GridSound newRepres;
+				
+		switch(type){
+		case NONE : newRepres = new None(); break;
+		case PITCH: newRepres = new Single(); break;
+		case PITCH_ONE_REF : newRepres = new Ref(); break;
+		case PITCH_MUL_REF : newRepres = new MulRef(); break;
+		default : throw new IllegalArgumentException("Type string not recognized: " + type);
+		}
+		
+		return newRepres;
+	}
+	
+	protected GridSound(){
+		AudioContext ac = new AudioContext();
+		ac.start();
+		mapping = new BeadsWaveTableSequenceMapping(ac, new Range<Float>(0.0f,0.0f),new Range<Float>(120.0f,5000.0f));
+		timer = new Timer("Ref Point Timer");
+	}
+	
+	public abstract void sound(Sequence.Value val);
+	
+	public abstract void ref(Sequence.Value val);
+	
+	
+	private static class None extends GridSound {
+
+		@Override
+		public void sound(Value val) {
+			// does nothing
+		}
+
+		@Override
+		public void ref(Value val) {
+			// does nothing
+		}
+	}
+	
+	private static class Single extends GridSound {
+		
+		float previousVal = Float.NaN;
+		
+		@Override
+		public void sound(Value val) {
+			if(Float.compare(val.getValue(), previousVal) != 0){
+				mapping.renderValue(val);
+				previousVal = val.getValue();
+			};
+		}
+
+		@Override
+		public void ref(Value val) {
+			//does nothing
+		}
+	}
+	
+	private static class Ref extends GridSound {
+		protected TimerTask lastTimerTask = new TimerTask(){public void run(){}};
+		@Override
+		public void sound(final Value val) {
+			mapping.renderValue(val);
+			
+			lastTimerTask.cancel();
+			
+			lastTimerTask = new TimerTask(){
+				
+				@Override
+				public void run() {
+					ref(val);
+				}	
+			};
+			timer.schedule(lastTimerTask, REFERENCE_DELAY_MS);
+		}
+
+		@Override
+		public void ref(Value val) {
+			mapping.renderValueOneRef(val);		
+		}
+	}
+	
+	private static class MulRef extends Ref {		
+
+		@Override
+		public void ref(Value val) {
+			mapping.renderValueMulRef(val,  /*resolution =*/ 1.0f);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/daw/referencesound/PitchedUGen.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,49 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.referencesound;
+
+import net.beadsproject.beads.core.AudioContext;
+import net.beadsproject.beads.core.Bead;
+import net.beadsproject.beads.core.UGen;
+import net.beadsproject.beads.core.UGenChain;
+
+abstract class PitchedUGen extends UGenChain {
+	
+	public PitchedUGen(AudioContext ac) {
+		super(ac, 0, 2);
+	}
+
+	public abstract void setPitch(float pitch);
+	
+	public abstract void setPitch(UGen pitch); 
+	
+	/**
+	 * 
+	 * Kills the UGen when decayTime has elapsed
+	 * 
+	 * @param sustain
+	 * @param decay
+	 */
+	public abstract void setLen(float sustain, float decayTime);
+	
+	public abstract void addLen(float sustain, float decayTime, Bead bead);
+	
+	public abstract void addLen(float sustain, float decayTime);
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/patterns/MathUtils.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,151 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.patterns;
+
+
+public class MathUtils {
+	
+	public static boolean equal(float a, float b){
+		return Float.compare(a, b) == 0;
+	}
+	
+	public static boolean equal(double a, double b){
+		return Double.compare(a, b) == 0 ;
+		
+	}
+	
+	public static boolean equal(float a, float b , float epsilon){
+		return Math.abs(a-b) < epsilon;
+	}
+	
+	public static boolean equal(double a, double b , double epsilon){
+		return Math.abs(a-b) < epsilon;
+	}
+
+	
+	/**
+	 * 
+	 * @param amp amplitude value in the normalized form (ranging from 0 to 1)
+	 * 
+	 * @return the decibel conversion of {@code amp}
+	 */
+	public static float toDb(float amp){
+		if(Float.compare(amp,0) < 0){
+			throw new RuntimeException();
+		}
+		
+		return (float)(20.0 * Math.log10(amp));
+	}
+	
+	public static float toAmp(float db){
+		return (float) (Math.pow(10, db/ 20.0));
+	}
+	
+	
+	public static class Scale {
+		private Range<java.lang.Float> from;
+		private Range<java.lang.Float> to;
+	
+		public Scale(Range<java.lang.Float> rangeFrom, Range<java.lang.Float> rangeTo) {
+			this.from = rangeFrom;
+			this.to = rangeTo;
+		}
+		
+		public Scale(float startFrom, float endFrom,float startTo, float endTo) {
+			this(new Range<java.lang.Float>(startFrom,endFrom),new Range<java.lang.Float>(startTo,endTo));
+		}
+
+		public float linear(float num){
+			if(Float.compare(num, from.getStart()) < 0 || Float.compare(num,from.getEnd()) > 0)
+				throw new IllegalArgumentException("num must be in rangeFrom interval. num:"+num+" rangeFrom:"+from);
+			
+			float ratio = (num - from.getStart()) / from.lenght();
+			
+			return to.getStart() + (ratio * to.lenght());
+		}
+		
+		public float exponential(float num){
+			if(Float.compare(num, from.getStart()) < 0 || Float.compare(num,from.getEnd()) > 0)
+				throw new IllegalArgumentException("num must be in rangeFrom interval. num:"+num+" rangeFrom:"+from);
+			
+			if(Float.compare(to.getStart(),0) == 0 )
+				throw new IllegalArgumentException("Cannot call esponential when destination range starts from 0");
+			
+			num = (num - from.getStart())/ from.lenght();
+			
+			float toRatio = to.getEnd() / to.getStart();
+			return (float) (Math.pow(toRatio,num) * to.getStart());
+		}
+	}
+	
+	/**
+	 * 
+	 * Calculates values at any time in a sequence by interpolating the sequence values.
+	 * 
+	 * A sequence is a finite series of float values at given times. In order to get values 
+	 * at any times, interpolation is necessary. This class can be useful to build a graph out of 
+	 * the sequence values. Of course, as the interpolation changes, the graph changes as well, as 
+	 * values at times different from those specified by the sequence will be calculated differently.
+	 *
+	 */
+	public static class Interpolate {
+		private Sequence sequence;
+		
+		public Interpolate(Sequence sequence){
+			this.sequence = sequence;
+		}
+		
+		public float linear(float time){
+			/* initialise with start and end of the sequence */
+			float startValue = sequence.getBegin();
+			float endValue = sequence.getEnd();
+			float startTimePos = 0;
+			float endTimePos = sequence.getLen();
+			
+			
+			for(int i=0; i<sequence.getValuesNum(); i++){
+				Sequence.Value sv = sequence.getValueAt(i); 
+				
+				if(Float.compare(time, sv.getTimePosition())  >= 0){
+					startValue = sv.getValue();
+					startTimePos = sv.getTimePosition();
+				}
+				
+				if(Float.compare(time, sv.getTimePosition()) <= 0){
+					endValue = sv.getValue();
+					endTimePos = sv.getTimePosition();
+					break; // can break because values are ordered by time 
+				}
+			}
+				
+			/* let D be the distance between time and start, then ratio * 
+			 * is the ratio between D and the start-end interval length *
+			 * if startTimepos and endtimePos the ratio is going to be 0*/
+			float ratio = MathUtils.equal(startTimePos, endTimePos) ? 0.0f : ((time - startTimePos) / (endTimePos-startTimePos));
+			
+			
+			/* this comes from the following formula:
+			 * valueAtTime = start.getValue + (ratio * (end.getValue() - start.getValue()) */
+			float valueAtTime = (1.0f - ratio) * startValue + ratio * endValue;
+			
+			return valueAtTime;
+		}
+		
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/patterns/Range.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,87 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.patterns;
+
+/**
+ * 
+ * Immutable class containing two comparable number which represent the start and the end of a range.
+ *
+ * @param <T> 
+ */
+ public class Range<T extends Number & Comparable<T> > {	
+		protected T start;
+		protected T end;
+		
+		public Range(T t1, T t2){
+			if(t1.compareTo(t2) < 1){
+				start = t1;
+				end = t2;
+			}else{
+				start = t2;
+				end = t1;
+			}
+		}
+		
+		/**
+		 * Creates a new instance by comparing {@code mm1} and {@code mm2}.
+		 * 
+		 * The minimum of this object will be the minimum value between {@code mm1.getMin()} and {@code mm2.getMin()}.
+		 * The maximum of this object will be the maximum value between {@code mm1.getMax()} and {@code mm2.getMax()}.
+		 *  
+		 * @param r1 the former Range whose min and max are to be compared
+		 * @param r2 the latter Range whose min and max are to be compared
+		 */
+		public Range(Range<T> r1, Range<T> r2){
+			if(r1.getStart().compareTo(r2.getStart()) < 0){ // if the minimum of mm1 is less than the minimum of mm2
+				start = r1.getStart();						  //   then min is the minimum of mm1
+			}else{										  // else
+				start = r2.getStart();						  //   min is the minimum of mm2
+			}
+			
+			if(r1.getEnd().compareTo(r2.getEnd()) > 0){ // if the maximum of mm1 is greater than the maximum of mm2
+				end = r1.getEnd();						  //   then max is the maximum of mm1
+			}else{										  // else
+				end = r2.getEnd();						  //   max is the maximum of mm2
+			}
+		}
+		
+		protected Range(){
+			// empty range 
+		}
+		
+		public T getStart() {
+			return start;
+		}
+		public T getEnd() {
+			return end;
+		}
+		
+		public float lenght(){
+			return getEnd().floatValue() - getStart().floatValue();
+		}
+		
+		@Override
+		public String toString(){
+			return "Range ["+ getStart()+","+getEnd()+"]";
+		}
+		
+		public static final Range<Float> NORMALIZED_RANGE_F = new Range<>(0.0f,1.0f);
+		public static final Range<Double> NORMALIZED_RANGE_D = new Range<>(0.0,1.0);
+	}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/patterns/Sequence.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,62 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.patterns;
+
+/**
+ * 
+ * A sequence of values arranged over time.
+ * 
+ * The values are ordered by time, therefore given @ {@code i} and {@code j} such that {@code (i < j)}, the following expression is always true :  
+ * {@code sequence.getValueAt(i).getTimePosition() <= sequence.getValueAt(j).getTimePosition()}
+ * 
+ * The values are limited in range. {@code getRange()) returns the range 
+ * of float values that a sequence value can assume.  
+ *
+ */
+public interface Sequence {
+	public float getBegin();
+	
+	public float getEnd();
+	
+	public int getValuesNum();
+	
+	public Value getValueAt(int index);
+	
+	public Range<Float> getRange();
+	
+	public void addSequenceListener(SequenceListener l);
+	
+	public void removeSequenceListener(SequenceListener l);
+	
+	/**
+	 * The length of the sequence in millisecons 
+	 * @return
+	 */
+	public float getLen();
+	
+	public interface Value  {
+		public float getValue();
+		
+		public float getTimePosition();
+		
+		public Sequence getSequence();
+		
+		public int index();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/patterns/SequenceEvent.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,61 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.patterns;
+
+import java.util.EventObject;
+
+
+public class SequenceEvent extends EventObject{
+	private static final long serialVersionUID = 1L;
+
+	public enum What {
+		VALUE_ADDED,
+		VALUE_REMOVED,
+		VALUE_CHANGED,
+		END_CHANGED,
+		BEGIN_CHANGED
+	};
+	private Sequence.Value value;
+	private SequenceEvent.What what;
+	
+	public SequenceEvent(Sequence seq,Sequence.Value val, What what){
+		super(seq);
+		this.value = val;
+		this.what = what;
+	}
+	
+	public What getWhat(){
+		return what;
+	}
+	
+	@Override
+	public Sequence getSource(){
+		return (Sequence)super.getSource();
+	}
+	
+	/**
+	 * If the event concerns a particular value, it returns a reference to that value.
+	 * If the event concerns the whole sequence it returns {@code null}.
+	 * 
+	 * @return a reference to the value, or {@code null} if the event concerns no value 
+	 */
+	public Sequence.Value getValue(){
+		return value;
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/patterns/SequenceListener.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,33 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.patterns;
+
+public interface SequenceListener {
+	/**
+	 * This method has to be implemented by classes that provide a continuous 
+	 * representation of the sequence. 
+	 * 
+	 * The method a callback that is called as soon as one of the values of the the 
+	 * sequence is added, deleted or modified. Implementors can thus promptly update their representation 
+	 * to reflect the changes in the sequence.
+	 * 
+	 * @param t an event holding information about the sequence that has been modfied. 
+	 */
+	public <T extends SequenceEvent> void sequenceUpdated(T t);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/uk/ac/qmul/eecs/depic/patterns/SequenceMapping.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,61 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.patterns;
+
+/**
+ * 
+ * A mapping of a sequence into some form of representation, e.g. visual, audio, audio-visual etc.  
+ * 
+ * The methods of this interface represent different ways a sequence can be rendered and 
+ * need not be all implemented in classes implementing this interface.    
+ *
+ */
+public interface SequenceMapping {
+	
+	public float DURATION_INF = Float.POSITIVE_INFINITY;
+	public float DURATION_STOP = -1.0f;
+	
+	public void renderValue(Sequence.Value val);
+	
+	/**
+	 * 
+	 * @param m
+	 * @param startTime the time in millisecond the curve rendering has to start from, if 
+	 * -1 will immediately stop the rendering.    
+	 */
+	public void renderCurve(Sequence m, float startTime);
+	
+	/**
+	 * 
+	 * Render a curve at precise time in sound. 
+	 * 
+	 * The sound lasts {@code duration} milliseconds and then fades out. 
+	 * If {@code INF} is passed as duration the sound will go on forever. Successive calls with {@code INF} as duration
+	 * will have the effect of changing the sound in order to represent the curve at the new {@code time} passed as argument.
+	 * Successive calls with {@code duration} different from {@code DURATION_INF} will carry on the sound for {@code duration} 
+	 * millisecond (and therefore setting {@code duration} to 0 will stop the sound).
+	 *  
+	 * @param sequence the sequence to render
+	 * @param time the time at which the sequence has to be rendered 
+	 * @param duration the duration of sound in millisecond. If (@code DURATION_INF) is used the sound 
+	 * will go on forever until it's stopped. To stop the sound use {@code duration = -1}; 
+	 */
+	public void renderCurveAt(Sequence sequence, float time, float duration);
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/AllTests.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,35 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({ 
+	SoundWaveChunkTest.class, 
+	//AudioTrackTest.class, 
+	ClipListTest.class,
+	ClipTest.class,
+	MathUtilsTest.class,
+	SoundWaveChunkTest.class})
+public class AllTests {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/AudioFileLoader.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,125 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.swing.SwingUtilities;
+
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+
+public class AudioFileLoader {
+	private static boolean swingIsDone; 
+	
+	public static void loadFile(final SoundWave wave, final String resourcePath){
+		long mill = System.currentTimeMillis();
+		swingIsDone = false;
+		try {
+			SwingUtilities.invokeAndWait(new Runnable(){
+				@Override
+				public void run() {
+					URI fileURI = null;
+					try {
+						fileURI = getClass().getResource(resourcePath).toURI();
+					} catch (URISyntaxException e) {
+						new RuntimeException(e.getMessage()).printStackTrace();
+						System.exit(-1);
+					}
+					wave.loadAudioData(new File(fileURI), new PropertyChangeListener(){
+						@Override
+						public void propertyChange(PropertyChangeEvent evt) {
+							if("progress".equals(evt.getPropertyName())){
+								if((Integer)evt.getNewValue() == SoundWave.FILE_LOAD_TOTAL_PROGRESS){
+									synchronized(wave){
+										swingIsDone = true;
+										wave.notify();
+									}
+								}
+							}else if(SoundWave.FILE_ERROR_PROPERTY.equals(evt.getPropertyName())){
+								new RuntimeException("could not open file:"+evt.getNewValue()).printStackTrace();
+								System.exit(-1);
+							}
+						}
+					});
+				}
+			});
+		} catch (InvocationTargetException | InterruptedException e) {
+			e.printStackTrace();
+			System.exit(-1);
+		}
+		
+		/* wait for the swing thread to finish. The swing thread will be finished when propertyChange with progress =  *
+		 * SoundWave.FILE_LOAD_TOTAL_PROGRESS will be scheduled for execution in the EDT, by the swing worker          */
+		synchronized(wave){
+			while(!swingIsDone)
+				try {
+					wave.wait();
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+					System.exit(-1);
+				}
+		}
+		System.out.println("File loaded in "+(System.currentTimeMillis()-mill)+" milliseconds");
+		
+	}
+	
+	public static byte[] getAudioBuffer(URL fileURL){
+		AudioInputStream inputStream = null;
+ 		try {
+			AudioInputStream originalInputStream = AudioSystem.getAudioInputStream(fileURL);
+ 			AudioFormat format = originalInputStream.getFormat();
+ 			/* one byte format to check for silence in the wav file without risk of error from audio formats */
+ 			AudioFormat newformat  = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
+ 					format.getSampleRate(),
+ 					8,
+ 					1,
+ 					1,
+ 					format.getFrameRate(),
+ 					true // big endian
+ 					);
+ 			inputStream = AudioSystem.getAudioInputStream(newformat,originalInputStream);
+			byte[] buffer = new byte[1024 * newformat.getFrameSize()];
+			ByteArrayOutputStream out = new ByteArrayOutputStream();
+			int numBytesRead = 0;
+			while((numBytesRead = inputStream.read(buffer)) != -1){
+				out.write(buffer, 0, numBytesRead);
+			}
+			inputStream.close();
+			return out.toByteArray();
+			
+		} catch (UnsupportedAudioFileException | IOException e) {
+			e.printStackTrace();
+			System.exit(-1);
+		}
+ 		
+		return null;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/AudioTrackTest.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,143 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import org.junit.Test;
+
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEvent;
+import uk.ac.qmul.eecs.depic.daw.beads.BeadsSoundEngineFactory;
+import uk.ac.qmul.eecs.depic.daw.gui.AudioTrack;
+
+public class AudioTrackTest {
+	private AudioTrack track;
+	private SoundWaveEvent evt;
+	private CursorPositionChecker checker;
+	
+	public AudioTrackTest(){
+		SoundWave wave = new BeadsSoundEngineFactory()
+			.setSoundWaveParameters(128,10,5)
+			.createSoundWave(); // scaleFactor ranges from 1 to 10 
+		AudioFileLoader.loadFile(wave, "./audio/sound1.wav"); // need to load file to avoid null pointers
+		/* checker's expected value is the assert expected value */
+		checker = new CursorPositionChecker(); 
+		track = new AudioTrack(wave);		
+		evt = new SoundWaveEvent(wave,SoundWaveEvent.POSITION_CHANGED);
+	}
+
+	@Test
+	public void testPositionUpdate() {
+		checker.setExpectedValue(0);
+		// regardless of scale factor 0 will always map to 0
+		for(int i = 1; i<=10; i++){
+			resetCursorPos(-1);
+			track.setScaleFactor(i); 
+			evt.setArgs(0);
+			track.update(evt);
+		}
+		
+		/* track scale factor = 3 */
+		track.removePropertyChangeListener(checker);
+		track.setScaleFactor(3);
+		track.addPropertyChangeListener(checker);
+
+		resetCursorPos(-1);
+		checker.setExpectedValue(0);
+		evt.setArgs(1);
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(0);
+		evt.setArgs(3);
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(1);
+		evt.setArgs(4);
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(1);
+		evt.setArgs(5);
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(1);
+		evt.setArgs(7);
+		track.update(evt);
+		
+		/* track scale factor = 1*/
+		track.removePropertyChangeListener(checker);
+		track.setScaleFactor(1);
+		track.addPropertyChangeListener(checker);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(0);
+		evt.setArgs(new Selection(0,3));
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(4);
+		evt.setArgs(new Selection(1,3));
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(8);
+		evt.setArgs(new Selection(2,3));
+		track.update(evt);
+		
+		resetCursorPos(-1);
+		checker.setExpectedValue(12);
+		evt.setArgs(new Selection(3,3));
+		track.update(evt);
+		
+	}
+	
+	private void resetCursorPos(int pos){
+		track.removePropertyChangeListener(checker); // unregister the listener in order to ignore next instruction
+		track.setCursorPos(pos); // makes the previous value different in order to get the propertychange event triggered 
+		track.addPropertyChangeListener(checker);
+	}
+	
+	/*
+	 * Listens to "cursorPos" AudioTrack bounded property and perform a check on it 
+	 * against the expected value
+	 */
+	private class CursorPositionChecker implements PropertyChangeListener {
+		int expectedValue;
+		
+		@Override
+		public void propertyChange(PropertyChangeEvent evt) {
+			if("cursorPos".equals(evt.getPropertyName())){
+				assertEquals(expectedValue,((Integer)evt.getNewValue()).intValue());
+			}
+		}
+		
+		/* checker's expected value is the assert expected value */
+		public void setExpectedValue(int value){
+			expectedValue = value;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/ClipListTest.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,90 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+
+import net.beadsproject.beads.data.SampleManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.ac.qmul.eecs.depic.daw.Clip;
+import uk.ac.qmul.eecs.depic.daw.ClipList;
+import uk.ac.qmul.eecs.depic.daw.Sample;
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.WavePeaks;
+import uk.ac.qmul.eecs.depic.daw.beads.BeadsSampleWrapper;
+
+public class ClipListTest {
+	Sample sample1;
+	Sample sample2;
+	ClipList clipList;
+	SoundWave wave = new DummySoundWave();
+
+
+	@Before
+	public void setUp() throws Exception {
+		sample1 = new BeadsSampleWrapper(SampleManager.sample(getClass().getResourceAsStream("./audio/sound1.wav")));
+		sample2 = new BeadsSampleWrapper(SampleManager.sample(getClass().getResourceAsStream("./audio/sound2.wav")));
+		clipList = new ClipList(1, new HashMap<Sample,WavePeaks> (), wave);
+		
+	}
+
+	@After
+	public void tearDown() throws Exception {
+	}
+	
+	@Test
+	public void testOrder() {
+		
+		/* insert in reverse order */
+		for(int i = 99; i>=0; i--){
+			clipList.add(new Clip(i,300,sample1,0,64.0f));
+		}
+		
+		/* check if the order is correct */
+		for(int i=0; i<100; i++)
+			assertEquals(i,clipList.get(i).getStart().intValue());
+		
+		/* remove first ten items and check if the order is mantained */
+		for(int i=1;i<=10;i++){
+			clipList.remove(0);
+			assertEquals(i,clipList.get(0).getStart().intValue());
+		}
+		
+		/*add another item at beginning and check if the order is mantained */
+		clipList.add(new Clip(0,300,sample1,0,64.0f));
+		assertEquals(0,clipList.get(0).getStart().intValue());
+	}
+	
+	@Test
+	public void testCut(){
+		/* cut on a [n,n] selection has no effect */
+		clipList.add(new Clip(0,300,sample1,0,64.0f));
+		clipList.getClipEditor().cut(wave, new Selection(5,5,clipList.getScaleFactor()));
+		assertEquals(1,clipList.size());
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/ClipTest.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,92 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import static org.junit.Assert.*;
+
+import net.beadsproject.beads.data.SampleManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.ac.qmul.eecs.depic.daw.Clip;
+import uk.ac.qmul.eecs.depic.daw.Sample;
+import uk.ac.qmul.eecs.depic.daw.beads.BeadsSampleWrapper;
+
+public class ClipTest {
+	Sample sample;
+	
+	@Before
+	public void setUp() throws Exception {
+		sample = new BeadsSampleWrapper(SampleManager.sample(getClass().getResourceAsStream("./audio/sound1.wav")));
+	}
+
+	@Test
+	public void testSplit() {
+		Clip clip = new Clip(0,100,sample,0,64.0f);
+
+		/* splits outside edges of selection */
+		assertEquals(1,clip.split(-1).length);
+		assertEquals(2,clip.split(1).length);
+		assertEquals(2,clip.split(100).length);
+		assertEquals(1,clip.split(101).length);
+
+		{
+			Clip [] split = clip.split(30);
+			assertEquals(2,split.length);
+			
+			assertEquals(0,split[0].getStart().intValue());
+			assertEquals(29,split[0].getEnd().intValue());
+			assertEquals(100,split[1].getEnd().intValue());
+			assertEquals(30,split[1].getStart().intValue());
+		}
+		{
+			/* split at edges of selection */
+			Clip [] split = clip.split(0);
+			assertEquals(1,split.length);
+			assertEquals(0,split[0].getStart().intValue());
+			assertEquals(100,split[0].getEnd().intValue());
+			
+		}
+		
+		{
+			/* split at edges of selection */
+			Clip [] split = clip.split(100);
+			assertEquals(2,split.length);
+			assertEquals(0,split[0].getStart().intValue());
+			assertEquals(99,split[0].getEnd().intValue());
+			assertEquals(100,split[1].getStart().intValue());
+			assertEquals(100,split[1].getEnd().intValue());
+		}
+	}
+
+
+	@Test
+	public void testJoin(){
+		Clip clip = new Clip(0,100,sample,0,64.0f);
+		
+		Clip [] split = clip.split(30);
+		
+		Clip joined = Clip.join(split[0], split[1]);
+		
+		assertNotNull(joined);
+		assertEquals(clip.getStart().intValue(),joined.getStart().intValue());
+		assertEquals(clip.getEnd().intValue(),joined.getEnd().intValue());
+	}	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/DummySoundWave.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,194 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.List;
+
+import javax.swing.JComponent;
+
+import uk.ac.qmul.eecs.depic.daw.Automation;
+import uk.ac.qmul.eecs.depic.daw.Chunk;
+import uk.ac.qmul.eecs.depic.daw.DbWave;
+import uk.ac.qmul.eecs.depic.daw.ParametersControl;
+import uk.ac.qmul.eecs.depic.daw.Selection;
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveEditor;
+import uk.ac.qmul.eecs.depic.daw.SoundWaveListener;
+
+public class DummySoundWave implements SoundWave {
+
+	public DummySoundWave() {
+		// TODO Auto-generated constructor stub
+	}
+
+	@Override
+	public void addSoundWaveListener(SoundWaveListener l) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void removeSoundWaveListener(SoundWaveListener l) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void loadAudioData(File audioFile,
+			PropertyChangeListener propertyChangelistener) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void stopLoading(boolean mayInterruptIfRunning) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void close() {
+		// TODO Auto-generated method stub
+
+	}
+
+
+	@Override
+	public SoundWave.TransportControl getTransportControl() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getCurrentChunkPosition() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public void scan(int position) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void setSelection(Selection selection) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void setPosition(int position) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void setScaleFactor(int scaleFactor) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public int getScaleFactor() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public float getMillisecPerChunk() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public List<Integer> getFramesPositionAt(int chuckPosition) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getChunkNum() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public Chunk getChunkAt(int i) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public float getWaveTime() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public int getMaxScaleFactor() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public SoundWaveEditor getEditor() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public JComponent[] getPreferencesPanels() {
+		// TODO Auto-generated method stub
+		return new JComponent [] {} ;
+	}
+
+	@Override
+	public Automation getSequence() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public DbWave getDbWave() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public void generatePeakMeter(boolean generate) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	@Override
+	public boolean hasSequence() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public ParametersControl getParametersControl() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/MathUtilsTest.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,66 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import uk.ac.qmul.eecs.depic.patterns.MathUtils;
+import uk.ac.qmul.eecs.depic.patterns.Range;
+
+public class MathUtilsTest {
+	private static float DELTA = 0.05f;
+
+	@Test
+	public void testLinearScale() {
+			MathUtils.Scale f = new MathUtils.Scale(new Range<Float>(-1.0f,1.0f),new Range<Float>(0.0f, 100.0f));
+			assertEquals(50.0f,f.linear(0.0f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(-1.0f,1.0f),new Range<Float>(-1.0f, 0.0f));
+			assertEquals(-0.5,f.linear(0.0f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(-1.0f,1.0f),new Range<Float>(0.0f, 0.0f));
+			assertEquals(0.0f,f.linear(0.0f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(-1.0f,1.0f),new Range<Float>(-100.0f, -100.0f));
+			assertEquals(-100.0f,f.linear(0.0f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(-1.0f,1.0f),new Range<Float>(-0.6f, -0.2f));
+			assertEquals(-0.4f,f.linear(0.0f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(-1.0f,0.0f),new Range<Float>(-150.0f, 100.0f));
+			assertEquals(37.5f,f.linear(-0.25f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(-1.0f,0.0f),new Range<Float>(0.0f, 1.0f));
+			assertEquals(0.75f,f.linear(-0.25f),DELTA);
+			f = new MathUtils.Scale(new Range<Float>(0.0f,2.0f),new Range<Float>(0.0f, 400.0f));
+			assertEquals(400.0f,f.linear(2.0f),DELTA);
+		
+	}
+	
+	@Test 
+	public void testAmpToDb (){
+		assertEquals(-6.0,MathUtils.toDb(0.5f),DELTA);
+		assertEquals(6.0,MathUtils.toDb(2.0f),DELTA);
+		assertEquals(-10.0,MathUtils.toDb(0.3162f),DELTA);
+		assertEquals(0.0f,MathUtils.toDb(1.0f),DELTA);
+		assertEquals(-60.0f, MathUtils.toDb(0.001f),DELTA);
+	
+		assertEquals(0.501f,MathUtils.toAmp(-6.0f),DELTA);
+		assertEquals(1.0f,MathUtils.toAmp(0.0f),DELTA);
+	}
+	
+	
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/uk/ac/qmul/eecs/depic/daw/test/SoundWaveChunkTest.java	Wed Aug 26 16:16:53 2015 +0100
@@ -0,0 +1,182 @@
+/*  
+ Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
+
+ Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
+	
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.depic.daw.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.swing.SwingUtilities;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import uk.ac.qmul.eecs.depic.daw.SoundWave;
+import uk.ac.qmul.eecs.depic.daw.beads.BeadsSoundEngineFactory;
+
+@SuppressWarnings("unused")
+public class SoundWaveChunkTest {
+	BeadsSoundEngineFactory factory;
+	SoundWave wave;
+	boolean swingIsDone;
+
+	@Before
+	public void setUp() throws Exception {
+		swingIsDone = false;
+		factory = new BeadsSoundEngineFactory();
+	}
+
+	@Test
+	public void testConstructor(){
+		int[] scaleFactors = new int[] {-1,0};
+		for(int scaleFactor : scaleFactors)
+			try {
+				
+				SoundWave w1 = factory
+						.setSoundWaveParameters(SoundWave.MIN_SUPPORTED_CHUNK_SIZE,scaleFactor,scaleFactor)
+						.createSoundWave();
+				fail();
+			}catch(RuntimeException e){
+				assertTrue(true);
+			}
+		
+		/* test chunks size < MIN_SUPPORTED_CHUNK_SIZE and not a power of 2*/
+		int[] chunkSizes = new int[] {-1,-16,0,10,100,SoundWave.MIN_SUPPORTED_CHUNK_SIZE+2};
+		for(int size : chunkSizes)
+			try {
+				SoundWave w2 = factory
+						.setSoundWaveParameters(size,1,1)
+						.createSoundWave();
+				fail("Size "+size+" allowed");
+			}catch(RuntimeException e){
+				assertTrue(true);
+			}
+		
+		/* check correct chunk size > MIN_SUPPORTED_CHUNK_SIZE and power of 2*/
+		chunkSizes = new int[] {SoundWave.MIN_SUPPORTED_CHUNK_SIZE,
+				SoundWave.MIN_SUPPORTED_CHUNK_SIZE*2,
+				SoundWave.MIN_SUPPORTED_CHUNK_SIZE*4};
+		for(int size : chunkSizes)
+			try {
+				factory.setSoundWaveParameters(size,1,1).createSoundWave();
+			}catch(RuntimeException e){
+				e.printStackTrace();
+				assertTrue(false);
+			}
+	}
+
+	@Test
+	public void testGetFrameAtChunk() {
+		/* 128 samples per chunk, scale factor = 1 and 2 */
+		wave = factory.setSoundWaveParameters(128,3,1).createSoundWave();
+		/* by-pass the final constrain using an array */
+		final AssertionError [] swingError = {null};
+		try {
+			SwingUtilities.invokeAndWait(new Runnable(){
+				@Override
+				public void run() {
+					URI fileURI = null;
+					try {
+						fileURI = getClass().getResource("./audio/sound1.wav").toURI();
+					} catch (URISyntaxException e) {
+						new RuntimeException(e.getMessage()).printStackTrace();
+						System.exit(-1);
+					}
+					wave.loadAudioData(new File(fileURI), new PropertyChangeListener(){
+						@Override
+						public void propertyChange(PropertyChangeEvent evt) {
+							if("progress".equals(evt.getPropertyName())){
+								/* only test when the file is fully loaded */
+								if((Integer)evt.getNewValue() == SoundWave.FILE_LOAD_TOTAL_PROGRESS){
+									try {
+										/* sound1.wav file is  44100Hz, 2 bytes per frame, mono   *
+										 * 0-1 and 7-8 seconds are silenced                 */
+										
+										byte[] audioData = AudioFileLoader.getAudioBuffer(getClass().getResource("./audio/sound1.wav")); 
+										/* first second @ 8000Hz is silence */
+										for(int i = 0; i * 128 < 8000; i++){
+											assertEquals("chunk "+i,0,audioData[(int)wave.getFramesPositionAt(i).get(0)]);
+											assertEquals("chunk "+i+" (scale = 2)",0,audioData[(int)wave.getFramesPositionAt(i).get(0)/2]);
+											assertEquals("chunk "+i+" (scale = 2)",0,audioData[(int)wave.getFramesPositionAt(i).get(0)/4]);
+										}
+										
+										/* (16min *44100)/128 = 5512.5             */
+										/* 1min * 44100 = 344 chunks and 68 frames */
+										/* 7th seconds @ 8000Hz is at (7 * 8000)/128 = 437 chunks + 64 frames */
+										/* check a neighbourhood of 10 samples  */
+										for(int i=10; i<20;i++){
+											wave.setScaleFactor(1);
+											assertEquals("chunk at second 7",0,audioData[(int)wave.getFramesPositionAt(437).get(0)+64+i]);
+											wave.setScaleFactor(2);
+											assertEquals("chunk at second 7 (scale = 2)",0,audioData[((int)wave.getFramesPositionAt(437).get(0)+64+i)/2]);
+											wave.setScaleFactor(3);
+											assertEquals("chunk at second 7 (scale = 3)",0,audioData[((int)wave.getFramesPositionAt(437).get(0)+64+i)/4]);
+										}
+										
+									}catch(AssertionError e){
+										swingError[0] = e;
+									}finally{ //System.out.println("just after assert Equals");
+										synchronized(wave){
+											swingIsDone = true;
+											wave.notify();
+										}
+									}
+								}
+							}else if(SoundWave.FILE_ERROR_PROPERTY.equals(evt.getPropertyName())){
+								new RuntimeException("could not open file:"+evt.getNewValue()).printStackTrace();
+								System.exit(-1);
+							}
+						}
+					});
+				}
+			});
+		} catch (InvocationTargetException | InterruptedException e) {
+			e.printStackTrace();
+			System.exit(-1);
+		}
+		
+		/* wait for the swing thread to finish. The swing thread will be finished when propertyChange with progress =  *
+		 * SoundWave.FILE_LOAD_TOTAL_PROGRESS will be scheduled for execution in the EDT, by the swing worker          */
+		synchronized(wave){
+			while(!swingIsDone)
+				try {
+					wave.wait();
+					/* throw the error in the JUnit thread so that it's counted */
+					if(swingError[0] != null){
+						assertTrue(swingError[0].getMessage(),false);
+					}
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+					System.exit(-1);
+				}
+		}
+		
+		
+	}
+
+	
+
+}
Binary file test/uk/ac/qmul/eecs/depic/daw/test/audio/sound1.wav has changed
Binary file test/uk/ac/qmul/eecs/depic/daw/test/audio/sound2.wav has changed