changeset 397:7a573750b186

- First add of a lot of graphics code from the old version. Not working yet, not even compiling yet.
author tomwalters
date Fri, 15 Oct 2010 05:40:53 +0000
parents 06a26f5cdad7
children 3ee03a6b95a0
files trunk/SConstruct trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevice.cc trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevice.h trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.cc trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.h trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.cc trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.h trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.cc trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.h trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicePlotutils.cc trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicePlotutils.h trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicewxGLCanvas.cc trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicewxGLCanvas.h trunk/src/Modules/Output/Graphics/GraphAxisSpec.cc trunk/src/Modules/Output/Graphics/GraphAxisSpec.h trunk/src/Modules/Output/Graphics/GraphicsView.cc trunk/src/Modules/Output/Graphics/GraphicsView.h trunk/src/Modules/Output/Graphics/GraphicsViewTime.cc trunk/src/Modules/Output/Graphics/GraphicsViewTime.h trunk/src/Modules/Output/Graphics/Scale/Scale.cc trunk/src/Modules/Output/Graphics/Scale/Scale.h trunk/src/Modules/Output/Graphics/Scale/ScaleERB.h trunk/src/Modules/Output/Graphics/Scale/ScaleLinear.h trunk/src/Modules/Output/Graphics/Scale/ScaleLog.h trunk/src/Modules/Output/Graphics/Scale/ScaleLogScaled.h trunk/src/Modules/SSI/ModuleSSI.cc trunk/src/Modules/SSI/ModuleSSI.h trunk/src/Support/Module.cc trunk/src/Support/SignalBank.h
diffstat 29 files changed, 3971 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/trunk/SConstruct	Wed Sep 29 00:24:03 2010 +0000
+++ b/trunk/SConstruct	Fri Oct 15 05:40:53 2010 +0000
@@ -51,6 +51,16 @@
                   'Modules/Output/FileOutputHTK.cc',
                   'Modules/Output/FileOutputAIMC.cc',
                   'Modules/Features/ModuleGaussians.cc']
+                  
+graphics_sources = [ 'Modules/Output/Graphics/GraphAxisSpec.cc',
+                     'Modules/Output/Graphics/GraphicsView.cc',
+                     'Modules/Output/Graphics/GraphicsViewTime.cc',
+                     'Modules/Output/Graphics/Devices/GraphicsOutputDevice.cc',
+                     'Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.cc',
+                     'Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.cc',
+                     'Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.cc' ]
+graphics_libraries = [ 'cairo', 
+                       '' ]
 
 # List of currently incative source files which we may want to add back in
 sources_disabled = ['Modules/SNR/ModuleNoise.cc',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevice.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,53 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "Support/Common.h"
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDevice.h"
+
+GraphicsOutputDevice::GraphicsOutputDevice(Parameters *pParam) {
+	m_pParam = pParam;
+}
+
+void GraphicsOutputDevice::gVertex3f(float x,
+                                     float y,
+                                     float z,
+                                     float r,
+                                     float g,
+                                     float b) {
+	gColor3f(r, g, b);
+	gVertex3f(x, y, z);
+}
+
+void GraphicsOutputDevice::gVertex2f(float x,
+                                     float y,
+                                     float r,
+                                     float g,
+                                     float b) {
+	gColor3f(r, g, b);
+	gVertex3f(x, y, 0);
+}
+
+void GraphicsOutputDevice::gVertex2f(float x, float y) {
+	gVertex3f(x, y, 0);
+}
+
+void GraphicsOutputDevice::gText2f(float x,
+                                   float y,
+                                   const char *sStr,
+                                   bool bRotated) {
+	gText3f(x, y, 0, sStr, bRotated);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevice.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,191 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __GRAPHICS_OUTPUT_DEVICE_H__
+#define __GRAPHICS_OUTPUT_DEVICE_H__
+
+#include "Support/Parameters.h"
+
+/*!
+ * \class GraphicsOutputDevice "Output/GraphicsOutputDevice.h"
+ * \brief General output device class
+ *
+ * This class provides basic drawing primitives in a device-independent manner.
+ * It is structured like the OpenGL interface. Following is an example drawing
+ * five horizontal lines, with the colour changing between red and blue over
+ * time.
+ * \code
+ *  GraphicsOutputDevice oOutput = static_cast<GraphicsOutputDevice>(new GraphicsOutputDeviceSomething());
+ *  unsigned int a=0;
+ *  ...
+ *  oOutput->Start();
+ *  while ( bIsRunning ) {
+ *		// Start a drawing operation
+ *		oOutput->gGrab();
+ *		// Draw five horizontal lines
+ *		for (int y=0; y<5; y++) {
+ *  		// Start a new line
+ *  		oOutput->gBegin();
+ *  		// Give each line it's own colour
+ *  		oOutput->gColor3f( (a%255)/255, 0, 1-(a%255)/255 );
+ *  		// Draw the line
+ *  		oOutput->gVertex2f(0, y);
+ *  		oOutput->gVertex2f(1, y);
+ *  		// End the line
+ *  		oOutput->gEnd();
+ *		}
+ *		oOutput->gRelease();
+ *		Sleep(1);
+ *		a++;
+ *  }
+ *  oOutput->Stop();
+ * \endcode
+ *
+ * This class includes some basic overloading definitions to ease the
+ * children's implementation. Note that the basic operations that need
+ * implementation are glVertex3f(x,y,z) and glColor(r,g,b).
+ */
+class GraphicsOutputDevice {
+ public:
+	GraphicsOutputDevice(AimParameters *pParam);
+	virtual ~GraphicsOutputDevice() { };
+
+	/*! \brief Initialize the module, sets up everything to Start().
+	 *  \return true on success, false on error
+	 *
+	 * 	Initialize() needs to be called before any other function.
+	 *
+	 *  This method is called in it's form as displayed here by the GraphicsView,
+	 *  but you may want to setup your own Initialize(...) function with
+	 *  different arguments and call it yourself.
+	 *
+	 *  Thus make sure you do all memory allocations here. They can be cleaned
+	 *  up by the destructor. Because Initialize() may fail, it's not put in
+	 *  the constructor, so it can return a value.
+	 *
+	 *  \sa Module::Initialize()
+	 */
+	virtual bool Initialize(unsigned int iVerticesMax) { return true; };
+	/*! \overload
+	 *  This function reloads the parameters; make sure to have at least the
+	 *  function with maximum parameters called once.
+	 */
+	virtual bool Initialize() { return true; };
+
+	/*! \brief Create a new drawing
+	 *  Run this before any other drawing command.
+	 *  \sa glRelease()
+	 */
+	virtual void gGrab() = 0;
+
+	//! \brief Start a new vertex group for drawing a line strip
+  virtual void gBeginLineStrip() = 0;
+  //! \brief Start a new vertex group for drawing a quad strip
+  virtual void gBeginQuadStrip() = 0;
+
+	/*! \brief Specify a vertex to draw
+	 *  \param[in] x X-coordinate of the vertex
+	 *  \param[in] y Y-coordinate of the vertex
+	 *  \param[in] z Z-coordinate of the vertex
+	 *  \param[in] r Red component of colour
+	 *  \param[in] g Green component of colour
+	 *  \param[in] b Blue component of colour
+	 *
+	 *  Currently, only lines are implemented.
+	 */
+  virtual void gVertex3f(float x, float y, float z, float r, float g, float b);
+
+	/*! \overload
+	 *  This will add a vertex with the last specified colour.
+	 */
+
+	virtual void gVertex3f(float x, float y, float z) = 0;
+	/*! \overload
+	 *  This will add a vertex in the 2d-plane with z=0.
+	 */
+
+	virtual void gVertex2f(float x, float y, float r, float g, float b);
+
+	/*! \overload
+	 *  This will add a vertex in the 2d-plane with z=0 with the last
+   *  specified colour.
+	 */
+	virtual void gVertex2f(float x, float y);
+
+	/*! \brief Sets the current colour
+	 *  \param[in] r Red component
+	 *  \param[in] g Green component
+	 *  \param[in] b Blue component
+	 */
+	virtual void gColor3f(float r, float g, float b) = 0;
+
+	//! \brief End a vertex group
+  virtual void gEnd() = 0;
+
+	/*! \brief Render a text string
+	 *  \param[in] x X-coordinate of the text's alignment point
+	 *  \param[in] y Y-coordinate of the text's alignment point
+	 *  \param[in] z Z-coordinate of the text's alignment point
+	 *  \param[in] sStr Text to render
+	 *  \param[in] bRotated \c true for vertically rotated text
+	 *
+	 *  Current alignment is horizontal:left and vertical:bottom
+	 *  \todo Allow multiple alignment points
+	 */
+	virtual void gText3f(float x,
+                       float y,
+                       float z,
+                       const char *sStr,
+                       bool bRotated = false) = 0;
+
+	/*! \overload
+	 *  This will render a text string in the 2d-plane with z=0.
+	 */
+	virtual void gText2f(float x,
+                      float y,
+                      const char *sStr,
+                      bool bRight = false);
+
+	/*! \brief Finish drawing
+	 *  Call this when a drawing is finished. It also makes sure that the
+	 *  rendering is actually done.
+	 *  \sa glGrab()
+	 */
+	virtual void gRelease() = 0;
+
+	/*! \brief Called when animation starts
+	 *
+	 *  You may wonder what Start() and Stop() do here. Some implementations
+	 *  may want to behave differently with respect to updating, if an
+	 *  animation is running or not (e.g. updating).
+	 */
+	virtual void Start() { m_bRunning = true; }
+
+	//! \brief Called when animation stops
+	virtual void Stop()  { m_bRunning = false; }
+
+ protected:
+	//! \brief True when animation is running
+	bool m_bRunning;
+	//! \brief Parameter store
+	AimParameters *m_pParam;
+
+	//! \brief Pixel Formats
+	enum PixelFormat {AIM_PIX_FMT_RGB24_32, AIM_PIX_FMT_RGB24_24};
+};
+
+#endif /* __GRAPHICS_OUTPUT_DEVICE__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,283 @@
+// Copyright 2007, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a graphics file using cairo (LGPL)
+ *
+ * \author Tom Walters <tom@acousticscale.org> and Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2007/09/17
+ * \version \$Header: $
+ */
+
+#include "Support/Common.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "Support/util.h"
+#include "Output/GraphicsOutputDeviceCairo.h"
+
+GraphicsOutputDeviceCairo::GraphicsOutputDeviceCairo(Parameters *pParam)
+	: GraphicsOutputDevice(pParam) {
+	m_bOutputFile = false;
+	m_iFileNumber = 0;
+	m_iVertexType = VertexTypeNone;
+	m_bUseMemoryBuffer=false;
+}
+
+bool GraphicsOutputDeviceCairo::Initialize(const char *sDir) {
+  Init();
+
+	//! \todo Output to file if sDir is a file, to directory with
+  //! multiple images if it's a directory.
+	strncpy(m_sDir, sDir, sizeof(m_sDir)/sizeof(m_sDir[0]));
+
+	/* Try to open an image to see if everything is allright. We want to avoid
+	 * errors in the main Process()ing loop. */
+	if ( !OpenFile(0) ) {
+		//! \todo Better error message that is more specific about the cause.
+		AIM_ERROR(_T("Could not open output directory '%s' using graphics format '%s'."),
+			m_sDir, m_pParam->GetString("output.img.format") );
+		return false;
+	}
+	CloseFile();
+
+	return true;
+}
+
+bool GraphicsOutputDeviceCairo::Initialize() {
+    Init();
+    m_bUseMemoryBuffer = true;
+    return(true);
+}
+
+void GraphicsOutputDeviceCairo::Init() {
+ 	AIM_ASSERT(m_pParam);
+	/*
+	 * Set parameters
+	 */
+	m_pParam->GetString("output.img.color.background");
+
+	m_bInvertColors = m_pParam->GetBool("output.img.color.invert");
+
+	// Output size!
+	m_iWidth = m_pParam->GetUInt("output.img.width");
+	m_iHeight = m_pParam->GetUInt("output.img.height");
+}
+
+unsigned char* GraphicsOutputDeviceCairo::GetBuffer() {
+  if(m_bUseMemoryBuffer)
+    return (cairo_image_surface_get_data (m_cSurface));
+  else
+    return NULL;
+}
+
+bool GraphicsOutputDeviceCairo::OpenFile(unsigned int index) {
+  const char *strPlottype = m_pParam->GetString("output.img.format");
+  if (!m_bUseMemoryBuffer) {
+    struct stat fileinfo;
+    // Get filename without trailing slash
+    strncpy(m_sFilename, m_sDir, sizeof(m_sFilename)/sizeof(m_sFilename[0]));
+#ifdef _WINDOWS
+    if (m_sFilename[strlen(m_sFilename)-1]=='\\') {
+      m_sFilename[strlen(m_sFilename)-1]='\0';
+    }
+#else
+    if (m_sFilename[strlen(m_sFilename)-1]=='/') {
+      m_sFilename[strlen(m_sFilename)-1]='\0';
+    }
+#endif
+    // Enumerate files it m_sDir is a directory.
+    if (stat(m_sFilename, &fileinfo) == 0 && (fileinfo.st_mode & S_IFDIR)) {
+      // We have a directory: enumerate with index
+      snprintf(m_sFilename, sizeof(m_sFilename)/sizeof(m_sFilename[0]),"%s%06d.%s",
+               m_sDir,
+               index,
+               strPlottype);
+      // If type is 'auto', fallback to 'png'
+      if (strcmp(strPlottype, "auto")==0)
+        strPlottype = "png";
+    } else {
+      // We have a (probably non-existant) file. Auto-detect type by extension if requested
+      strncpy(m_sFilename, m_sDir, sizeof(m_sFilename)/sizeof(m_sFilename[0]));
+      char *pDot = strrchr(m_sFilename, '.');
+      if (!pDot) {
+        AIM_ERROR(_T("Please supply extension on filename when using 'auto' format: '%s'"),
+                  m_sFilename);
+        return false;
+      }
+      strPlottype = &pDot[1];
+    }
+    m_bOutputFile= true; //! \todo Should check that it's possible to write to the file
+  }
+  // Cairo's RGB24 format has 32-bit pixels with the upper 8 bits unused.
+  // This is not the same as the plotutils PNG format. This information is transferred by the
+  // function GetPixelFormat. The pixel format is dealt with by the reciever.
+  m_cSurface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                           m_iWidth,
+                                           m_iHeight);
+  m_cCr = cairo_create (m_cSurface);
+  cairo_scale(m_cCr, (float)m_iWidth, (float)m_iHeight);
+	// Now setup things for this plotter.
+	cairo_select_font_face(m_cCr,
+                         m_pParam->GetString("output.img.fontname"),
+                         CAIRO_FONT_SLANT_NORMAL,
+                         CAIRO_FONT_WEIGHT_BOLD);
+  cairo_set_font_size (m_cCr, 0.015);
+	return true;
+}
+
+void GraphicsOutputDeviceCairo::CloseFile() {
+	// Plotting library
+	if (m_iPlotHandle>0) {
+	    cairo_destroy(m_cCr);
+		m_iPlotHandle = 0;
+	}
+	// And the output file
+	if (m_bOutputFile) {
+	  cairo_surface_write_to_png(m_cSurface, m_sFilename);
+		m_bOutputFile = false;
+	}
+	cairo_surface_destroy(m_cSurface);
+}
+
+GraphicsOutputDeviceCairo::~GraphicsOutputDeviceCairo() {
+	AIM_ASSERT(!m_iPlotHandle);
+	CloseFile();
+}
+
+void GraphicsOutputDeviceCairo::gGrab() {
+  // Open file.
+	if (!OpenFile(m_iFileNumber)) {
+		return;
+  }
+	// Setup plotting area.
+	cairo_set_line_width (m_cCr, 0.001f);
+	gColor3f (0.0f, 0.0f, 0.0f);
+  cairo_paint (m_cCr);
+  gColor3f(1.0f, 1.0f, 0.0f);
+}
+
+int GraphicsOutputDeviceCairo::GetPixelFormat() {
+    return AIM_PIX_FMT_RGB24_32;
+}
+
+void GraphicsOutputDeviceCairo::gBeginLineStrip() {
+	m_bIsFirstVertex = true;
+	m_iVertexType = VertexTypeLine;
+	//! \todo Make line width user-settable
+	cairo_set_line_width (m_cCr, 0.001f);
+}
+
+void GraphicsOutputDeviceCairo::gBeginQuadStrip() {
+	m_bIsFirstVertex = true;
+	m_iVertexType = VertexTypeQuad;
+	m_iPrevVertexCount = 0;
+  cairo_set_line_width (m_cCr, 0.001f);
+}
+
+void GraphicsOutputDeviceCairo::gColor3f(float r, float g, float b) {
+  if (m_bInvertColors) {
+		r = 1-r;
+		g = 1-g;
+		b = 1-b;
+	}
+  cairo_set_source_rgb (m_cCr, r, g, b);
+}
+
+void GraphicsOutputDeviceCairo::gVertex3f(float x, float y, float z) {
+	switch(m_iVertexType) {
+	case VertexTypeLine:
+		if (m_bIsFirstVertex) {
+			m_bIsFirstVertex = false;
+			//pl_fmove(x, y);
+			cairo_move_to(m_cCr, x, 1-y);
+		} else {
+			//pl_fcont(x, y);
+			cairo_line_to(m_cCr, x, 1-y);
+		}
+		break;
+	case VertexTypeQuad:
+		/* Store vertices until we got four in a row.
+		 * The order of vertices when processing quads is:
+		 *    1-----3-----5
+		 *    |     |     |
+		 *    0-----2-----4
+		 */
+		if (m_iPrevVertexCount >= 3) {
+			// Plot this quad
+			cairo_move_to(m_cCr, m_aPrevX[0], 1-m_aPrevY[0]);
+			cairo_line_to(m_cCr, m_aPrevX[1], 1-m_aPrevY[1]);
+			cairo_line_to(m_cCr, x, y);
+			cairo_line_to(m_cCr, m_aPrevX[2], 1-m_aPrevY[2]);
+			cairo_close_path (m_cCr);
+
+			// Last vertices of this quad are the first of the next
+			m_aPrevX[0] = m_aPrevX[2];
+			m_aPrevY[0] = m_aPrevY[2];
+			m_aPrevX[1] = x;
+			m_aPrevY[1] = y;
+			m_iPrevVertexCount = 2;
+		} else {
+			// Not at the fourth, keep storing
+			m_aPrevX[m_iPrevVertexCount] = x;
+			m_aPrevY[m_iPrevVertexCount] = y;
+			m_iPrevVertexCount++;
+		}
+		break;
+	default:
+		// Should not happen
+		AIM_ASSERT(0);
+	}
+}
+
+void GraphicsOutputDeviceCairo::gEnd() {
+	if(m_iVertexType==VertexTypeLine)
+    cairo_stroke (m_cCr);
+  else
+    cairo_fill (m_cCr);
+	m_iVertexType = VertexTypeNone;
+}
+
+void GraphicsOutputDeviceCairo::gText3f(float x,
+                                        float y,
+                                        float z,
+                                        const char *sStr,
+                                        bool bRotated) {
+  cairo_text_extents_t te;
+	if (bRotated) {
+	  cairo_rotate(m_cCr, M_PI/2);
+		cairo_move_to(m_cCr, x ,1-y);
+		cairo_show_text(m_cCr, sStr);
+		//cairo_identity_matrix(m_cCr);
+		cairo_rotate(m_cCr, -M_PI/2);
+	} else {
+		cairo_move_to(m_cCr, x ,1-y);
+		cairo_show_text(m_cCr, sStr);
+	}
+}
+
+void GraphicsOutputDeviceCairo::gRelease() {
+	AIM_ASSERT(m_iPlotHandle>0);
+	CloseFile();
+	// Finished this one, up to the next!
+	m_iFileNumber++;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,133 @@
+// Copyright 2007, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a graphics file using cairo
+ *
+ * \author Tom Walters <tom@acousticscale.org> and Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2007/09/17
+ * \version \$Header: $
+ */
+
+#ifndef __GRAPHICS_OUTPUT_DEVICE_CAIRO_H__
+#define __GRAPHICS_OUTPUT_DEVICE_CAIRO_H__
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cairo.h"
+
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDevice.h"
+
+/*!
+ * \class GraphicsOutputDeviceCairo "Output/GraphicsOutputDeviceCairo.h"
+ * \brief Output class for output to a graphics file using Cairo
+ *
+ * This class outputs a graphics operation to file. It only supports 2d though,
+ * so the z-component is ignored.
+ */
+class GraphicsOutputDeviceCairo : public GraphicsOutputDevice
+{
+ public:
+  GraphicsOutputDeviceCairo(AimParameters *pParam);
+  virtual ~GraphicsOutputDeviceCairo();
+
+	/*! \brief Initializes this output device, prepares plotting tools.
+	 *  \param sDir Directory or filename where to put images, max length is
+   *         _MAX_PATH. Must end with slash!!!
+	 *  \return true on success, false on failure.
+	 *
+	 *  sDir can be either a filename, in which case the output will be
+	 *  to that file, or a directory, in which case it will be filled
+	 *  with 6-digit numbered files. A new file is then created at every
+	 *  call to gGrab().
+	 *
+	 *  As usual, make sure to call this function before any other. If this
+	 *  Initialize() failed, you shouldn't try the other functions either.
+	 */
+	bool Initialize(const char *sDir);
+	bool Initialize();
+  void gGrab();
+  void gBeginLineStrip();
+  void gBeginQuadStrip();
+	using GraphicsOutputDevice::gVertex3f; // Because we overload it
+	void gVertex3f(float x, float y, float z);
+	void gColor3f(float r, float g, float b);
+  void gEnd();
+	void gText3f(float x, float y, float z, const char *sStr, bool bRotated = false);
+	void gRelease();
+	unsigned char* GetBuffer();
+	int GetPixelFormat();
+ protected:
+  /*! \brief Internal initialisation
+   *
+   */
+  void Init();
+
+	/*! \brief Open the file with given index for output
+	 *  \param index File number to open
+	 *  \return true on success, false on error
+	 *
+	 *  This opens a file for output and sets up the plotting library.
+	 */
+	bool OpenFile(unsigned int index);
+
+	//! \brief Closes a plot output file, if any is open.
+	void CloseFile();
+
+	//! \brief Set to true if the input file can be written to
+	bool m_bOutputFile;
+	//! \brief The Cairo plotter
+	int m_iPlotHandle;
+	//! \brief Output directory
+	char m_sDir[PATH_MAX];
+	//! \brief Current file number
+	unsigned int m_iFileNumber;
+	//! \brief true if this is the first vertex after gBegin()
+	bool m_bIsFirstVertex;
+
+	enum VertexType {
+		VertexTypeNone,
+    VertexTypeLine,
+    VertexTypeQuad
+	};
+	//! \brief The current vertex type
+	VertexType m_iVertexType;
+	//! \brief Begin vertex of current quad
+	float m_aPrevX[3], m_aPrevY[3];
+	//! \brief Current number of quad vertices stored
+	unsigned int m_iPrevVertexCount;
+
+	//! \brief Whether to invert the colors or not
+	bool m_bInvertColors;
+
+  //! \brief Cairo Drawing Surface
+  cairo_surface_t *m_cSurface;
+
+  //! \brief Cairo Context
+	cairo_t *m_cCr;
+
+  //! \brief Internal store for the input filename
+	char m_sFilename[PATH_MAX];
+
+	unsigned int m_iWidth;
+	unsigned int m_iHeight;
+	bool m_bUseMemoryBuffer;
+};
+
+#endif /* __GRAPHICS_OUTPUT_DEVICE_CAIRO_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,256 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a movie
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/10/16
+ * \version \$Id: GraphicsOutputDeviceMovie.cpp 633 2008-09-11 04:20:16Z tom $
+ */
+
+/*! \todo
+ *  A recent experiment showed that the video was about an audioframe
+ *  (something like 30 ms) behind the audio in rdct.wav. It seems odd
+ *  to me, since I already output a frame at the beginning to compensate
+ *  for the missed buffer.
+ *  A solution that would solve this and be a broader improvement, is to
+ *  include the source's time when Fire()ing. The outputdevice can then
+ *  do audio/video synchronization. In the case of the movie, the very
+ *  first gGrab() looks at the time and emits as much empty output frames
+ *  as needed until the correct signal time is reached.
+ */
+#include "Support/Common.h"
+
+#ifdef _WINDOWS
+#	include <direct.h> // for _mkdir&_rmdir
+#else
+#	include <sys/types.h>
+#	include <dirent.h> // for opendir&friends
+#endif
+#include <stdio.h>
+#include <string.h>
+
+
+#include "Output/GraphicsOutputDeviceMovie.h"
+
+GraphicsOutputDeviceMovie::GraphicsOutputDeviceMovie(Parameters *pParam)
+	: GraphicsOutputDeviceCairo(pParam) {  // or GraphicsOutputDevicePlotutils
+	m_sMovieFile[0] = '\0';
+	m_sSoundFile[0] = '\0';
+}
+
+bool GraphicsOutputDeviceMovie::Initialize(const char *sSoundFile,
+                                           const char *sMovieFile) {
+	FILE *f;
+	AIM_ASSERT(sSoundFile);
+	AIM_ASSERT(sMovieFile);
+
+	// Check sound file exists
+	if ((f = fopen(sSoundFile, "r")) == NULL) {
+		AIM_ERROR(_T("Couldn't open sound file '%s' for movie creation."),
+             sSoundFile);
+		return false;
+	}
+	fclose(f);
+	strcpy(m_sSoundFile, sSoundFile);
+
+	// Check movie output file can be made
+	if ( (f=fopen(sMovieFile, "w"))==NULL ) {
+		aimERROR(_T("Couldn't open movie file '%s' to write to."),
+             sMovieFile);
+		return false;
+	}
+	fclose(f);
+	strcpy(m_sMovieFile, sMovieFile);
+
+	// Get a temporary image output directory
+	//! \warning Not really safe ... but windows has no mkdtemp()
+	//! \todo Make build system check for mkdtemp() to use it when available. See TODO.txt.
+	char *sTmpDir = NULL;
+#ifdef _WINDOWS
+	if ((sTmpDir = _tempnam(NULL, AIM_NAME))
+      && _mkdir(sTmpDir) >= 0) {
+		strcpy(m_sDir, sTmpDir);
+		strcat(m_sDir, "\\"); // Make sure to end with trailing slash
+	} else
+#else
+	strcpy(m_sDir, "/tmp/"AIM_NAME"-movie.XXXXXX");
+	if (mkdtemp(m_sDir)) {
+		strcat(m_sDir, "/"); // Make sure to end with trailing slash
+	} else
+#endif
+	{
+		AIM_ERROR(_T("Couldn't create a temporary directory for movie output."));
+		if (sTmpDir) free(sTmpDir);
+		return false;
+	}
+	if (sTmpDir) {
+    free(sTmpDir);
+  }
+
+	// We want png for movie conversion
+	//! \bug This may change the user preference in GUI, hmm what to do? See TODO.txt
+	m_pParam->SetString("output.img.format", "png");
+	//if ( !GraphicsOutputDevicePlotutils::Initialize(m_sDir) ) {
+	if ( !GraphicsOutputDeviceCairo::Initialize(m_sDir) ) {
+		return false;
+  }
+
+	return true;
+}
+
+void GraphicsOutputDeviceMovie::Start() {
+	//GraphicsOutputDevicePlotutils::Start();
+	GraphicsOutputDeviceCairo::Start();
+	// Just output a single frame to get audio/video in sync, put params in there
+	gGrab();
+	PlotParameterScreen();
+	gRelease();
+}
+
+void GraphicsOutputDeviceMovie::Stop() {
+	// Make sure Plotutils is really done writing.
+	//GraphicsOutputDevicePlotutils::Stop();
+	GraphicsOutputDeviceCairo::Stop();
+	CloseFile();
+
+#ifdef __WX__
+	// GUI only: popup dialog
+#else
+	printf("Generating movie ... \n");
+#endif
+	AIM_ASSERT(m_pParam);
+	// Convert images and sound file to a movie
+	//! \warning Movie files are overwritten without warning
+	//! \bug ffmpeg only works with colour images, not with bw. So make sure to not use bw only in drawing..
+	// Always convert to audio stream of 44.1kHz or problems may occur in playing or conversio.
+	float fFps = 1000.0 / m_pParam->GetFloat("output.frameperiod");
+	char sffmpegPath[1024];
+	if (!m_pParam->IsSet("output.ffmpeg_path")) {
+  strcpy(sffmpegPath,"ffmpeg");
+  } else {
+  	strcpy(sffmpegPath, m_pParam->GetString("output.ffmpeg_path"));
+  }
+  char sCodecOptions[1024];
+	if (!m_pParam->IsSet("output.ffmpeg_codec_options")) {
+    strcpy(sCodecOptions,"");
+  } else {
+	  strcpy(sCodecOptions, m_pParam->GetString("output.ffmpeg_codec_options"));
+  }
+
+	char sCmdLine[1024]; //!\todo check that snprintf does not want a larger buffer
+	snprintf(sCmdLine, sizeof(sCmdLine)/sizeof(sCmdLine[0]),
+		"%s -r %.2f -y -i \"%s\" -i \"%s%%06d.png\" "
+		"-title \"%s\" -comment \"Generated by "AIM_NAME" "AIM_VERSION_STRING"\" "
+		"-sameq -r %.2f -ar 44100 -acodec pcm_s16le %s \"%s\"",
+		sffmpegPath, fFps, m_sSoundFile, m_sDir,
+		m_pParam->GetString("output.movie.title"),
+		fFps, sCodecOptions, m_sMovieFile);
+    printf(sCmdLine);
+    printf("\n");
+	if (system(sCmdLine)) {
+		AIM_ERROR(_T("Couldn't create movie output."));
+	}
+
+#ifdef __WX__
+	// GUI only: close dialog again
+#endif
+	// Remove files in temporary directory and the dir itself
+	//! \todo make portable function, possibly decided on by build system
+#ifdef _WINDOWS
+	HANDLE hList;
+	WIN32_FIND_DATA FileData;
+	snprintf(sCmdLine, sizeof(sCmdLine)/sizeof(sCmdLine[0]), "%s/*.*", m_sDir);
+	if ((hList = FindFirstFile(sCmdLine, &FileData)) == INVALID_HANDLE_VALUE) {
+		AIM_ERROR(_T("Couldn't remove files from temporary directory."));
+		return;
+	}
+	bool bRMfinished = false;
+	while (!bRMfinished) {
+	  snprintf(sCmdLine,
+             sizeof(sCmdLine)/sizeof(sCmdLine[0]),
+			       "%s%s",
+             m_sDir,
+             FileData.cFileName);
+		remove(sCmdLine);
+		if (!FindNextFile(hList, &FileData) && GetLastError() == ERROR_NO_MORE_FILES) {
+			bRMfinished = true;
+    }
+	}
+	FindClose(hList);
+	_rmdir(m_sDir);
+#else
+	DIR *dir;
+	struct dirent *dirent;
+	if (!(dir = opendir(m_sDir))) {
+		AIM_ERROR(_T("Couldn't remove files in temporary directory."));
+		return;
+	}
+	while (dirent = readdir(dir)) {
+		snprintf(sCmdLine,
+             sizeof(sCmdLine)/sizeof(sCmdLine[0]),
+			       "%s%s",
+             m_sDir,
+             dirent->d_name);
+		unlink(sCmdLine);
+	}
+	closedir(dir);
+	rmdir(m_sDir);
+#endif
+}
+
+void GraphicsOutputDeviceMovie::PlotParameterScreen() {
+	AIM_ASSERT(m_pParam);
+	char sStr[50];
+	int lineno = 1;
+
+	float fMarL = m_pParam->GetFloat(_S("graph.margin.left"));
+	float fMarT = m_pParam->GetFloat(_S("graph.margin.top"));
+	float fTextHeight = 1.0f / 50.0f * 1.2; // change this when fontsizing is there!
+
+	gText2f(fMarL, 1-(fMarT+fTextHeight*lineno++),
+          _S("AIM-C"));
+	gText2f(fMarL,
+          1-(fMarT+fTextHeight*lineno++),
+          _S("(c) 2006-2010, Thomas Walters, Willem van Engen"));
+	gText2f(fMarL,
+          1-(fMarT+fTextHeight*lineno++),
+          _S("http://aimc.acousticscale.org/"));
+	lineno++;
+
+	static const char *pPlotParams[] = {
+		_S("input.buffersize"),
+		_S("input.samplerate"),
+		_S("bmm.freqstart"),
+		_S("bmm.freqend"),
+		_S("bmm.numchannels"),
+		_S("preset.name"),
+		_S("preset.title"),
+		NULL
+	};
+	for (int i = 0; pPlotParams[i]; i++) {
+		snprintf(sStr,
+             sizeof(sStr)/sizeof(sStr[0]), _S("%s=%s"),
+			       pPlotParams[i],
+             m_pParam->GetString(pPlotParams[i]));
+		gText2f(fMarL,
+            1-(fMarT+fTextHeight*lineno++),
+            sStr);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,75 @@
+// Copyright 2006-2010, Willem van Engen, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a movie
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/10/16
+ * \version \$Id: $
+ */
+
+#ifndef __GRAPHICS_OUTPUT_DEVICE_MOVIE_H__
+#define __GRAPHICS_OUTPUT_DEVICE_MOVIE_H__
+
+#include "Support/util.h"
+//#include "Modules/Output/Graphics/Devices/GraphicsOutputDevicePlotutils.h"
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceCairo.h"
+
+/*!
+ * \class GraphicsOutputDeviceMovie "Output/GraphicsOutputDeviceMovie.h"
+ * \brief Output class for output to a movie
+ */
+ // GraphicsOutputDevicePlotutils is also possible here
+class GraphicsOutputDeviceMovie : public GraphicsOutputDeviceCairo {
+ public:
+  GraphicsOutputDeviceMovie(AimParameters *pParam);
+	virtual ~GraphicsOutputDeviceMovie() { };
+
+	/*! \brief Initializes this output device, prepares plotting tools.
+	 *  \param sSoundFile Sound file for the movie
+	 *  \param sMovieFile Movie filename to produce
+	 *  \return true on success, false on failure.
+	 *
+	 *  As usual, make sure to call this function before any other. If this
+	 *  Initialize() failed, you shouldn't try the other functions either.
+	 */
+	bool Initialize(const char *sSoundFile, const char *sMovieFile);
+
+	void Start();
+	//! \brief This function now also generates the output movie.
+	void Stop();
+
+ protected:
+	/*! \brief Plots a summary of relevant parameters on the output
+	 *
+	 *  This is intended for use in a movie as the first frame, which has no
+	 *  interesting data anyway, since at least one buffer of data is needed
+	 *  to be able to show someting.
+	 *
+	 *  The caller must do the gGrab() and gRelease().
+	 */
+	void PlotParameterScreen();
+
+	//! \brief Name of the sound file to be merged with the video
+	char m_sSoundFile[PATH_MAX];
+	//! \brief Name of the movie file to produce
+	char m_sMovieFile[PATH_MAX];
+};
+
+#endif /* __GRAPHICS_OUTPUT_DEVICE_MOVIE_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,396 @@
+// Copyright 2007, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output direct to a movie via local calls to libavcodec
+ *
+ * \author Tom Walters <tom@acousticscale.org>
+ * \date created 2007/10/05
+ * \version \$Id: $
+ */
+
+#include "Support/Common.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "Support/util.h"
+#include "Output/GraphicsOutputDeviceMovieDirect.h"
+
+GraphicsOutputDeviceMovieDirect::GraphicsOutputDeviceMovieDirect(Parameters *params)
+	: GraphicsOutputDeviceMovie(params) {
+	m_sMovieFile[0] = '\0';
+	m_sSoundFile[0] = '\0';
+}
+
+bool GraphicsOutputDeviceMovieDirect::Initialize(const char *sSoundFile,
+                                                 const char *sMovieFile) {
+	// We want pnm for direct movie conversion as the data format is nice and simple
+	//! \bug This may change the user preference in GUI, hmm what to do? See TODO.txt
+	//m_pParam->SetString("output.img.format", "pnm");
+
+	// Initialise GraphicsOutputDevicePlotutils for memory buffer use
+	if(!GraphicsOutputDeviceCairo::Initialize())
+    return false;
+
+  int width = m_pParam->GetUInt("output.img.width");
+	int height = m_pParam->GetUInt("output.img.height");
+	//float framerate = 1000.0f/m_pParam->GetFloat("output.frameperiod");
+	float framerate=1000.0f/20.0f;
+
+  m_pOutputMovie = new LibavformatWriter;
+	m_pOutputMovie->Init(sMovieFile,  width, height, framerate);
+
+	return true;
+}
+
+void GraphicsOutputDeviceMovieDirect::Stop() {
+	// Make sure Plotutils is really done writing.
+	GraphicsOutputDeviceCairo::Stop();
+	m_pOutputMovie->End();
+	delete m_pOutputMovie;
+
+}
+
+void GraphicsOutputDeviceMovieDirect::gRelease() {
+  // write the buffer
+  unsigned char *buf = GraphicsOutputDeviceCairo::GetBuffer();
+
+  if (buf != NULL)
+    m_pOutputMovie->WriteFrame(buf);
+
+  GraphicsOutputDeviceCairo::gRelease();
+}
+
+
+// Everything below here is hacked from the Libavformat API example:
+/*
+ * Libavformat API example: Output a media file in any supported
+ * libavformat format. The default codecs are used.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+LibavformatWriter::LibavformatWriter() {
+    sws_flags = SWS_BICUBIC;
+    pixfmt= PIX_FMT_RGB24;
+    fmt = NULL;
+}
+
+bool LibavformatWriter::Init(const char *sMovieFile,
+                             int width,
+                             int height,
+                             float framerate) {
+  /* initialize libavcodec, and register all codecs and formats */
+  av_register_all();
+
+  /* auto detect the output format from the name. default is mpeg. */
+  fmt = guess_format(NULL, sMovieFile, NULL);
+  if (!fmt) {
+    printf("Could not deduce output format from file extension: using MPEG.\n");
+    fmt = guess_format("mpeg", NULL, NULL);
+  }
+  if (!fmt) {
+    fprintf(stderr, "Could not find suitable output format\n");
+    return false;
+  }
+
+  /* allocate the output media context */
+  oc = av_alloc_format_context();
+  if (!oc) {
+    fprintf(stderr, "Memory error\n");
+    return false;
+  }
+  oc->oformat = fmt;
+  snprintf(oc->filename, sizeof(oc->filename), "%s", sMovieFile);
+
+  /* add the audio and video streams using the default format codecs
+     and initialize the codecs */
+  video_st = NULL;
+  fmt->video_codec=CODEC_ID_PNG;
+  if (fmt->video_codec != CODEC_ID_NONE) {
+    video_st = add_video_stream(oc, fmt->video_codec, width, height, framerate);
+  }
+
+  /* set the output parameters (must be done even if no
+     parameters). */
+  if (av_set_parameters(oc, NULL) < 0) {
+    fprintf(stderr, "Invalid output format parameters\n");
+    return false;
+  }
+
+  dump_format(oc, 0, sMovieFile, 1);
+
+  /* now that all the parameters are set, we can open the audio and
+     video codecs and allocate the necessary encode buffers */
+  if (video_st) {
+    open_video(oc, video_st);
+  }
+
+  /* open the output file, if needed */
+  if (!(fmt->flags & AVFMT_NOFILE)) {
+    if (url_fopen(&oc->pb, sMovieFile, URL_WRONLY) < 0) {
+      fprintf(stderr, "Could not open '%s'\n", sMovieFile);
+        return false;
+      }
+  }
+
+  /* write the stream header, if any */
+  av_write_header(oc);
+    return true;
+}
+
+
+/**************************************************************/
+/* video output */
+
+/* add a video output stream */
+AVStream* LibavformatWriter::add_video_stream(AVFormatContext *oc,
+                                              CodecID codec_id,
+                                              int width,
+                                              int height,
+                                              float framerate) {
+  AVCodecContext *c;
+  AVStream *st;
+
+  st = av_new_stream(oc, 0);
+  if (!st) {
+    fprintf(stderr, "Could not alloc stream\n");
+    return NULL;
+  }
+
+  c = st->codec;
+  c->codec_id = codec_id;
+  c->codec_type = CODEC_TYPE_VIDEO;
+
+  /* put sample parameters */
+  /* resolution must be a multiple of two */
+  c->width = width;
+  c->height = height;
+  /* time base: this is the fundamental unit of time (in seconds) in terms
+     of which frame timestamps are represented. for fixed-fps content,
+     timebase should be 1/framerate and timestamp increments should be
+     identically 1. */
+  c->time_base.den = (int)framerate;
+  c->time_base.num = 1;
+  c->gop_size = 12; /* emit one intra frame every twelve frames at most */
+  c->pix_fmt = pixfmt;
+  // some formats want stream headers to be separate
+  if(!strcmp(oc->oformat->name, "mp4")
+     || !strcmp(oc->oformat->name, "mov")
+     || !strcmp(oc->oformat->name, "3gp")) {
+      c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+    }
+  return st;
+}
+
+AVFrame* LibavformatWriter::alloc_picture(int pix_fmt,
+                                          int width,
+                                          int height) {
+  AVFrame *picture;
+  uint8_t *picture_buf;
+  int size;
+
+  picture = avcodec_alloc_frame();
+  if (!picture) {
+    return NULL;
+  }
+  size = avpicture_get_size(pix_fmt, width, height);
+  picture_buf = (uint8_t*)av_malloc(size);
+  if (!picture_buf) {
+    av_free(picture);
+    return NULL;
+  }
+  avpicture_fill((AVPicture *)picture, picture_buf,
+                pix_fmt, width, height);
+  return picture;
+}
+
+void LibavformatWriter::open_video(AVFormatContext *oc, AVStream *st) {
+  AVCodec *codec;
+  AVCodecContext *c;
+  c = st->codec;
+
+  /* find the video encoder */
+  codec = avcodec_find_encoder(c->codec_id);
+  if (!codec) {
+    fprintf(stderr, "codec not found\n");
+    exit(1);
+  }
+
+  /* open the codec */
+  if (avcodec_open(c, codec) < 0) {
+    fprintf(stderr, "could not open codec\n");
+    exit(1);
+  }
+
+  video_outbuf = NULL;
+  if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
+    /* allocate output buffer */
+    /* XXX: API change will be done */
+    /* buffers passed into lav* can be allocated any way you prefer,
+       as long as they're aligned enough for the architecture, and
+       they're freed appropriately (such as using av_free for buffers
+       allocated with av_malloc) */
+    video_outbuf_size = 200000;
+    video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
+  }
+
+  /* allocate the encoded raw picture */
+  picture = alloc_picture(c->pix_fmt, c->width, c->height);
+  if (!picture) {
+    fprintf(stderr, "Could not allocate picture\n");
+    exit(1);
+  }
+
+  /* if the output format is not RGB32, then a temporary RGB32
+     picture is needed too. It is then converted to the required
+     output format */
+    tmp_picture = NULL;
+    if (c->pix_fmt != PIX_FMT_RGB32) {
+      tmp_picture = alloc_picture(PIX_FMT_RGB32, c->width, c->height);
+      if (!tmp_picture) {
+        fprintf(stderr, "Could not allocate temporary picture\n");
+        exit(1);
+      }
+  }
+}
+
+
+void LibavformatWriter::close_video(AVFormatContext *oc,
+                                    AVStream *st) {
+  avcodec_close(st->codec);
+  av_free(picture->data[0]);
+  av_free(picture);
+  if (tmp_picture) {
+    av_free(tmp_picture->data[0]);
+    av_free(tmp_picture);
+  }
+  av_free(video_outbuf);
+}
+
+void LibavformatWriter::WriteFrame(unsigned char *pFrameBuffer) {
+  /* compute current video time */
+  if (video_st)
+    video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
+  else
+    video_pts = 0.0;
+
+  int out_size, ret;
+  AVCodecContext *c;
+  static struct SwsContext *img_convert_ctx;
+  c = video_st->codec;
+  if (c->pix_fmt != PIX_FMT_RGB32) {
+    /* as we only generate a RGB32 picture, we must convert it
+       to the codec pixel format if needed */
+    if (img_convert_ctx == NULL) {
+      img_convert_ctx = sws_getContext(c->width,
+                                       c->height,
+                                       PIX_FMT_RGB32,
+                                       c->width,
+                                       c->height,
+                                       c->pix_fmt,
+                                       sws_flags,
+                                       NULL,
+                                       NULL,
+                                       NULL);
+      if (img_convert_ctx == NULL) {
+        fprintf(stderr, "Cannot initialize the conversion context\n");
+        exit(1);
+      }
+    }
+    fill_image(tmp_picture, pFrameBuffer, c->width, c->height);
+    sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize,
+              0, c->height, picture->data, picture->linesize);
+  } else {
+    fill_image(picture, pFrameBuffer, c->width, c->height);
+  }
+  /* encode the image */
+  out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);
+  /* if zero size, it means the image was buffered */
+  if (out_size > 0) {
+    AVPacket pkt;
+    av_init_packet(&pkt);
+
+    pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base);
+    if(c->coded_frame->key_frame) {
+      pkt.flags |= PKT_FLAG_KEY;
+      pkt.stream_index= video_st->index;
+      pkt.data= video_outbuf;
+      pkt.size= out_size;
+      /* write the compressed frame in the media file */
+      ret = av_write_frame(oc, &pkt);
+    }
+  } else {
+    ret = 0;
+  }
+  if (ret != 0) {
+    fprintf(stderr, "Error while writing video frame\n");
+    exit(1);
+  }
+}
+
+void LibavformatWriter::End() {
+  /* close each codec */
+  if (video_st) {
+    close_video(oc, video_st);
+  }
+  /* write the trailer, if any */
+  av_write_trailer(oc);
+
+  /* free the streams */
+  for(i = 0; i < oc->nb_streams; i++) {
+    av_freep(&oc->streams[i]->codec);
+    av_freep(&oc->streams[i]);
+  }
+
+  if (!(fmt->flags & AVFMT_NOFILE)) {
+    /* close the output file */
+    url_fclose(&oc->pb);
+  }
+  /* free the stream */
+  av_free(oc);
+}
+
+void LibavformatWriter::fill_image(AVFrame *pict,
+                                  unsigned char *pFrameBuffer,
+                                  int width,
+                                  int height) {
+	memcpy((void*)&(pict->data[0][0]), (void*)pFrameBuffer, width*height*4);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,89 @@
+// Copyright 2006, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output direct to a movie via local calls to libavcodec
+ *
+ * \author Tom Walters <tom@acousticscale.org>
+ * \date created 2007/10/8
+ * \version \$Id$
+ */
+
+#ifndef __GRAPHICS_OUTPUT_DEVICE_MOVIE_DIRECT_H__
+#define __GRAPHICS_OUTPUT_DEVICE_MOVIE_DIRECT_H__
+
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.h"
+
+extern "C" {
+#include <ffmpeg/avformat.h>
+#include <ffmpeg/swscale.h>
+}
+
+/*!
+ * \class LibavformatWriter "Output/GraphicsOutputDeviceMovieDirect.h"
+ * \brief Helper class to use libavcodec to write a movie file
+ */
+class LibavformatWriter {
+ public:
+  LibavformatWriter();
+	~LibavformatWriter() { };
+	bool Init(const char *sMovieFile, int width, int height, float framerate);
+	void WriteFrame(unsigned char *pFrameBuffer);
+	void End();
+ private:
+  AVFrame *picture, *tmp_picture;
+  uint8_t *video_outbuf;
+  int frame_count, video_outbuf_size;
+  int sws_flags;
+  PixelFormat pixfmt;
+  AVOutputFormat *fmt;
+  AVFormatContext *oc;
+  AVStream *video_st;
+  double video_pts;
+  int i;
+  AVStream* add_video_stream(AVFormatContext *oc, CodecID codec_id, int width, int height, float framerate);
+  AVFrame* alloc_picture(int pix_fmt, int width, int height);
+  void open_video(AVFormatContext *oc, AVStream *st);
+  void close_video(AVFormatContext *oc, AVStream *st);
+  void fill_image(AVFrame *pict,unsigned char *pFrameBuffer , int width, int height);
+};
+
+/*!
+ * \class GraphicsOutputDeviceMovie "Output/GraphicsOutputDeviceMovie.h"
+ * \brief Output class for output to a movie
+ */
+class GraphicsOutputDeviceMovieDirect : public GraphicsOutputDeviceMovie {
+ public:
+  GraphicsOutputDeviceMovieDirect(AimParameters *pParam);
+	virtual ~GraphicsOutputDeviceMovieDirect() { };
+	/*! \brief Initializes this output device, prepares plotting tools.
+	 *  \param sSoundFile Sound file for the movie
+	 *  \param sMovieFile Movie filename to produce
+	 *  \return true on success, false on failure.
+	 *
+	 *  As usual, make sure to call this function before any other. If this
+	 *  Initialize() failed, you shouldn't try the other functions either.
+	 */
+	bool Initialize(const char *sSoundFile, const char *sMovieFile);
+	void Stop();
+  void gRelease();
+ private:
+	LibavformatWriter* m_pOutputMovie;
+};
+
+#endif /* __GRAPHICS_OUTPUT_DEVICE_MOVIE_DIRECT_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicePlotutils.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,309 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a graphics file using plotutils
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/10/13
+ * \version \$Id: GraphicsOutputDevicePlotutils.cpp 493 2007-11-27 10:59:20Z tom $
+ */
+#include "Support/Common.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "Support/util.h"
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDevicePlotutils.h"
+
+GraphicsOutputDevicePlotutils::GraphicsOutputDevicePlotutils(Parameters *pParam)
+    : GraphicsOutputDevice(pParam) {
+	m_iPlotHandle = 0;
+	m_pOutputFile = NULL;
+	m_iFileNumber = 0;
+	m_iVertexType = VertexTypeNone;
+	m_bUseMemoryBuffer = false;
+	m_pMemoryBuffer = NULL;
+	m_uWidth=0;
+	m_uHeight=0;
+}
+
+bool GraphicsOutputDevicePlotutils::Initialize(const char *sDir) {
+  Init();
+
+	//! \todo Output to file if sDir is a file, to directory with multiple
+  //! images if it's a directory.
+	strncpy(m_sDir, sDir, sizeof(m_sDir)/sizeof(m_sDir[0]));
+
+	/* Try to open an image to see if everything is allright. We want to avoid
+	 * errors in the main Process()ing loop. */
+	if (!OpenFile(0)) {
+	  //! \todo Better error message that is more specific about the cause.
+		AIM_ERROR(_T("Could not open output directory '%s' using graphics format '%s'."),
+			       m_sDir, m_pParam->GetString("output.img.format") );
+		return false;
+	}
+	CloseFile();
+	return true;
+}
+
+void GraphicsOutputDevicePlotutils::Init() {
+  AIM_ASSERT(m_pParam);
+	/*
+	 * Set parameters
+	 */
+	pl_parampl("BG_COLOR", (void*)m_pParam->GetString("output.img.color.background"));
+	// Handle GIFs as other output formats, don't merge frames into single GIF.
+	pl_parampl("GIF_ANIMATION", (void*)"no");
+	pl_parampl("INTERLACE", (void*)"no");
+
+	m_bInvertColors = m_pParam->GetBool("output.img.color.invert");
+
+	// Output size!
+	m_uWidth = m_pParam->GetUInt("output.img.width");
+	m_uHeight = m_pParam->GetUInt("output.img.height");
+	char strSize[100];
+	snprintf(strSize, sizeof(strSize)/sizeof(strSize[0]), "%ux%u", m_uWidth, m_uHeight);
+	pl_parampl("BITMAPSIZE", (void*)strSize);
+}
+
+bool GraphicsOutputDevicePlotutils::Initialize() {
+  Init();
+  m_bUseMemoryBuffer = true;
+  return(true);
+}
+
+bool GraphicsOutputDevicePlotutils::OpenFile(unsigned int index) {
+  const char *strPlottype = m_pParam->GetString("output.img.format");
+  if (!m_bUseMemoryBuffer) {
+    char sFilename[PATH_MAX];
+    struct stat fileinfo;
+
+    // Get filename without trailing slash
+    strncpy(sFilename, m_sDir, sizeof(sFilename)/sizeof(sFilename[0]));
+#ifdef _WINDOWS
+    if (sFilename[strlen(sFilename)-1]=='\\') {
+      sFilename[strlen(sFilename)-1]='\0';
+    }
+#else
+    if (sFilename[strlen(sFilename)-1]=='/') {
+      sFilename[strlen(sFilename)-1]='\0';
+    }
+#endif
+    // Enumerate files it m_sDir is a directory.
+    if ( stat(sFilename, &fileinfo)==0 && (fileinfo.st_mode & S_IFDIR) ) {
+      // We have a directory: enumerate with index
+      snprintf(sFilename, sizeof(sFilename)/sizeof(sFilename[0]),"%s%06d.%s", m_sDir, index, strPlottype);
+      // If type is 'auto', fallback to 'png'
+      if (strcmp(strPlottype, "auto")==0)
+          strPlottype = "png";
+      } else {
+        // We have a (probabely non-existant) file. Auto-detect type by extension if requested
+        strncpy(sFilename, m_sDir, sizeof(sFilename)/sizeof(sFilename[0]));
+        char *pDot = strrchr(sFilename, '.');
+        if (!pDot) {
+          AIM_ERROR(_T("Please supply extension on filename when using 'auto' format: '%s'"), sFilename);
+          return false;
+        }
+        strPlottype = &pDot[1];
+      }
+      if ((m_pOutputFile = fopen(sFilename, "wb")) == NULL) {
+        return false;
+      }
+    } else {
+#if !defined(_WINDOWS) && !defined(_MACOSX)
+    if ((m_pOutputFile=open_memstream(&m_pMemoryBuffer, &m_sMemoryBufferSize))==NULL)
+#endif
+      return false;
+      strPlottype="pnm"; // Force pnm format or this doesn't work
+    }
+
+	/*
+	 * Create a plotter
+	 *
+	 * Plotutils knows the following types for file output:
+	 *   pnm gif ai ps gif pcl hpgl tek meta
+	 */
+	if ((m_iPlotHandle = pl_newpl(strPlottype,
+                                NULL,
+                                m_pOutputFile,
+                                stderr)) < 0 ) {
+		return false;
+	}
+	pl_selectpl(m_iPlotHandle);
+
+	if ( pl_openpl() < 0) {
+		return false;
+	}
+
+	// Now setup things for this plotter
+	pl_fontname(m_pParam->GetString("output.img.fontname"));
+	//! \todo Make fontsize work in Plotutils, currently disabled
+	//pl_ffontsize(m_pParam->GetUInt("output.fontsize"));
+
+	return true;
+}
+
+char* GraphicsOutputDevicePlotutils::GetBuffer() {
+  if(m_bUseMemoryBuffer && (m_pMemoryBuffer!=NULL))
+    return (&m_pMemoryBuffer[m_sMemoryBufferSize-(m_uWidth*m_uHeight*3)]);
+  else
+    return NULL;
+}
+
+void GraphicsOutputDevicePlotutils::CloseFile() {
+	// Plotting library
+	if (m_iPlotHandle>0) {
+		pl_closepl();
+
+		pl_selectpl(0);
+		pl_deletepl(m_iPlotHandle);
+		m_iPlotHandle = 0;
+	}
+
+	// And the output file
+	if (m_pOutputFile) {
+		fclose(m_pOutputFile);
+		m_pOutputFile = NULL;
+	}
+}
+
+GraphicsOutputDevicePlotutils::~GraphicsOutputDevicePlotutils() {
+	// Output file should be closed by gRelease()
+	AIM_ASSERT(!m_pOutputFile);
+	AIM_ASSERT(!m_iPlotHandle);
+	CloseFile();
+}
+
+PixelFormat GraphicsOutputDevicePlotutils::GetPixelFormat() {
+    return AIM_PIX_FMT_RGB24_24;
+}
+
+void GraphicsOutputDevicePlotutils::gGrab() {
+	// Open file
+	if (!OpenFile(m_iFileNumber)) {
+		return;
+  }
+	// Setup plotting area
+	pl_fspace(0.0, 0.0, 1.0, 1.0);
+	pl_flinewidth(0.0001);
+	pl_pencolorname("darkblue");
+	pl_erase();
+}
+
+void GraphicsOutputDevicePlotutils::gBeginLineStrip() {
+	m_bIsFirstVertex = true;
+	m_iVertexType = VertexTypeLine;
+	pl_filltype(0);
+}
+
+void GraphicsOutputDevicePlotutils::gBeginQuadStrip() {
+	m_bIsFirstVertex = true;
+	m_iVertexType = VertexTypeQuad;
+	m_iPrevVertexCount = 0;
+	pl_filltype(1);
+}
+
+void GraphicsOutputDevicePlotutils::gColor3f(float r, float g, float b) {
+	if (m_bInvertColors) {
+		r = 1-r;
+		g = 1-g;
+		b = 1-b;
+	}
+	int ir = (int)(r*0xffff);
+	int ig = (int)(g*0xffff);
+	int ib = (int)(b*0xffff);
+	ir = MIN(0xffff, MAX(0,ir));
+	ig = MIN(0xffff, MAX(0,ig));
+	ib = MIN(0xffff, MAX(0,ib));
+	pl_color(ir, ig, ib);
+}
+
+void GraphicsOutputDevicePlotutils::gVertex3f(float x, float y, float z) {
+	switch(m_iVertexType) {
+	case VertexTypeLine:
+		if (m_bIsFirstVertex) {
+			m_bIsFirstVertex = false;
+			pl_fmove(x, y);
+		} else {
+			pl_fcont(x, y);
+		}
+		break;
+	case VertexTypeQuad:
+		/* Store vertices until we got four in a row.
+		 * The order of vertices when processing quads is:
+		 *    1-----3-----5
+		 *    |     |     |
+		 *    0-----2-----4
+		 */
+		if (m_iPrevVertexCount>=3) {
+			// Plot this quad
+			pl_fmove(m_aPrevX[0],m_aPrevY[0]);
+			pl_fcont(m_aPrevX[1],m_aPrevY[1]);
+			pl_fcont(x,y);
+			pl_fcont(m_aPrevX[2],m_aPrevY[2]);
+			pl_endpath();
+			// Last vertices of this quad are the first of the next
+			m_aPrevX[0] = m_aPrevX[2];
+			m_aPrevY[0] = m_aPrevY[2];
+			m_aPrevX[1] = x;
+			m_aPrevY[1] = y;
+			m_iPrevVertexCount = 2;
+		} else {
+			// Not at the fourth, keep storing
+			m_aPrevX[m_iPrevVertexCount] = x;
+			m_aPrevY[m_iPrevVertexCount] = y;
+			m_iPrevVertexCount++;
+		}
+		break;
+	default:
+		// Should not happen
+		AIM_ASSERT(0);
+	}
+}
+
+void GraphicsOutputDevicePlotutils::gEnd() {
+	pl_endpath();
+	m_iVertexType = VertexTypeNone;
+}
+
+void GraphicsOutputDevicePlotutils::gText3f(float x,
+                                            float y,
+                                            float z,
+                                            const char *sStr,
+                                            bool bRotated) {
+	if (bRotated) {
+		pl_textangle(90);
+		pl_fmove(x, y);
+		pl_alabel('l', 't', sStr);
+	} else {
+		pl_textangle(0);
+		pl_fmove(x, y);
+		pl_alabel('l', 'b', sStr);
+	}
+}
+
+void GraphicsOutputDevicePlotutils::gRelease() {
+	AIM_ASSERT(m_pOutputFile);
+	AIM_ASSERT(m_iPlotHandle>0);
+	CloseFile();
+	m_iFileNumber++;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicePlotutils.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,133 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a graphics file using plotutils
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/10/13
+ * \version \$Id: $
+ */
+
+#ifndef __GRAPHICS_OUTPUT_DEVICE_PLOTUTILS_H__
+#define __GRAPHICS_OUTPUT_DEVICE_PLOTUTILS_H__
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <plot.h>
+
+#include "Support/util.h"
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDevice.h"
+
+/*!
+ * \class GraphicsOutputDevicePlotutils "Output/GraphicsOutputDevicePlotutils.h"
+ * \brief Output class for output to a graphics file using plotutils
+ *
+ * This class outputs a graphics operation to file. It only supports 2d though,
+ * so the z-component is ignored.
+ */
+class GraphicsOutputDevicePlotutils : public GraphicsOutputDevice {
+ public:
+  GraphicsOutputDevicePlotutils(Parameters *pParam);
+  virtual ~GraphicsOutputDevicePlotutils();
+
+	/*! \brief Initializes this output device, prepares plotting tools.
+	 *  \param sDir Directory or filename where to put images, max length
+   *         is _MAX_PATH. Must end with slash!!!
+	 *  \return true on success, false on failure.
+	 *
+	 *  sDir can be either a filename, in which case the output will be
+	 *  to that file, or a directory, in which case it will be filled
+	 *  with 6-digit numbered files. A new file is then created at every
+	 *  call to gGrab().
+	 *
+	 *  As usual, make sure to call this function before any other. If this
+	 *  Initialize() failed, you shouldn't try the other functions either.
+	 */
+	bool Initialize(const char *sDir);
+	bool Initialize();
+
+  void gGrab();
+  void gBeginLineStrip();
+  void gBeginQuadStrip();
+	using GraphicsOutputDevice::gVertex3f; // Because we overload it
+	void gVertex3f(float x, float y, float z);
+	void gColor3f(float r, float g, float b);
+  void gEnd();
+	void gText3f(float x, float y, float z, const char *sStr, bool bRotated = false);
+	void gRelease();
+	char* GetBuffer();
+ protected:
+	/*! \brief Open the file with given index for output
+	 *  \param index File number to open
+	 *  \return true on success, false on error
+	 *
+	 *  This opens a file for output and sets up the plotting library.
+	 */
+	bool OpenFile(unsigned int index);
+
+	//! \brief Closes a plot output file, if any is open.
+	void CloseFile();
+
+	//! \brief Internal initialisation function called by overloaded variants of Initialize()
+	void Init();
+
+	//! \brief The current output file's handle
+	FILE *m_pOutputFile;
+	//! \brief The plotutils plotter
+	int m_iPlotHandle;
+	//! \brief Output directory
+	char m_sDir[PATH_MAX];
+	//! \brief Current file number
+	unsigned int m_iFileNumber;
+	//! \brief true if this is the first vertex after gBegin()
+	bool m_bIsFirstVertex;
+
+	enum VertexType {
+		VertexTypeNone,
+    VertexTypeLine,
+    VertexTypeQuad
+	};
+	//! \brief The current vertex type
+	VertexType m_iVertexType;
+	//! \brief Begin vertex of current quad
+	float m_aPrevX[3], m_aPrevY[3];
+	//! \brief Current number of quad vertices stored
+	unsigned int m_iPrevVertexCount;
+
+	//! \brief Whether to invert the colors or not
+	bool m_bInvertColors;
+
+	//! \brief The size of the memory bitmap buffer as provided by open_memstream
+	size_t m_sMemoryBufferSize;
+
+	//! \brief Are we using a memory buffer for writing to (as opposed to a file)?
+	bool m_bUseMemoryBuffer;
+
+	//! \brief Pointer to the memory buffer in question
+	char *m_pMemoryBuffer;
+
+	//! \brief Width of the bitmap
+	unsigned int m_uWidth;
+
+	//! \brief Height of the bitmap
+	unsigned int m_uHeight;
+};
+
+#endif /* __GRAPHICS_OUTPUT_DEVICE_PLOTUTILS_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicewxGLCanvas.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,506 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a wxWidgets OpenGL canvas
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/21
+ * \version \$Id: $
+ */
+
+#include "Support/Common.h"
+
+/*! \class GraphicsOutputDevicewxGLCanvas
+ *
+ *  Graphics output takes a large part of the application's performance at the
+ *  moment when it is inline with the Process() loop. Much is gained by
+ *  putting it in a separate thread, which can be done using ShotTargetThreaded.
+ *
+ *  OpenGL-related documents:
+ *   - http://www.opengl.org/
+ *   - http://www.sgi.com/products/software/opengl/
+ *   - http://developer.apple.com/graphicsimaging/opengl/
+ *   - http://developer.nvidia.com/page/documentation.html
+ *   - Vertex arrays
+ *     - http://www.opengl.org/registry/specs/EXT/vertex_array.txt
+ *     - http://www.awprofessional.com/articles/article.asp?p=461848&seqNum=2&rl=1
+ *     - http://jdobry.webpark.cz/opengl/opengl_maximum_performance.html
+ *   - Fonts and OpenGL
+ *     - http://gltt.sourceforge.net/
+ */
+
+// And finally our own
+#include "Support/util.h"
+#include "Output/GraphicsOutputDevice.h"
+#include "Output/GraphicsOutputDevicewxGLCanvas.h"
+
+BEGIN_EVENT_TABLE(GraphicsOutputDevicewxGLCanvas, wxGLCanvas)
+    EVT_SIZE(GraphicsOutputDevicewxGLCanvas::OnSize)
+    EVT_PAINT(GraphicsOutputDevicewxGLCanvas::OnPaint)
+    EVT_ERASE_BACKGROUND(GraphicsOutputDevicewxGLCanvas::OnEraseBackground)
+END_EVENT_TABLE()
+
+// wxGLCanvas attributes
+int GraphicsOutputDevicewxGLCanvas::GLAttrlist[] = {
+  WX_GL_RGBA, 1,
+  WX_GL_DOUBLEBUFFER, 1,
+  WX_GL_MIN_RED, 5,
+  WX_GL_MIN_GREEN, 5,
+  WX_GL_MIN_BLUE, 5,
+  WX_GL_MIN_ALPHA, 3,
+  WX_GL_DEPTH_SIZE, 16,
+  0
+};
+
+// OpenGL get procaddress function pointer, differs across platforms
+typedef void (*(*glGetProcAddressPtr_t)(const char*))();
+
+GraphicsOutputDevicewxGLCanvas::GraphicsOutputDevicewxGLCanvas(Parameters *pParam,
+	                                                             wxWindow *parent,
+                                                               wxWindowID id,
+                                                               const wxPoint& pos,
+                                                               const wxSize& size,
+                                                               long style,
+                                                               const wxString& name)
+	: wxGLCanvas(parent, (wxGLCanvas*) NULL, id, pos, size,
+               style|wxFULL_REPAINT_ON_RESIZE, name, GLAttrlist),
+	  GraphicsOutputDevice(pParam) {
+	m_init = false;
+	m_gllist = 0;
+	m_pWorkerContext = NULL;
+	m_bAntialiasing = true;
+	m_pFont = NULL;
+	m_sFontFile = NULL;
+	m_iFontsize = -1;
+#if !defined(_MACOSX)
+	s_bWorkerNeedsInit = false;
+#endif
+
+#ifdef WITH_GL_VERTEX_ARRAYS
+	m_iVertexType = 0xffff; // no gBegin() has happened yet
+	m_bStaticColor = false;
+	m_pVertices = NULL;
+	m_iVerticesMax = 0;
+	// Enable vertex arrays if possible
+#ifdef _MACOSX
+	m_glLockArraysEXT = ::glLockArraysEXT;
+	m_glUnlockArraysEXT = ::glUnlockArraysEXT;
+	m_bVertexArrayLock = true;
+#else
+	m_bVertexArrayLock = false;
+	// OpenGL command needed to fetch entry point, do it in InitGL()
+#endif /* _MACOSX */
+#endif /* WITH_GL_VERTEX_ARRAYS */
+}
+
+GraphicsOutputDevicewxGLCanvas::~GraphicsOutputDevicewxGLCanvas() {
+	// Cleanup OpenGL display list
+	if (m_init) {
+		glDeleteLists(m_gllist, 1);
+	}
+	DELETE_IF_NONNULL(m_pWorkerContext);
+	DELETE_IF_NONNULL(m_pFont);
+#ifdef WITH_GL_VERTEX_ARRAYS
+	DELETE_ARRAY_IF_NONNULL(m_pVertices);
+#endif
+}
+
+void GraphicsOutputDevicewxGLCanvas::Start() {
+	// This seems to be needed to prevent a crash on windows, but why????
+	SetCurrent();
+	return GraphicsOutputDevice::Start();
+}
+
+bool GraphicsOutputDevicewxGLCanvas::Initialize(unsigned int iVerticesMax) {
+	AIM_ASSERT(m_pParam);
+	// Give a chance to update anti-aliasing settings
+	if (m_bAntialiasing != m_pParam->GetBool("output.antialias")) {
+	  m_bAntialiasing = m_pParam->GetBool("output.antialias");
+		if (SetCurrent()) {
+		  InitGL();
+#if !defined(_MACOSX)
+			{
+				wxMutexLocker lock(s_mutexOpenGL);
+				s_bWorkerNeedsInit = true;
+			}
+#endif
+		}
+	}
+
+#ifdef WITH_GL_VERTEX_ARRAYS
+	// Re-allocate vertices
+	if (iVerticesMax > m_iVerticesMax) {
+		DELETE_IF_NONNULL(m_pVertices);
+		m_iVerticesMax = iVerticesMax;
+		// If color is static, we need not store the color
+		if (m_bStaticColor)
+			m_pVertices = new GLfloat[(iVerticesMax+1)*3];
+		else
+			m_pVertices = new GLfloat[(iVerticesMax+1)*6];
+	}
+#endif
+
+  // Change font if requested
+	const char *sFontFile = m_pParam->GetString("output.gl.fontfile");
+	unsigned int iFontsize = m_pParam->GetUInt("output.fontsize");
+	if (!m_sFontFile
+      || !strcmp(m_sFontFile,sFontFile)==0
+      || m_iFontsize!=(int)iFontsize) {
+		wxMutexLocker lock(s_mutexOpenGL);
+		DELETE_IF_NONNULL(m_pFont);
+		wxString sWorkingFontFilename = wxString::FromAscii(sFontFile);
+		if (!wxFileExists(sWorkingFontFilename)) {
+	    sWorkingFontFilename = wxString::FromAscii(aimDataDir());
+		  sWorkingFontFilename += _T("/");
+		  sWorkingFontFilename += wxString::FromAscii(sFontFile);
+		}
+		//if (!wxFileExists(sWorkingFontFilename))
+		//sWorkingFontFilename.replace("Font:").append(sFontFile);
+		m_pFont = static_cast<FTFont*>(new FTGLBitmapFont(sWorkingFontFilename.fn_str()));
+		if (!m_pFont || m_pFont->Error()) {
+			aimERROR(_T("Couldn't load font '%s'"), sFontFile);
+			DELETE_IF_NONNULL(m_pFont);
+		} else {
+			// Display lists don't mix with our own usage :(
+			// May not be needed for a Bitmap font
+			//m_pFont->UseDisplayList(false);
+			if ( !m_pFont->FaceSize(iFontsize) ) {
+				AIM_ERROR(_T("Couldn't select font size %u on font '%s'"), iFontsize, sFontFile);
+				DELETE_IF_NONNULL(m_pFont);
+			}
+		}
+		m_sFontFile = sFontFile;
+		m_iFontsize = iFontsize;
+	}
+	return true;
+}
+bool GraphicsOutputDevicewxGLCanvas::Initialize() {
+	return Initialize(0);
+}
+
+void GraphicsOutputDevicewxGLCanvas::Render() {
+	wxPaintDC dc(this);
+	// We want to initialize first from main thread.
+	if (!m_init) {
+	  if (!SetCurrent()) return;
+	  InitGL();
+	}
+	// Render saved list only if not animating (redrawn anyway in that case)
+	if (!m_bRunning) {
+		if (!SetCurrent()) {
+      return;
+    }
+		glClear(GL_COLOR_BUFFER_BIT/*|GL_DEPTH_BUFFER_BIT*/);
+		glCallList(m_gllist);
+		SwapBuffers();
+	}
+}
+
+void GraphicsOutputDevicewxGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
+  Render();
+}
+
+void GraphicsOutputDevicewxGLCanvas::OnSize(wxSizeEvent& event) {
+  // this is also necessary to update the context on some platforms
+  wxGLCanvas::OnSize(event);
+
+  // set GL viewport
+	// (not called by wxGLCanvas::OnSize on all platforms...)
+  if (SetCurrent()) {
+		DoResize();
+		// It is only sensible to update the other thread when it's running
+		// Don't acquire the mutex when s_bWorkerNeedsInit already to avoid deadlock
+		if (/*m_bRunning &&*/ !s_bWorkerNeedsInit) {
+			wxMutexLocker lock(s_mutexOpenGL);
+			s_bWorkerNeedsInit = true;
+		}
+	}
+}
+
+void GraphicsOutputDevicewxGLCanvas::OnEraseBackground(wxEraseEvent& WXUNUSED(event)) {
+}
+
+bool GraphicsOutputDevicewxGLCanvas::SetCurrent() {
+  bool bRet=true;
+
+#ifndef __WXMOTIF__
+	bRet = (GetContext()!=NULL);
+	if (bRet)
+#endif
+	{
+		wxGLCanvas::SetCurrent();
+	}
+	return bRet;
+}
+
+void GraphicsOutputDevicewxGLCanvas::DoResize() {
+  int w, h;
+	GetClientSize(&w, &h);
+	glViewport(0, 0, (GLint)w, (GLint)h);
+}
+
+void GraphicsOutputDevicewxGLCanvas::InitGL() {
+  /* No SetCurrent() here, because this can be called from different GL contexts.
+	 * Convenient for multi-threaded operation. */
+  //aimERROR(_T("InitGL Called"));
+#if defined(WITH_GL_VERTEX_ARRAYS) && !defined(_MACOSX)
+	if (!m_init) {
+	  /* This needs to be done here, because OpenGL commands may need SetCurrent()
+		 * and an already shown window. */
+		char *extensions = (char *)glGetString(GL_EXTENSIONS);
+		if (!extensions) {
+			AIM_INFO(_T("Could not query OpenGL extensions, vertex arrays disabled"));
+    }	else if (strstr(extensions, "GL_EXT_compiled_vertex_array")) {
+			m_glLockArraysEXT = (LOCAL_PFNGLLOCKARRAYSEXTPROC)GL_GET_PROC_ADDRESS("glLockArraysEXT");
+			m_glUnlockArraysEXT = (LOCAL_PFNGLUNLOCKARRAYSEXTPROC)GL_GET_PROC_ADDRESS("glUnlockArraysEXT");
+			if(!m_glLockArraysEXT || !m_glUnlockArraysEXT)
+				AIM_ERROR(_T("OpenGL error on GL_EXT_compiled_vertex_array"));
+			else
+				m_bVertexArrayLock = true;
+		}
+	}
+#endif
+	DoResize();
+	glClearColor(0, 0, 0, 1);
+	glMatrixMode( GL_PROJECTION );
+	glLoadIdentity( );
+
+	glEnable(GL_VERTEX_ARRAY);
+
+	// Window limits in OpenGL co-ordiantes
+	//! \todo Make this configurable, or change and document fixed values
+	glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
+	glTranslatef(0.0, 0.0, 0.0);
+
+	if (m_bAntialiasing) {
+		glEnable(GL_LINE_SMOOTH);
+		glEnable(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		//glBlendFunc(GL_ONE, GL_ONE);
+		glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
+	} else {
+		glDisable(GL_LINE_SMOOTH);
+		glDisable(GL_BLEND);
+	}
+	glLineWidth(1.0);
+
+	// Get a free display list only the first time
+	if (!m_init) {
+#if !defined(_MACOSX)
+		// Windows and Linux need a separate worker context
+		aimASSERT(wxIsMainThread());
+#if wxCHECK_VERSION(2,8,0)
+		m_pWorkerContext = new wxGLContext(this, m_glContext);
+#else
+		m_pWorkerContext = new wxGLContext(true,
+                                       this,
+                                       wxNullPalette,
+                                       m_glContext);
+#endif
+		aimASSERT(m_pWorkerContext);
+		s_bWorkerNeedsInit = true;
+#endif
+		m_gllist = glGenLists(1);
+		aimASSERT(m_gllist);
+		// Empty window at start
+		glNewList(m_gllist, GL_COMPILE_AND_EXECUTE);
+		glEndList();
+		m_init = true;
+	}
+}
+
+// Call before any other render* functions
+void GraphicsOutputDevicewxGLCanvas::gGrab() {
+	AimwxGuiLocker __lock__;
+#if !defined(_MACOSX)
+	// Detect if we're the main thread or not.
+	if (!wxIsMainThread()) {
+		// We're called by a worker thread, make sure there's a right context
+		AIM_ASSERT(m_pWorkerContext);
+#if wxCHECK_VERSION(2,8,0)
+		m_pWorkerContext->SetCurrent(*this);
+#else
+		m_pWorkerContext->SetCurrent();
+#endif
+		// Update OpenGL settings if needed
+		wxMutexLocker lock(s_mutexOpenGL);
+		if (s_bWorkerNeedsInit) {
+			InitGL();
+			s_bWorkerNeedsInit = false;
+		}
+	} else
+#endif
+	{
+		// Either called by main thread, or we need no special worker glContext
+		if (!SetCurrent()) {
+		  return;
+		}
+		// Init OpenGL once, but after SetCurrent
+		if (!m_init) {
+			InitGL();
+		}
+	}
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	// Start and store in a display list for redrawing
+	glNewList(m_gllist, GL_COMPILE);
+}
+
+void GraphicsOutputDevicewxGLCanvas::gBeginLineStrip() {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	aimASSERT(m_iVertexType == 0xffff); // Previous gBegin*() must be gEnd()ed
+	// New lines vertex array
+	m_iVertexCount = 0;
+	m_iVertexType = GL_LINE_STRIP;
+#else
+	AimwxGuiLocker __lock__;
+	glBegin(GL_LINE_STRIP);
+#endif
+}
+
+void GraphicsOutputDevicewxGLCanvas::gBeginQuadStrip() {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	aimASSERT(m_iVertexType == 0xffff); // Previous gBegin*() must be gEnd()ed
+	// New quads vertex array
+	m_iVertexCount = 0;
+	m_iVertexType = GL_QUAD_STRIP;
+#else
+	AimwxGuiLocker __lock__;
+	glBegin(GL_QUAD_STRIP);
+#endif
+}
+
+void GraphicsOutputDevicewxGLCanvas::gVertex3f(float x, float y, float z) {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	aimASSERT(m_iVertexType != 0xffff); // Must be inside gBegin*()
+	if (m_iVertexCount>=m_iVerticesMax) {
+		static bool errShown=false;
+		if (!errShown) {
+			aimERROR(_T("Error: max vertex count reached: %d"), m_iVertexCount);
+			errShown=true;
+		}
+		return;
+	}
+	if (m_bStaticColor) {
+		m_pVertices[m_iVertexCount*3+0] = x;
+		m_pVertices[m_iVertexCount*3+1] = y;
+		m_pVertices[m_iVertexCount*3+2] = z;
+	} else {
+		m_pVertices[m_iVertexCount*6+0] = m_fCurColorR;
+		m_pVertices[m_iVertexCount*6+1] = m_fCurColorG;
+		m_pVertices[m_iVertexCount*6+2] = m_fCurColorB;
+		m_pVertices[m_iVertexCount*6+3] = x;
+		m_pVertices[m_iVertexCount*6+4] = y;
+		m_pVertices[m_iVertexCount*6+5] = z;
+	}
+	m_iVertexCount++;
+#else
+	AimwxGuiLocker __lock__;
+	glVertex3f(x,y,z);
+#endif
+}
+
+void GraphicsOutputDevicewxGLCanvas::gColor3f(float r, float g, float b) {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	if (m_iVertexType==0xffff || m_bStaticColor) {
+		// If not inside vertex array run, use the ordinary command
+		glColor3f(r, g, b);
+	}
+	if (!m_bStaticColor) {
+		// Set current color for vertex array usage
+		m_fCurColorR = r;
+		m_fCurColorG = g;
+		m_fCurColorB = b;
+	}
+#else
+	AimwxGuiLocker __lock__;
+    glColor3f(r, g, b);
+#endif
+}
+
+void GraphicsOutputDevicewxGLCanvas::gEnd() {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	aimASSERT(m_iVertexType != 0xffff); // Must be inside gBegin*()
+	AimwxGuiLocker __lock__;
+
+	// Draw the vertex array
+	glEnableClientState(GL_VERTEX_ARRAY);
+
+	// Draw vertices
+	if (m_bStaticColor)
+		glVertexPointer(3, GL_FLOAT, 0, m_pVertices);
+	else
+		glInterleavedArrays(GL_C3F_V3F, 0, m_pVertices);
+	if (m_bVertexArrayLock) m_glLockArraysEXT(0, m_iVertexCount);
+	glDrawArrays(m_iVertexType, 0, m_iVertexCount);
+	if (m_bVertexArrayLock) m_glUnlockArraysEXT();
+
+	glDisableClientState(GL_VERTEX_ARRAY);
+
+	// Remember we're outside a gBegin()..gEnd() loop
+	m_iVertexType = 0xffff;
+#else
+	AimwxGuiLocker __lock__;
+	glEnd();
+#endif
+}
+
+void GraphicsOutputDevicewxGLCanvas::gText3f(float x,
+                                             float y,
+                                             float z,
+                                             const char *sStr,
+                                             bool bRotated) {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	aimASSERT(m_iVertexType == 0xffff); // Must be outside gBegin*()
+#endif
+
+	if (!m_pFont)
+	  return;
+
+	//! \todo make rotation work
+	if (bRotated)
+		return;
+
+	{
+		AimwxGuiLocker __lock__;
+		/*
+		if (bRotated) {
+			glPushMatrix();
+			glTranslatef(x,y,z);
+			glRotatef(90.0f, 0, 0, 1.0f);
+			glRasterPos3f(0,0,0);
+			m_pFont->Render(sStr);
+			glPopMatrix();
+		} else {
+		*/
+		glRasterPos3f(x, y, z);
+		m_pFont->Render(sStr);
+	}
+}
+
+void GraphicsOutputDevicewxGLCanvas::gRelease() {
+#ifdef WITH_GL_VERTEX_ARRAYS
+	aimASSERT(m_iVertexType == 0xffff); // Must be gEnd()ed
+#endif
+	AimwxGuiLocker __lock__;
+	glEndList();
+	glCallList(m_gllist);
+	//glFlush();
+ 	SwapBuffers(); // Doesn't matter in what context
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDevicewxGLCanvas.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,234 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Output device for output to a wxWidgets OpenGL canvas
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/21
+ * \version \$Id: $
+ */
+
+#ifndef __GRAPHICS_OUTPUT_DEVICE_GL_CANVAS_H__
+#define __GRAPHICS_OUTPUT_DEVICE_GL_CANVAS_H__
+
+// Precompiled wxWidgets headers
+#include "stdwx.h"
+
+// Make sure GLCANVAS is compiled into wxWidgets
+#if !wxUSE_GLCANVAS
+#	error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
+#endif
+
+#if defined (_MACOSX)
+#	include <OpenGL/gl.h>
+#	include <OpenGl/glext.h>
+#elif defined (_WINDOWS)
+#	include <GL/gl.h>
+#	define GL_GET_PROC_ADDRESS wglGetProcAddress
+#else
+#	include <GL/gl.h>
+#	define GL_GET_PROC_ADDRESS(x) glXGetProcAddress((const GLubyte*)x)
+#endif
+/* Define them just ourselves, easiest way to get it working cross-platform
+ * and -Mesa/OpenGL-version. */
+#ifndef APIENTRY
+#	define APIENTRY
+#endif
+typedef void (APIENTRY * LOCAL_PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);
+typedef void (APIENTRY * LOCAL_PFNGLUNLOCKARRAYSEXTPROC) (void);
+
+#ifdef FTGL_SUBDIR
+#	include <FTGL/FTGLBitmapFont.h>
+#else
+#	include <FTGLBitmapFont.h>
+#endif
+
+#include "Output/GraphicsOutputDevice.h"
+
+// Use by default
+#define WITH_GL_VERTEX_ARRAYS
+
+/*!
+ * \class GraphicsOutputDevicewxGLCanvas "Output/GraphicsOutputDevicewxGLCanvas.h"
+ * \brief Output class for output to a wxWidgets OpenGL canvas
+ *
+ * On windows, OpenGL needs a different context when two different threads
+ * want to issue OpenGL commands. This is handled automatically for two
+ * wxThread s.
+ */
+class GraphicsOutputDevicewxGLCanvas : public wxGLCanvas,
+                                       public GraphicsOutputDevice {
+ public:
+  GraphicsOutputDevicewxGLCanvas(Parameters *pParam,
+	                               wxWindow *parent,
+                                 wxWindowID id = wxID_ANY,
+                                 const wxPoint& pos = wxDefaultPosition,
+                                 const wxSize& size = wxDefaultSize,
+                                 long style = 0,
+                                 const wxString& name
+                                     = _T("GraphicsOutputDeviceGLCanvas"));
+  virtual ~GraphicsOutputDevicewxGLCanvas();
+  void OnPaint(wxPaintEvent& event);
+  void OnSize(wxSizeEvent& event);
+  void OnEraseBackground(wxEraseEvent& event);
+
+	/*! \param iVerticesMax Maximum number of vertices to be draw inside a gBegin()..gEnd()
+	 *
+	 *  When iVerticesMax is zero, this variable will not be updated. Note that is _has_
+	 *  to be set at least once before using this class.
+	 */
+	bool Initialize(unsigned int iVerticesMax);
+	bool Initialize();
+
+	void Start();
+
+  void gGrab();
+  void gBeginLineStrip();
+  void gBeginQuadStrip();
+	using GraphicsOutputDevice::gVertex3f; // Because we overload it
+	void gVertex3f(float x, float y, float z);
+	void gColor3f(float r, float g, float b);
+  void gEnd();
+	void gText3f(float x, float y, float z, const char *sStr, bool bRotated = false);
+	void gRelease();
+ protected:
+	/*! \brief Smarter SetCurrent() replacement
+	 *  \return true on success, false on error
+	 *
+	 *  This function tries GetContext() first. If that fails, it returns false.
+	 */
+	bool SetCurrent();
+	void Render();
+
+	/*! \brief Initialize the OpenGL environment.
+	 *
+	 *  This must be called after the canvas is realized but before any other
+	 *  OpenGL operation is done. Make sure to run SetCurrent() beforehand.
+	 *  Usually only needed when m_init is false.
+	 */
+  void InitGL();
+
+	/*! \brief Handle a resize (notify OpenGL of the new area)
+	 *
+	 *  This is a separate function, because in multi-threading environments
+	 *  multiple contexts have to call it.
+	 */
+	void DoResize();
+
+	/*! \brief Only need to initialize OpenGL once.
+	 *
+	 *  This is false at start and true when OpenGL has been initialized.
+	 *  No mutex needed, since it's set once at InitGL() and only read afterwards.
+	 */
+  bool m_init;
+
+	/*! \brief Vertex list for last drawing so it can be updated on repaint.
+	 *
+	 *  No mutex needed, since it's set once at InitGL() and only read afterwards.
+	 */
+  GLuint m_gllist;
+
+	//! \brief OpenGL context for worker thread, use when wxIsMainThread() returns false.
+	wxGLContext *m_pWorkerContext;
+
+	//! \brief Mutex for inter-thread communication
+	wxMutex s_mutexOpenGL;
+
+	//! \brief When true, OpenGL needs to be reinitialized (in the worker thread)
+	bool s_bWorkerNeedsInit;
+
+	//! \brief OpenGL attributes used for initialization.
+  static int GLAttrlist[];
+
+	//! \brief Whether to use anti-aliasing or not
+	bool m_bAntialiasing;
+
+	//! \brief FTGL Font class
+	FTFont *m_pFont;
+	//! \brief Current font filename
+	const char *m_sFontFile;
+	//! \brief Current font size
+	int m_iFontsize;
+
+#if defined(WITH_GL_VERTEX_ARRAYS) || defined(DOXYGEN)
+	//! \brief OpenGL vertex type of the current m_pVertices, or 0xffff is outside gBegin()..gEnd()
+	int m_iVertexType;
+	//! \brief Maximum number of vertices begin gBegin()..gEnd()
+	unsigned int m_iVerticesMax;
+	//! \brief Vertex array to draw at gEnd(), this becomes m_pVertices[m_iVerticesMax*3]
+	GLfloat *m_pVertices;
+	//! \brief The current number of vertices inside m_pVertices
+	unsigned int m_iVertexCount;
+
+	/*! \brief Whether to use coloring in vertex lists
+	 *
+	 *  This variable must not change after Initialize(), or the program may crash.
+	 *  When this variable is true, color information is stored in vertex lists. If
+	 *  it is false, only vertex data is stored.
+	 *
+	 *  This variable exists for performance reasons, but is currently only set in
+	 *  the constructor of this object.
+	 */
+	bool m_bStaticColor;
+	//! \brief Current color for vertex list drawing
+	float m_fCurColorR, m_fCurColorG, m_fCurColorB;
+
+	//! \brief Whether to use vertex array locking or not
+	bool m_bVertexArrayLock;
+	//! \brief Pointer to vertex array locking function; can be NULL.
+	LOCAL_PFNGLLOCKARRAYSEXTPROC m_glLockArraysEXT;
+	//! \brief Pointer to vertex array unlocking function; can be NULL.
+	LOCAL_PFNGLUNLOCKARRAYSEXTPROC m_glUnlockArraysEXT;
+#endif
+
+	/*! \brief wxMutexGuiEnter() / wxMutexGuiLeave() wrapper
+	 *
+	 *  This is a wxMutexLocker-alike for the main gui mutex. Any method that
+	 *  is public, can be called from within another thread and does OpenGL or
+	 *  other gui calls must use this. Example:
+	 *  \code
+	 *  void DoFoo() {
+	 *    AimwxGuiLocker __lock__;
+	 *    glAmazingMethod();
+	 *  }
+	 *  \endcode
+	 *
+	 *  It is mostly on X-Windows (Xorg/XFree86) that the gui mutex appears to
+	 *  be needed. Otherwise the error "Xlib: unexpected async reply" can occur.
+	 *
+	 *  On windows, the ui may occasionally lock up for a short while with these
+	 *  mutexes. Since they aren't really needed on that platform, it's left out
+	 *  alltogether.
+	 */
+	class AimwxGuiLocker {
+	 public:
+		inline AimwxGuiLocker() {
+#ifndef _WINDOWS
+			if (!wxIsMainThread()) wxMutexGuiEnter();
+#endif
+		}
+		inline ~AimwxGuiLocker() {
+#ifndef _WINDOWS
+			if (!wxIsMainThread()) wxMutexGuiLeave();
+#endif
+		}
+	};
+  DECLARE_EVENT_TABLE()
+};
+#endif /* __GRAPHICS_OUTPUT_DEVICE_GL_CANVAS_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/GraphAxisSpec.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,126 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "Support/Common.h"
+#include <string.h>
+#include <stdio.h>
+
+#include "Modules/Output/Graphics/GraphAxisSpec.h"
+
+GraphAxisSpec::GraphAxisSpec(float fMin, float fMax, Scale::ScaleType iScale) {
+	m_pScale = NULL;
+	m_sLabel = NULL;
+	SetDisplayRange(fMin, fMax);
+	SetDisplayScale(iScale);
+}
+
+GraphAxisSpec::GraphAxisSpec() {
+	m_pScale = NULL;
+	m_sLabel = NULL;
+	m_fMin = 0;
+	m_fMax = 0;
+}
+
+GraphAxisSpec::~GraphAxisSpec() {
+	DELETE_IF_NONNULL(m_pScale);
+}
+
+void GraphAxisSpec::SetDisplayRange(float fMin, float fMax) {
+	AIM_ASSERT(fMin <= fMax);
+	m_fMin = fMin;
+	m_fMax = fMax;
+	if (m_pScale)
+		m_pScale->FromLinearScaledExtrema(m_fMin, m_fMax);
+}
+
+void GraphAxisSpec::SetDisplayScale(Scale::ScaleType iScale) {
+	DELETE_IF_NONNULL(m_pScale);
+	m_pScale = Scale::Create(iScale);
+	aimASSERT(m_pScale);
+	m_pScale->FromLinearScaledExtrema(m_fMin, m_fMax);
+}
+
+bool GraphAxisSpec::Initialize(Parameters *parameters,
+                               const char *sPrefix,
+                               float fMin,
+                               float fMax,
+                               Scale::ScaleType iScale) {
+	AIM_ASSERT(pParam);
+	AIM_ASSERT(sPrefix && sPrefix[0]!='\0');
+	char sParamName[Parameters::MaxParamNameLength];
+
+	//! The following parameters are recognized for each axis:
+	//!   - \c "<prefix>.min", the minimum value; a float or \c 'auto'
+	snprintf(sParamName, sizeof(sParamName)/sizeof(sParamName[0]),
+          "%s.min",
+          sPrefix);
+	if (pParam->IsSet(sParamName)) {
+		if (strcmp(parameters->GetString(sParamName), "auto") == 0)
+			m_fMin = fMin;
+		else
+			m_fMin = parameters->GetFloat(sParamName);
+	}
+
+	//!   - \c "<prefix>.max", the maximum value; a float or \c 'auto'
+	snprintf(sParamName, sizeof(sParamName)/sizeof(sParamName[0]),
+          "%s.max",
+          sPrefix);
+	if (pParam->IsSet(sParamName)) {
+		if (strcmp(parameters->GetString(sParamName), "auto")==0)
+			m_fMax = fMax;
+		else
+			m_fMax = parameters->GetFloat(sParamName);
+	}
+
+	// Make sure ranges are updated properly
+	SetDisplayRange(m_fMin, m_fMax);
+
+	//!   - \c "<prefix>.scale", the scale; one of \c 'auto',
+  //!     \c 'linear', \c 'erb' or \c 'log'
+	snprintf(sParamName, sizeof(sParamName)/sizeof(sParamName[0]),
+           "%s.scale",
+           sPrefix);
+	if (pParam->IsSet(sParamName)) {
+		// Scale change, we updated min/max values already so no need to
+		Scale::ScaleType iThisScale;
+		const char *sVal = parameters->GetString(sParamName);
+		if (strcmp(sVal, "auto")==0)
+			iThisScale = iScale;
+		else if (strcmp(sVal, "linear")==0)
+			iThisScale = Scale::SCALE_LINEAR;
+		else if (strcmp(sVal, "erb")==0)
+			iThisScale = Scale::SCALE_ERB;
+		else if (strcmp(sVal, "log")==0)
+			iThisScale = Scale::SCALE_LOG;
+		else {
+			AIM_ERROR(_T("Unrecognized scale type in parameter '%s': '%s'"),
+                  sParamName,
+                  sVal);
+			return false;
+		}
+		SetDisplayScale(iThisScale);
+	}
+
+	//!   - \c "<prefix>.label", the label; a string
+	snprintf(sParamName, sizeof(sParamName)/sizeof(sParamName[0]),
+           "%s.label",
+           sPrefix);
+	if (parameters->IsSet(sParamName))
+		m_sLabel = parameters->GetString(sParamName); // Assumes strings remains valid
+
+	return true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/GraphAxisSpec.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,88 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef __GRAPH_AXIS_SPEC_H__
+#define __GRAPH_AXIS_SPEC_H__
+
+#include "Support/Parameters.h"
+#include "Modules/Output/Graphics/Scale/Scale.h"
+
+/*! \class GraphAxisSpec "Output/GraphAxisSpec.h"
+ *  \brief Axis specification for a GraphicsView
+ */
+class GraphAxisSpec {
+public:
+	/*! \brief Create a new GraphAxisSpec
+	 *  \param fMin Minimum value on the axis to show
+	 *  \param fMax Maximum value on the axis to show
+	 *  \param iScale Scale to use
+	 *
+	 *  Please see SetScale() and SetRange() for more details.
+	 */
+	GraphAxisSpec(float fMin, float fMax, Scale::ScaleType iScale);
+	//! \brief Create a new GraphAxisSpec from defaults
+	GraphAxisSpec();
+
+	~GraphAxisSpec();
+
+	/*! \brief Set the scale to use
+	 *  \param iScale Scale to use
+	 */
+	void SetDisplayScale(Scale::ScaleType iScale);
+	/*! \brief Set the minumum and maximum values to show on the axis
+	 *  \param fMin Minimum value on the axis to show
+	 *  \param fMax Maximum value on the axis to show
+	 *
+	 *  Either fMin _must_ be smaller than fMax, or fMin==fMax but then
+	 *  this function must be called later to fulfil the former condition
+	 *  before it is used to scale data.
+	 */
+	void SetDisplayRange(float fMin, float fMax);
+	/*! \brief Set the label of this axis
+	 *  \param sLabel New label, or NULL to remove label
+	 */
+	void SetLabel(const char *sLabel);
+
+	/*! \brief Read axis specification from parameter store
+	 *  \param pParam Parameter store to read from
+	 *  \param sPrefix Prefix to use, e.g. "view.x"
+	 *  \param fMin Default minumum value for 'auto'
+	 *  \param fMax Default maximum value for 'auto'
+	 *  \param iScale Default scale for 'auto'
+	 *  \return true if no error occured in reading
+	 */
+	bool Initialize(Parameters *pParam,
+                  const char *sPrefix,
+                  float fMin,
+                  float fMax,
+                  Scale::ScaleType iScale);
+
+protected:
+	//! \brief Minimum value on the axis to display
+	float m_fMin;
+	//! \brief Maximum value on the axis to display
+	float m_fMax;
+	//! \brief Scale to use
+	Scale *m_pScale;
+	//! \brief Axis label, NULL for no label
+	const char *m_sLabel;
+
+	friend class GraphicsView;
+	friend class GraphicsViewTime;
+};
+
+#endif /* __GRAPH_AXIS_SPEC_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/GraphicsView.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,234 @@
+// Copyright 2006, Willem van Engen
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "Support/Common.h"
+
+#include "Output/GraphicsView.h"
+#include "Output/GraphicsOutputDevice.h"
+
+GraphicsView::GraphicsView(Parameters *parameters) : Module(parameters) {
+	module_description_ = "";
+  module_identifier_ = "graphics";
+  module_type_ = "output";
+  module_version_ = "$Id: $";
+  
+	m_pDev = NULL;
+	m_bPlotLabels = false;
+	m_pAxisX = new GraphAxisSpec();
+	AIM_ASSERT(m_pAxisX);
+	m_pAxisY = new GraphAxisSpec();
+	AIM_ASSERT(m_pAxisY);
+	m_pAxisFreq = new GraphAxisSpec();
+	AIM_ASSERT(m_pAxisFreq);
+
+  m_pAxisY->Initialize(m_pParam,
+                       _S("graph.y"),
+                       -1,
+                       1,
+                       Scale::SCALE_LINEAR);
+	m_fMarginLeft = m_pParam->GetFloat(_S("graph.margin.left"));
+	m_fMarginRight = m_pParam->GetFloat(_S("graph.margin.right"));
+	m_fMarginTop = m_pParam->GetFloat(_S("graph.margin.top"));
+	m_fMarginBottom = m_pParam->GetFloat(_S("graph.margin.bottom"));
+	m_bPlotLabels = m_pParam->GetBool(_S("graph.plotlabels"));
+
+	const char *sGraphType = m_pParam->GetString(_S("graph.type"));
+	if (strcmp(sGraphType, _S("line"))==0)
+		m_iGraphType = GraphTypeLine;
+	else if (strcmp(sGraphType, _S("colormap"))==0)
+		m_iGraphType = GraphTypeColormap;
+	else if (strcmp(sGraphType, _S("none"))==0)
+		m_iGraphType = GraphTypeNone;
+	else {
+		ret = false;
+		AIM_ERROR(_T("Unrecognized graph type: '%s'"), sGraphType);
+	}
+
+	if (strcmp(m_pParam->GetString(_S("graph.mindistance")),"auto") == 0)
+		// -1 means detect later, based on type and Fire() argument
+		m_fMinPlotDistance = -1;
+	else
+		m_fMinPlotDistance = m_pParam->GetFloat(_S("graph.mindistance"));
+
+}
+
+GraphicsView::~GraphicsView() {
+	DELETE_IF_NONNULL(m_pAxisX);
+	DELETE_IF_NONNULL(m_pAxisY);
+	DELETE_IF_NONNULL(m_pAxisFreq);
+}
+
+bool
+
+bool GraphicsView::InitializeInternal(const SignalBank &bank) {
+	if (!m_pDev) {
+		LOG_ERROR("Output device not connected");
+		return false;
+	}
+
+  float y_min = bank.centre_frequency(0);
+  float y_max = bank.centre_frequency(bank.channel_count() - 1);
+	if (!m_pAxisFreq->Initialize(m_pParam,
+                               "graph.freq",
+		                           y_min,
+		                           y_max,
+		                           Scale::SCALE_ERB)) {
+    LOG_ERROR("");
+    return false;
+  }
+
+  float x_min = 0.0;
+  float x_max = 1000.0 * bank.buffer_length() / bank.sample_rate();
+	if (!m_pAxisX->Initialize(m_pParam,
+                            "graph.x",
+		                        x_min,
+                            x_max,
+		                        Scale::SCALE_LINEAR)) {
+     LOG_ERROR("");
+     return false;
+  }
+
+	/* Inform graphics output of maximum number of vertices between
+	 * gBegin*() and gEnd(), for any type of plot. Colormap needs most.
+	 */
+	if (!m_pDev->Initialize(MAX(10, bank.buffer_length() * 2 + 2))) {
+    LOG_ERROR("");
+    return false;
+  }
+}
+
+void GraphicsView::Process(const SignalBank &bank) {
+	float height = 1.0 / bank.channel_count();
+	float heightMinMargin = height * (1.0f - m_fMarginBottom - m_fMarginTop);
+	float xScaling = 1.0f;
+
+	m_pDev->gGrab();
+	PlotAxes(bank);
+	m_pDev->gColor3f(1.0f, 1.0f, 0.8f);
+	for (int i = 0; i < bank.channel_count(); i++) {
+		float yOffs = bank.centre_frequency(i);
+		yOffs = m_pAxisFreq->m_pScale->FromLinearScaled(yOffs) + 0.5f;
+		/* Don't plot below zero and stop when above 1, since yOffs is
+     * monotonically increasing. Because of rounding errors, we need
+     * to check for yOffs < -1e-6 instead of yOffs < 0. */
+		if (yOffs < -1e-6)
+      continue;
+		if (yOffs > 1)
+      break;
+
+    // Scale to single channel graphing.
+		yOffs = yOffs * (1.0f - height) + height / 2.0;
+		yOffs = yOffs * (1.0f - m_fMarginTop - m_fMarginBottom) + m_fMarginBottom;
+		PlotData(bank[i], yOffs, heightMinMargin, xScaling);
+	}
+	m_pDev->gRelease();
+}
+
+void GraphicsView::SetAxisScale(Scale::ScaleType iHori,
+                                Scale::ScaleType iVert,
+                                Scale::ScaleType iFreq) {
+	AIM_ASSERT(m_pAxisX);
+	AIM_ASSERT(m_pAxisY);
+	AIM_ASSERT(m_pAxisFreq);
+	m_pAxisX->SetDisplayScale(iHori);
+	m_pAxisY->SetDisplayScale(iVert);
+	m_pAxisFreq->SetDisplayScale(iFreq);
+}
+
+void GraphicsView::BeginDataStrip() {
+	m_iPrevValEqual=0;
+	m_bFirstPoint=true;
+	switch(m_iGraphType) {
+	case GraphTypeLine:
+		m_pDev->gBeginLineStrip();
+		break;
+	case GraphTypeColormap:
+		m_pDev->gBeginQuadStrip();
+		break;
+	case GraphTypeNone:
+		// Nothing: just for testing computation overhead of graphing
+		break;
+	}
+}
+
+void GraphicsView::PlotDataPoint(float x,
+                                 float y,
+                                 float val,
+                                 float height,
+                                 bool isLast) {
+	AIM_ASSERT(m_pDev);
+
+	/* Reduce the number of points plotted by eliminating duplicate values:
+	 *
+	 *             oooo                         o--o
+	 *                 o                       /    o
+	 *        oooo       ooo       =>      o--o      o--o
+	 *    oooo              ooo        o--o              o-o
+	 *
+	 * with 'o' points that are plotted, and '-' by the graphics output
+	 * device interpolated points. We could be smarter and include
+	 * first-order interpolation, but we leave that as an exercise for
+	 * the reader. Please send your patches :)
+	 *
+	 * So, we don't draw points that are too close to the previous value.
+	 * But if the value changes (or it's the last point), we must draw the
+	 * previous point too.
+	 */
+	if (!m_bFirstPoint
+      && !isLast
+      && fabs(m_fPrevVal-val) < m_fMinPlotDistance) {
+		m_iPrevValEqual++;
+		// Don't set m_fPrevVal to avoid not catching slow changes
+		m_fPrevX = x;
+		m_fPrevY = y;
+		m_fPrevHeight = height;
+		return;
+	} else {
+		if (m_iPrevValEqual > 0) {
+			// Draw previous point
+			PlotDataPointDirect(m_fPrevX, m_fPrevY, m_fPrevVal, m_fPrevHeight);
+		}
+		m_iPrevValEqual = 0;
+		m_fPrevVal = val;
+		m_bFirstPoint = false;
+	}
+	PlotDataPointDirect(x, y, val, height);
+}
+
+void GraphicsView::PlotDataPointDirect(float x,
+                                       float y,
+                                       float val,
+                                       float height) {
+	// Draw it in the right way
+	switch(m_iGraphType) {
+	case GraphTypeLine:
+		m_pDev->gVertex2f(x, y + val * height);
+		break;
+	case GraphTypeColormap:
+		//! \todo make it a real colormap instead of grayscale
+		m_pDev->gColor3f(val + 0.5, val + 0.5, val + 0.5);
+		m_pDev->gVertex2f(x, y - height / 2);
+		m_pDev->gVertex2f(x, y + height / 2);
+		break;
+	case GraphTypeNone:
+		// Nothing: just for testing computation overhead of graphing
+		break;
+	default:
+		// Shouldn't happen
+		AIM_ASSERT(0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/GraphicsView.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,182 @@
+// Copyright 2006-2010, Willem van Engen, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief General graphics view definition
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/26
+ * \version \$Id: $
+ */
+
+#ifndef __GRAPHICS_VIEW_H__
+#define __GRAPHICS_VIEW_H__
+
+#include "Support/Parameters.h"
+#include "Support/SignalBank.h"
+#include "Modules/Output/Graphics/Scale/Scale.h"
+#include "Modules/Output/Graphics/Devices/GraphicsOutputDevice.h"
+#include "Modules/Output/Graphics/GraphAxisSpec.h"
+
+/*!
+ * \class GraphicsView "Modules/Output/Graphics/GraphicsView.h"
+ * \brief General graphics view module
+ *
+ * This class is a general graph drawing class for one or more Signals.
+ * \sa Signal, SignalBank
+ */
+class GraphicsView : public Module {
+public:
+	/*! \brief Create a new view
+	 *  \param params A parameter store
+	 *
+	 */
+	GraphicsView(Parameters *params);
+	virtual ~GraphicsView();
+
+	/*! \brief Create a copy of this GraphicsView
+	 *  \param pDev Output device to bind to
+	 *  \return Newly created GraphicsView, to be freed by the caller.
+	 *  \deprecated Possibly, use Initialize()
+	 */
+	virtual GraphicsView *Clone(GraphicsOutputDevice *pDev) = 0;
+
+	/*! \brief (Re-)initialize this graphics view and it's device
+	 *  \param pParam Main parameter store to bind to
+	 *  \param pDev Graphics output device to draw to
+	 *  \return true on success, false on error
+	 *
+	 *  It is possible to call Initialize() multiple times for binding
+	 *  to another graphics device or updating the parameters. This method
+	 *  does read all parameters than can be changed at run-time.
+	 *
+	 *  One of the Initialize() functions _must_ be called before actually
+	 *  using it for plotting.
+	 */
+	virtual bool InitializeInternal(const SignalBank &bank);
+
+	/*! \brief Set the axes' scale
+	 *  \param iXScale Scale type of the horizontal axis
+	 *  \param iYScale Scale type of the vertical axis for signal data
+	 *  \param iFreqScale Scale type of the vertical axis
+	 *  \deprecated Possibly, use AimParameters and Initialize
+	 *  \todo const arguments
+	 */
+	virtual void SetAxisScale(Scale::ScaleType iXScale,
+                            Scale::ScaleType iYScale,
+                            Scale::ScaleType iFreqScale);
+
+
+	virtual void Process(const SignalBank &bank);
+
+ protected:
+	/*! \brief Plot the data of a signal
+	 *  \param pSig Signal to plot
+	 *  \param yOffset Vertical offset (between 0 and 1), where to plot 0 signal value.
+	 *  \param height Height of the signal to plot (between 0 and 1)
+	 *  \param xScale Scaling in x-direction. 1.0 makes it cover the whole length. 0.5 only the left half.
+	 */
+	virtual void PlotData(const vector<float> &signal,
+                        float yOffset,
+                        float height,
+                        float xScale) = 0;
+
+	/*! \brief Plot the axes for a signal bank
+	 *  \param pSig Signal to plot the axes for
+	 */
+	virtual void PlotAxes(const SignalBank &bank) = 0;
+
+	//! \brief Calls the correct m_pDev->gBegin*() for the current PlotType
+	virtual void BeginDataStrip();
+	/*! \brief Plot a data point (with smart reduction)
+	 *  \param x X-coordinate of point (in OpenGL space)
+	 *  \param y y-coordinate of point (in OpenGL space)
+	 *  \param val Value to plot (from -0.5..0.5)
+	 *  \param height Height to use for plotting (in OpenGL space)
+	 *  \param isLast True if this is the last point of this batch to draw
+	 *
+	 *  This method tries to reduce the number of actual graphics output
+	 *  operations using it's interpolation methods (e.g., a line).
+	 */
+	virtual void PlotDataPoint(float x,
+                             float y,
+                             float val,
+                             float height,
+                             bool isLast);
+
+	/*! \brief Plot a data point (directly)
+	 *  \param x X-coordinate of point (in OpenGL space)
+	 *  \param y y-coordinate of point (in OpenGL space)
+	 *  \param val Value to plot (from -0.5..0.5)
+	 *  \param height Height to use for plotting (in OpenGL space)
+	 */
+	virtual void PlotDataPointDirect(float x, float y, float val, float height);
+
+   /*! \brief Plot a strobe point
+    *  \param x X-coordinate of centre (in OpenGL space)
+    *  \param y Y-coordinate of centre (in OpenGL space)
+    *  \param size Size of the block drawn
+    *  \param colour_r Red colour intensity
+    *  \param colour_g Green colour intensity
+    *  \param colour_b Blue colour intensity
+    */
+	virtual void PlotStrobePoint(float x,
+                               float y,
+                               float size,
+                               float color_r,
+                               float color_g,
+                               float color_b);
+
+	//! \brief Where to plot to
+	GraphicsOutputDevice *m_pDev;
+
+	//! \brief Main parameter store
+	Parameters *m_pParam;
+
+	//! \brief Axes specifications
+	GraphAxisSpec *m_pAxisX, *m_pAxisY, *m_pAxisFreq;
+
+	//! \brief Graph margins
+	float m_fMarginLeft, m_fMarginRight, m_fMarginTop, m_fMarginBottom;
+
+	//! \brief Whether labels are plotted or not
+	bool m_bPlotLabels;
+
+	//! \brief Graphics device type
+	enum GraphType {
+		GraphTypeNone,
+    GraphTypeLine,
+    GraphTypeColormap
+	};
+	GraphType m_iGraphType;
+
+	/*! \brief Minimum distance between subsequent values for plotting a point
+	 *
+	 *  This value is set in Initialize() from parameter graph.mindistance.
+	 */
+	float m_fMinPlotDistance;
+
+	//! \brief true if this next point is the first of the strip
+	bool m_bFirstPoint;
+	//! \brief Value of previously plotted point
+	float m_fPrevVal, m_fPrevX, m_fPrevY, m_fPrevHeight;
+	//! \brief Number of times m_fValPrev was within range m_fMinPlotDistance
+	int m_iPrevValEqual;
+};
+
+#endif /* __GRAPHICS_VIEW_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/GraphicsViewTime.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,139 @@
+// Copyright 2006-2010, Willem van Engen, Thomas Walters
+//
+// AIM-C: A C++ implementation of the Auditory Image Model
+// http://www.acousticscale.org/AIMC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/*!
+ * \file
+ * \brief Time-representation graphics view
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/26
+ * \version \$Id: GraphicsViewTime.cpp 607 2008-06-25 00:14:14Z tom $
+ */
+
+#include "Support/Common.h"
+
+#include <stdio.h>
+
+#include "Support/SignalBank.h"
+#include "Modules/Output/Graphics/GraphicsView.h"
+#include "Modules/Output/Graphics/GraphicsViewTime.h"
+
+GraphicsViewTime::GraphicsViewTime(Parameters *pParam)
+	: GraphicsView(pParam) {
+}
+
+GraphicsViewTime *GraphicsViewTime::Clone(GraphicsOutputDevice *pDev) {
+	GraphicsViewTime *pView = new GraphicsViewTime(m_pParam);
+	// Copy everything
+	pView->m_pAxisX->SetDisplayRange(m_pAxisX->m_fMax, m_pAxisX->m_fMin);
+	pView->m_pAxisX->SetDisplayScale(m_pAxisX->m_pScale->getType());
+	pView->m_pAxisY->SetDisplayRange(m_pAxisY->m_fMax, m_pAxisY->m_fMin);
+	pView->m_pAxisY->SetDisplayScale(m_pAxisY->m_pScale->getType());
+	pView->m_pAxisFreq->SetDisplayRange(m_pAxisFreq->m_fMax, m_pAxisFreq->m_fMin);
+	pView->m_pAxisFreq->SetDisplayScale(m_pAxisFreq->m_pScale->getType());
+	return pView;
+}
+
+void GraphicsViewTime::PlotAxes(const SignalBank &bank) {
+	m_pDev->gColor3f(0.0f, 0.7f, 0.7f);
+	// Vertical axis
+	m_pDev->gBeginLineStrip();
+	m_pDev->gVertex2f(m_fMarginLeft, m_fMarginBottom);
+	m_pDev->gVertex2f(m_fMarginLeft, 1.0f - m_fMarginTop);
+	m_pDev->gEnd();
+	// Horizontal axis
+	m_pDev->gBeginLineStrip();
+	m_pDev->gVertex2f(m_fMarginLeft, m_fMarginBottom);
+	m_pDev->gVertex2f(1.0f-m_fMarginRight, m_fMarginBottom);
+	m_pDev->gEnd();
+
+	if (!m_bPlotLabels)
+		return;
+
+	// Labels
+	char sTxt[80];
+	snprintf(sTxt, sizeof(sTxt) / sizeof(sTxt[0]),
+           _S("%s [%.0f..%.0f Hz, %s scale]"),
+		       m_pAxisFreq->m_sLabel ? m_pAxisFreq->m_sLabel : "",
+		       m_pAxisFreq->m_fMin, m_pAxisFreq->m_fMax,
+		       m_pAxisFreq->m_pScale->getName());
+	m_pDev->gText2f(0.0025f, 0.35f, sTxt, true);
+	if (m_bPlotScaled) {
+		snprintf(sTxt, sizeof(sTxt) / sizeof(sTxt[0]),
+             _S("%s [cycles, %s scale]"),
+			       m_pAxisX->m_sLabel ? m_pAxisX->m_sLabel : "",
+			       m_pAxisX->m_pScale->getName());
+	} else {
+		snprintf(sTxt, sizeof(sTxt) / sizeof(sTxt[0]),
+             _S("%s [%.2f..%.2f ms, %s scale]"),
+			       m_pAxisX->m_sLabel ? m_pAxisX->m_sLabel : "",
+			       m_pAxisX->m_fMin,
+             m_pAxisX->m_fMax,
+			       m_pAxisX->m_pScale->getName());
+	}
+	m_pDev->gText2f(m_fMarginLeft, 0.0025f, sTxt, false);
+
+	// Frame time
+	snprintf(sTxt, sizeof(sTxt)/sizeof(sTxt[0]), _S("t=%.0f ms"),
+		       pBank->getSampleTime(0));
+	m_pDev->gText2f(0.8f, 0.0025f, sTxt, false);
+}
+
+void GraphicsViewTime::PlotData(const vector<float> &signal,
+                                float sample_rate,
+                                float yOffset,
+                                float height,
+                                float xScale) {
+	AIM_ASSERT(pSig);
+	AIM_ASSERT(xScale >= 0 && xScale <= 1);
+	AIM_ASSERT(height > 0  && height <= 1);
+	AIM_ASSERT(yOffset >= 0 && yOffset <= 1);
+	AIM_ASSERT(m_pAxisX && m_pAxisX->m_pScale);
+	AIM_ASSERT(m_pAxisY && m_pAxisY->m_pScale);
+
+	// Make sure we get time is ms as x value
+	xScale *= 1000.0 / sample_rate;
+	m_pDev->gColor3f(1.0f, 1.0f, 0.8f);
+	BeginDataStrip();
+
+	// Draw the signal.
+	float x = 0;
+	float y = 0;
+	for (int i = 0; i < signal.size(); i++) {
+		// Find out where to draw and do so
+		x = xScale * i;
+		y = signal[i];
+		x = m_pAxisX->m_pScale->FromLinearScaled(x) + 0.5f;
+		y = m_pAxisY->m_pScale->FromLinearScaled(y);
+
+		if (x < 0.0)
+      continue;
+
+		// Now fit it into the drawing area
+		x = x * (1.0f - m_fMarginLeft - m_fMarginRight) + m_fMarginLeft;	// fit inside x-axis area
+		PlotDataPoint(x, yOffset, y, height, false);
+		/* There's no point in drawing anything when x>1.0, outside of view.
+		 * x is continuously increasing, so we can just stop completely. We need to
+		 * plot this point however, to finish the final line. */
+		if (x > 1.0)
+			break;
+	}
+	// Redraw the last point in case it's needed
+	PlotDataPoint(x, yOffset, y, height, true);
+
+	m_pDev->gEnd();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/GraphicsViewTime.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,43 @@
+/*!
+ * \file
+ * \brief Time-representation graphics view definition
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/26
+ * \version \$Id: GraphicsViewTime.h 459 2007-11-08 11:50:04Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#ifndef __GRAPHICS_VIEW_TIME_H__
+#define __GRAPHICS_VIEW_TIME_H__
+
+#include "Support/Signal.h"
+#include "Support/SignalBank.h"
+#include "Output/GraphicsOutputDevice.h"
+#include "Output/GraphicsView.h"
+
+/*!
+ * \class GraphicsViewTime "Output/GraphicsViewTime.h"
+ * \brief Time-definition graphics view class
+ *
+ * This plots a Signal or SignalBank in the time domain.
+ */
+class GraphicsViewTime : public GraphicsView {
+public:
+	/*! \brief Create a new view
+	 *  \param pParam Main parameter store
+	 */
+	GraphicsViewTime(AimParameters *pParam);
+	virtual ~GraphicsViewTime() { };
+
+	virtual GraphicsViewTime *Clone(GraphicsOutputDevice *pDev);
+
+private:
+	void PlotData(Signal* pSig, float yOffset, float height, float xScale = 1.0);
+	void PlotAxes(Signal* pSig);
+	void PlotAxes(SignalBank* pBank);
+
+};
+
+#endif /* __GRAPHICS_VIEW_TIME_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Scale/Scale.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,106 @@
+/*!
+ * \file
+ * \brief Frequency scale for generating filter banks and their frequencies
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/28
+ * \version \$Id: Scale.cpp 459 2007-11-08 11:50:04Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#include "Support/common.h"
+#include "Support/util.h"
+#include "Modules/Scale/Scale.h"
+#include "Modules/Scale/ScaleLinear.h"
+#include "Modules/Scale/ScaleERB.h"
+#include "Modules/Scale/ScaleLog.h"
+#include "Modules/Scale/ScaleLogScaled.h"
+
+Scale *Scale::Create(ScaleType iType, unsigned int min, unsigned int max, float density)
+{
+	switch(iType) {
+	case SCALE_LINEAR:
+		return static_cast<Scale*>(new ScaleLinear(min, max, density));
+	case SCALE_ERB:
+		return static_cast<Scale*>(new ScaleERB(min, max, density));
+	case SCALE_LOG:
+		return static_cast<Scale*>(new ScaleLog(min, max, density));
+	case SCALE_LOGSCALED:
+		return static_cast<Scale*>(new ScaleLogScaled(min, max, density));
+	default:
+		aimASSERT(0);
+		break;
+	}
+	// Unreachable code
+	aimASSERT(0);
+	return NULL;
+}
+
+Scale *Scale::Create(ScaleType iType)
+{
+	return Create(iType, 0, 0, 0);
+}
+
+Scale *Scale::Clone()
+{
+	Scale *pScale = Create(m_iType, m_iMin, m_iMax, m_fDensity);
+	aimASSERT(pScale);
+	pScale->m_fScaledCurHalfSum = m_fScaledCurHalfSum;
+	pScale->m_fScaledCurDiff = m_fScaledCurDiff;
+	return pScale;
+}
+
+float Scale::FromLinearScaled(float fVal)
+{
+	/*! This function returns
+	 *    ( FromLinear(fVal) - (fMinScaled+fMaxScaled)/2 ) / (fMaxScaled-fMinScaled)
+	 */
+	float fValScaled = FromLinear(fVal);
+	return (fValScaled - m_fScaledCurHalfSum) / m_fScaledCurDiff;
+}
+void Scale::FromLinearScaledExtrema(float fMin, float fMax)
+{
+	float fMinScaled = FromLinear(fMin);
+	float fMaxScaled = FromLinear(fMax);
+	m_fScaledCurHalfSum = (fMinScaled+fMaxScaled)/2;
+	m_fScaledCurDiff = fMaxScaled-fMinScaled;
+	m_fMin = fMin;
+	m_fMax = fMax;
+}
+void Scale::FromLinearScaledExtrema(Scale *pScale)
+{
+	aimASSERT(pScale);
+	FromLinearScaledExtrema(pScale->m_fMin, pScale->m_fMax);
+}
+
+SignalBank* Scale::CreateSignalBank(unsigned int iChannels, unsigned int iBufferlength, unsigned int iSamplerate)
+{
+	SignalBank *pBank;
+	double intpart, fracpart;
+	fracpart = modf((m_iMax-m_iMin)*m_fDensity, &intpart);
+	unsigned int nBankChan = (unsigned int) intpart;
+	if (fracpart >= 0.5f)
+        nBankChan++;
+
+	pBank = new SignalBank(iChannels, iBufferlength, iSamplerate, nBankChan);
+	aimASSERT(pBank);
+
+	float scaleDelta = ( FromLinear(m_iMax) - FromLinear(m_iMin) ) / (nBankChan-1);
+	float scaleCur = FromLinear(m_iMin);
+
+	for (unsigned int i=0; i<nBankChan; i++) {
+		pBank->setCentreFrequency(i, ToLinear(scaleCur));
+		scaleCur+=scaleDelta;
+	}
+
+	return pBank;
+}
+
+
+
+SignalBank* Scale::CreateSignalBank(Signal* pSig) {
+	aimASSERT(pSig);
+	return CreateSignalBank(pSig->getAudioChannels(), pSig->getBufferlength(), pSig->getSamplerate());
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Scale/Scale.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,159 @@
+/*!
+ * \file
+ * \brief General definition for frequency scale management
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/26
+ * \version \$Id: Scale.h 576 2008-05-29 12:51:16Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#ifndef __MODULE_SCALE_H__
+#define __MODULE_SCALE_H__
+
+#include "Support/SignalBank.h"
+
+/*!
+ * \class Scale "Modules/Scale/Scale.h"
+ * \brief General class for frequency scale management
+ *
+ * This class
+ *   - Converts between a linear and specified scale
+ *   - Creates filterbank signals based on that scale
+ *   - Creates any of the children filterbanks with Scale::Create.
+ * To create a new scale, all you need to do is override ToLinear() and
+ * FromLinear(). See existing scales like ScaleERB for example:
+ */
+class Scale;
+class Scale {
+public:
+	//! \brief A list of possible scales
+	enum ScaleType {
+		SCALE_LINEAR, SCALE_ERB, SCALE_LOG, SCALE_LOGSCALED
+	};
+	/*! \brief Create a new scale based on type
+	 *  \param iType Scale type to create
+	 *  \param min Bottom frequency
+	 *  \param max Top frequency
+	 *  \param density Density of distribution on the scale (scale-dependent)
+	 *  \return Newly created scale, to be freed by the caller.
+	 *
+	 *  This is on purpose no virtual function, always use it Scale::Create().
+	 *  \todo Split into scaling and filterbank creation parts.
+	 *        Maybe even a separate ScaleMaker that implements Create().
+	 */
+	static Scale *Create(ScaleType iType, unsigned int min, unsigned int max, float density);
+
+	/*! \overload
+	 *  This function is for scaling-only Scales, you must not create a SignalBank
+     *  with the returned Scale.
+	 */
+	static Scale *Create(ScaleType iType);
+
+	/*! \brief Create a new Scale
+	 *  \param min Bottom frequency
+	 *  \param max Top frequency
+	 *  \param density Density of distribution on the scale (scale-dependent)
+	 */
+	Scale(unsigned int min, unsigned int max, float density) {
+		m_iMin = min;
+		m_iMax = max;
+		m_fDensity = density;
+		m_sName = NULL;
+	};
+
+	virtual ~Scale() { };
+
+	/*! \brief Create an exact copy of this Scale
+	 *  \return A newly created Scale, to be freed by the caller.
+	 */
+	virtual Scale *Clone();
+
+	//! \return this Signal's ScaleType
+	ScaleType getType() { return m_iType; };
+	//! \return this Signal's name
+	const char *getName() { return m_sName; };
+
+	/*! \brief Scale a frequency from linear to this scale
+	 *  \param fFreq Frequency to scale
+	 *  \return Scaled frequency
+	 */
+	virtual float FromLinear(float fFreq) = 0;
+
+	/*! \brief Scale a frequency from this scale to linear
+	 *  \param fFreq Scaled frequency to scale back
+	 *  \return Linear frequency
+	 */
+	virtual float ToLinear(float fFreq) = 0;
+
+	/*! \brief Scale from [fMin..fMax] to [-0.5..0.5]
+	 *  \param fVal Value to scale, must be within [fMin..fMax]
+	 *  \return Float in [-0.5..0.5] according to this scale
+	 *  \sa FromLinearScaledExtrema
+	 *
+	 *  This is mainly for displaying any value on a scale and to make sure
+	 *  that it fits in on screen. Don't use this for any real maths!
+	 *
+	 *  The default implementation assumes that the scale is monotonic, so the
+	 *  FromLinear(fMin) and FromLinear(fMax) are scale's output boundaries.
+	 *
+	 *  fMin and fMax are set by FromLinearScaledExtrema. Do not use this before
+	 *  calling that.
+	 */
+	float FromLinearScaled(float fVal);
+	/*! \brief Update the FromLinearScaled min/max values
+	 *  \param fMin Minimum value to be input
+	 *  \param fMax Maxmimum value to be input
+	 *  \sa FromLinearScaled
+	 */
+	void FromLinearScaledExtrema(float fMin, float fMax);
+	/*! \overload
+	 *  \brief Copy min/max values from another Scale
+	 *  \param pScale Scale from which to copy min/max values, must not be NULL
+	 */
+	void FromLinearScaledExtrema(Scale *pScale);
+
+	/*! \brief Create a new signal bank
+	 *  \param iChannels Number of audio channels
+	 *  \param iBufferlength Length of the buffer in frames
+	 *  \param iSamplerate Samplerate in Hz
+	 *  Note that the caller must free the signal bank again.
+	 */
+	virtual SignalBank* CreateSignalBank(unsigned int iChannels, unsigned int iBufferlength, unsigned int iSamplerate);
+
+	/*! \overload
+	 *  \brief Create a signal bank based on a Signal's parameters
+	 *  \param pSig Signal to get parameters from
+	 *  pSig is only used to look at parameters like samplerate, nothing is done
+	 *  with its contents.
+	 *  Note that the caller must free the signal bank again.
+	 */
+	virtual SignalBank* CreateSignalBank(/*! \todo const*/ Signal* pSig);
+
+
+protected:
+	//! \brief Bottom frequency
+	unsigned int m_iMin;
+	//! \brief Top frequency
+	unsigned int m_iMax;
+	//! \brief Density of distribution on the scale (scale-dependent)
+	float m_fDensity;
+	//! \brief The type of this scale, used by Clone(); set this in children.
+	ScaleType m_iType;
+	//! \brief The name of this scale; set this in children.
+	const char *m_sName;
+
+	/*! \brief Minimum value for scaling as input
+	 *  \sa FromLinearScaled(), FromLinearScaledExtrema() */
+	float m_fMin;
+	/*! \brief Maximum value for scaling as input
+	 *  \sa FromLinearScaled(), FromLinearScaledExtrema() */
+	float m_fMax;
+	//! \brief Value used in FromLinearScaled
+	float m_fScaledCurHalfSum;
+	//! \brief Value used in FromLinearScaled
+	float m_fScaledCurDiff;
+};
+
+#endif /* __MODULE_SCALE_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Scale/ScaleERB.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,43 @@
+/*!
+ * \file
+ * \brief ERB frequency scale for generating filter banks and their frequencies
+ *
+ * \author Tom Walters <tcw24@cam.ac.uk>
+ * \date created 2006/09/26
+ * \version \$Id: ScaleERB.h 459 2007-11-08 11:50:04Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#ifndef __MODULE_SCALE_ERB_H__
+#define __MODULE_SCALE_ERB_H__
+
+#include <math.h>
+
+#include "Modules/Scale/Scale.h"
+
+/*!
+ * \class ScaleERB "Modules/Scale/ScaleERB.h"
+ * \brief ERB frequency scale for generating filter banks and their frequencies
+ *
+ * It is very advisable to use Scale::Create() to an instance of this scale.
+ *
+ * References:
+ *   - J. Smith and J. Abel (1999), "Bark and ERB bilinear transforms"
+ *     http://www-ccrma.stanford.edu/~jos/bbt/
+ */
+class ScaleERB : public Scale {
+public:
+	ScaleERB(unsigned int min, unsigned int max, float density)
+		: Scale(min, max, density) { m_iType = SCALE_ERB; m_sName = "erb"; };
+
+	float FromLinear(float fFreq) {
+		return 21.4f*log10(0.00437f*fFreq + 1.0f);
+	};
+
+	float ToLinear(float fFreq) {
+		return (pow(10, fFreq/21.4f) - 1.0f)/0.00437f;
+	};
+};
+
+#endif /* __MODULE_SCALE_ERB_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Scale/ScaleLinear.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,39 @@
+/*!
+ * \file
+ * \brief Linear frequency scale for generating filter banks and their frequencies
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/26
+ * \version \$Id: ScaleLinear.h 459 2007-11-08 11:50:04Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#ifndef __MODULE_SCALE_LINEAR_H__
+#define __MODULE_SCALE_LINEAR_H__
+
+#include "Modules/Scale/Scale.h"
+
+/*!
+ * \class ScaleLinear "Modules/Scale/ScaleLinear.h"
+ * \brief Linear frequency scale for generating filter banks and their frequencies
+ *
+ * It is very advisable to use Scale::Create() to an instance of this scale.
+ *
+ * In terms of scaling, this is of course an identity transformation.
+ */
+class ScaleLinear : public Scale {
+public:
+	ScaleLinear(unsigned int min, unsigned int max, float density)
+		: Scale(min, max, density) { m_iType = SCALE_LINEAR; m_sName = "linear"; };
+
+	float FromLinear(float fFreq) {
+		return fFreq;
+	};
+
+	float ToLinear(float fFreq) {
+		return fFreq;
+	};
+};
+
+#endif /* __MODULE_SCALE_LINEAR_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Scale/ScaleLog.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,42 @@
+/*!
+ * \file
+ * \brief Logarithmic frequency scale for generating filter banks and their frequencies
+ *
+ * \author Willem van Engen <cnbh@willem.engen.nl>
+ * \date created 2006/09/28
+ * \version \$Id: ScaleLog.h 459 2007-11-08 11:50:04Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#ifndef __MODULE_SCALE_LOG_H__
+#define __MODULE_SCALE_LOG_H__
+
+#include <math.h>
+
+#include "Modules/Scale/Scale.h"
+
+/*!
+ * \class ScaleLog "Modules/Scale/ScaleLog.h"
+ * \brief Logarithmic frequency scale for generating filter banks and their frequencies
+ *
+ * It is very advisable to use Scale::Create() to an instance of this scale.
+ */
+class ScaleLog : public Scale {
+public:
+	ScaleLog(unsigned int min, unsigned int max, float density)
+		: Scale(min, max, density) { m_iType = SCALE_LOG; m_sName="log"; };
+
+	/*! The log scale has a problem, because log(0)=inf, so all values below
+	 *  1e-5 are truncated to 1e-5. */
+	float FromLinear(float fFreq) {
+		if (fFreq<1e-5f) fFreq=1e-5f;
+		return log(fFreq);
+	};
+
+	float ToLinear(float fFreq) {
+		return exp(fFreq);
+	};
+};
+
+#endif /* __MODULE_SCALE_LOG_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/Modules/Output/Graphics/Scale/ScaleLogScaled.h	Fri Oct 15 05:40:53 2010 +0000
@@ -0,0 +1,43 @@
+/*!
+ * \file
+ * \brief Log frequency scale for generating filter banks and their frequencies. Based on the scaling of the ERB scale
+ *
+ * \author Tom Walters <tcw24@cam.ac.uk>
+ * \date created 2006/09/26
+ * \version \$Id: ScaleLogScaled.h 459 2007-11-08 11:50:04Z tom $
+ */
+/* (c) 2006, University of Cambridge, Medical Research Council
+ * http://www.pdn.cam.ac.uk/groups/cnbh/aimmanual
+ */
+#ifndef __MODULE_SCALE_LOGSCALED_H__
+#define __MODULE_SCALE_LOGSCALED_H__
+
+#include <math.h>
+
+#include "Modules/Scale/Scale.h"
+
+/*!
+ * \class ScaleERB "Modules/Scale/ScaleERB.h"
+ * \brief ERB frequency scale for generating filter banks and their frequencies
+ *
+ * It is very advisable to use Scale::Create() to an instance of this scale.
+ *
+ * References:
+ *   - J. Smith and J. Abel (1999), "Bark and ERB bilinear transforms"
+ *     http://www-ccrma.stanford.edu/~jos/bbt/
+ */
+class ScaleLogScaled : public Scale {
+public:
+	ScaleLogScaled(unsigned int min, unsigned int max, float density)
+		: Scale(min, max, density) { m_iType = SCALE_ERB; m_sName = "logscaled"; };
+
+	float FromLinear(float fFreq) {
+		return 21.4f*log10(0.00437f*fFreq);
+	};
+
+	float ToLinear(float fFreq) {
+		return (pow(10, fFreq/21.4f))/0.00437f;
+	};
+};
+
+#endif /* __MODULE_SCALE_ERB_H__ */
--- a/trunk/src/Modules/SSI/ModuleSSI.cc	Wed Sep 29 00:24:03 2010 +0000
+++ b/trunk/src/Modules/SSI/ModuleSSI.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -69,6 +69,13 @@
   // The centre frequency of the channel which will just fill the complete
   // width of the SSI buffer
   pivot_cf_ = parameters_->DefaultFloat("ssi.pivot_cf", 1000.0f);
+  
+  // Whether or not to do smooth offset when the pitch cutoff is active.
+  do_smooth_offset_ = parameters_->DefaultBool("ssi.do_smooth_offset", false);
+  
+  // The number of cycles, centered on the pitch line, over which the SSI is taken
+  // to zero when doing the pitch cutoff.
+  smooth_offset_cycles_ = parameters_->DefaultFloat("ssi.smooth_offset_cycles", 3.0f);
 }
 
 ModuleSSI::~ModuleSSI() {
@@ -146,16 +153,45 @@
   if (do_pitch_cutoff_) {
     pitch_index = ExtractPitchIndex(input);
   }
+  
+  float gamma_min = -1.0f;
+  float gamma_max = log2(ssi_width_cycles_);
 
   for (int ch = 0; ch < channel_count_; ++ch) {
     float centre_frequency = input.centre_frequency(ch);
+    
+    float channel_weight = 1.0f;
+    int cutoff_index = buffer_length_ - 1;
+    if (do_pitch_cutoff_) {
+      if (pitch_index < cutoff_index) {
+        if (weight_by_cutoff_) {
+          channel_weight = static_cast<float>(buffer_length_)
+                    / static_cast<float>(pitch_index);
+        }
+        cutoff_index = pitch_index;
+      }
+    }
+    
+    // tanh(3) is about 0.995. Seems reasonable. 
+    float smooth_pitch_constant = smooth_offset_cycles_ * 3.0f;
+    float pitch_h = 0.0f;
+    if (do_smooth_offset_) {
+      if (log_cycles_axis_) {
+        float gamma = gamma_min + (gamma_max - gamma_min)
+                                   * static_cast<float>(pitch_index)
+                                   / static_cast<float>(ssi_width_samples_);
+        pitch_h = pow(2.0f, gamma);
+      } else {
+        pitch_h = static_cast<float>(pitch_index) * ssi_width_cycles_
+            / static_cast<float>(ssi_width_samples_);
+      }
+    }
+    
     // Copy the buffer from input to output, addressing by h-value
     for (int i = 0; i < ssi_width_samples_; ++i) {
       float h;
       float cycle_samples = sample_rate_ / centre_frequency;
       if (log_cycles_axis_) {
-        float gamma_min = -1.0f;
-        float gamma_max = log2(ssi_width_cycles_);
         float gamma = gamma_min + (gamma_max - gamma_min)
                                    * static_cast<float>(i)
                                    / static_cast<float>(ssi_width_samples_);
@@ -173,17 +209,11 @@
       float frac_part = modf(h * cycle_samples, &whole_part);
       int sample = floor(whole_part);
 
-      float weight = 1.0f;
-
-      int cutoff_index = buffer_length_ - 1;
-      if (do_pitch_cutoff_) {
-        if (pitch_index < cutoff_index) {
-          if (weight_by_cutoff_) {
-            weight *= static_cast<float>(buffer_length_)
-                      / static_cast<float>(pitch_index);
-          }
-          cutoff_index = pitch_index;
-        }
+      float weight = channel_weight;
+      
+      if (do_smooth_offset_ && do_pitch_cutoff_) {
+        // Smoothing around the pitch cutoff line.
+        weight *= (1.0f + tanh(smooth_pitch_constant * (pitch_h - h))) / 2.0f;
       }
 
       if (weight_by_scaling_) {
@@ -193,7 +223,7 @@
       }
 
       float val;
-      if (sample < cutoff_index) {
+      if (sample < cutoff_index || do_smooth_offset_) {
         float curr_sample = input.sample(ch, sample);
         float next_sample = input.sample(ch, sample + 1);
         val = weight * (curr_sample
--- a/trunk/src/Modules/SSI/ModuleSSI.h	Wed Sep 29 00:24:03 2010 +0000
+++ b/trunk/src/Modules/SSI/ModuleSSI.h	Fri Oct 15 05:40:53 2010 +0000
@@ -62,6 +62,8 @@
   bool weight_by_scaling_;
   bool log_cycles_axis_;
   float pitch_search_start_ms_;
+  bool do_smooth_offset_;
+  float smooth_offset_cycles_;
 };
 }  // namespace aimc
 
--- a/trunk/src/Support/Module.cc	Wed Sep 29 00:24:03 2010 +0000
+++ b/trunk/src/Support/Module.cc	Fri Oct 15 05:40:53 2010 +0000
@@ -87,7 +87,6 @@
   if (!initialized_)
     return;
 
-  // LOG_INFO("Resetting module %s", module_identifier_.c_str());
   ResetInternal();
 
   // Iterate through all the targets of this module, resetting
--- a/trunk/src/Support/SignalBank.h	Wed Sep 29 00:24:03 2010 +0000
+++ b/trunk/src/Support/SignalBank.h	Fri Oct 15 05:40:53 2010 +0000
@@ -54,14 +54,27 @@
   bool Initialize(const SignalBank &input);
   bool Validate() const;
 
+  // Return a const reference to an individual signal. Allows for 
+  // signal[channel][sample] referencing of SignalBanks.
   inline const vector<float> &operator[](int channel) const {
     return signals_[channel];
   };
 
+  // Return a const reference to an individual signal.
   inline const vector<float> &get_signal(int channel) const {
     return signals_[channel];
   };
   
+  // Return a reference to the signal vector. The reference is not
+  // const, so the vector is directly modifiable. In order to maintain
+  // consistency of the data within the filterbank, the size of this
+  // vector should not be changed. Changes to the vector size can be picked
+  // up with a call to Validate(), which will return false if the sizes of
+  // channle vectors are not consistent.
+  inline vector<float> &get_mutable_signal(int channel) {
+    return signals_[channel];
+  };
+  
   inline void set_signal(int channel, vector<float> input) {
     signals_[channel] = input;
   }