Index: trunk/src/org/openstreetmap/josm/actions/audio/AudioFastSlowAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/audio/AudioFastSlowAction.java	(revision 563)
+++ trunk/src/org/openstreetmap/josm/actions/audio/AudioFastSlowAction.java	(revision 563)
@@ -0,0 +1,38 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions.audio;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.tools.AudioPlayer;
+
+abstract public class AudioFastSlowAction extends JosmAction {
+
+	private double multiplier;
+	
+	public AudioFastSlowAction(String name, String iconName, String tooltip, int shortcut, int modifier, boolean fast) {
+		super(name, iconName, tooltip, shortcut, modifier, true);
+		try {
+			multiplier = Double.parseDouble(Main.pref.get("audio.fastfwdmultiplier","1.3"));
+		} catch (NumberFormatException e) {
+			multiplier = 1.3;
+		}
+		if (! fast) 
+			multiplier = 1.0 / multiplier;
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		double speed = AudioPlayer.speed();
+		if (speed * multiplier <= 0.1) 
+			return;
+		try {
+			if (AudioPlayer.playing() || AudioPlayer.paused())
+				AudioPlayer.play(AudioPlayer.url(), AudioPlayer.position(), speed * multiplier);
+		} catch (Exception ex) {
+			AudioPlayer.audioMalfunction(ex);
+		}
+	}
+}
Index: trunk/src/org/openstreetmap/josm/actions/audio/AudioFasterAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/audio/AudioFasterAction.java	(revision 563)
+++ trunk/src/org/openstreetmap/josm/actions/audio/AudioFasterAction.java	(revision 563)
@@ -0,0 +1,13 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions.audio;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.KeyEvent;
+
+public class AudioFasterAction extends AudioFastSlowAction {
+	
+	public AudioFasterAction() {
+		super(tr("Faster"), "audio-faster", tr("Faster Forward"), KeyEvent.VK_F9, 0, true);
+	}
+}
Index: trunk/src/org/openstreetmap/josm/actions/audio/AudioPlayPauseAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/audio/AudioPlayPauseAction.java	(revision 562)
+++ trunk/src/org/openstreetmap/josm/actions/audio/AudioPlayPauseAction.java	(revision 563)
@@ -26,5 +26,8 @@
 				AudioPlayer.play(url);
 			} else if (AudioPlayer.playing()){
-				AudioPlayer.pause();
+				if (AudioPlayer.speed() != 1.0)
+					AudioPlayer.play(url, AudioPlayer.position());
+				else
+					AudioPlayer.pause();
 			} else {
 				// find first audio marker to play
Index: trunk/src/org/openstreetmap/josm/actions/audio/AudioSlowerAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/audio/AudioSlowerAction.java	(revision 563)
+++ trunk/src/org/openstreetmap/josm/actions/audio/AudioSlowerAction.java	(revision 563)
@@ -0,0 +1,13 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions.audio;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.KeyEvent;
+
+public class AudioSlowerAction extends AudioFastSlowAction {
+	
+	public AudioSlowerAction() {
+		super(tr("Slower"), "audio-slower", tr("Slower Forward"), KeyEvent.VK_F9, KeyEvent.SHIFT_MASK, false);
+	}
+}
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 562)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 563)
@@ -47,4 +47,6 @@
 import org.openstreetmap.josm.actions.audio.AudioPlayPauseAction;
 import org.openstreetmap.josm.actions.audio.AudioPrevAction;
+import org.openstreetmap.josm.actions.audio.AudioFasterAction;
+import org.openstreetmap.josm.actions.audio.AudioSlowerAction;
 import org.openstreetmap.josm.actions.search.SearchAction;
 import org.openstreetmap.josm.data.DataSetChecker;
@@ -100,4 +102,6 @@
 	public final JosmAction audioFwd = new AudioFwdAction();
 	public final JosmAction audioBack = new AudioBackAction();
+	public final JosmAction audioFaster = new AudioFasterAction();
+	public final JosmAction audioSlower = new AudioSlowerAction();
 
 	/* Help menu */
@@ -222,4 +226,8 @@
 			current = audioMenu.add(audioBack);
 			current.setAccelerator(audioBack.shortCut);
+			current = audioMenu.add(audioSlower);
+			current.setAccelerator(audioSlower.shortCut);
+			current = audioMenu.add(audioFaster);
+			current.setAccelerator(audioFaster.shortCut);
 			add(audioMenu);
 		}
Index: trunk/src/org/openstreetmap/josm/gui/preferences/AudioPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/AudioPreference.java	(revision 562)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/AudioPreference.java	(revision 563)
@@ -1,3 +1,2 @@
-// License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.gui.preferences;
 
@@ -40,4 +39,5 @@
 	private JTextField audioLeadIn = new JTextField(8);
 	private JTextField audioForwardBackAmount = new JTextField(8);
+	private JTextField audioFastForwardMultiplier = new JTextField(8);
 	private JTextField audioCalibration = new JTextField(8);
 
@@ -106,4 +106,9 @@
 		gui.audio.add(audioForwardBackAmount, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
 
+		audioFastForwardMultiplier.setText(Main.pref.get("audio.fastfwdmultiplier", "1.3"));
+		audioFastForwardMultiplier.setToolTipText(tr("The amount by which the speed is multiplied for fast forwarding"));
+		gui.audio.add(new JLabel(tr("Fast forward multiplier")), GBC.std());
+		gui.audio.add(audioFastForwardMultiplier, GBC.eol().fill(GBC.HORIZONTAL).insets(5,0,0,5));
+
 		audioLeadIn.setText(Main.pref.get("audio.leadin", "1"));
 		audioLeadIn.setToolTipText(tr("Playback starts this number of seconds before (or after, if negative) the audio track position requested"));
@@ -127,4 +132,5 @@
 		Main.pref.put("marker.audiosampleminmetres", audioSampleMinMetres.getText());		
 		Main.pref.put("audio.forwardbackamount", audioForwardBackAmount.getText());		
+		Main.pref.put("audio.fastfwdmultiplier", audioFastForwardMultiplier.getText());		
 		Main.pref.put("audio.leadin", audioLeadIn.getText());		
 		Main.pref.put("audio.calibration", audioCalibration.getText());		
Index: trunk/src/org/openstreetmap/josm/tools/AudioPlayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/AudioPlayer.java	(revision 562)
+++ trunk/src/org/openstreetmap/josm/tools/AudioPlayer.java	(revision 563)
@@ -38,4 +38,5 @@
 	private double bytesPerSecond; 
 	private static long chunk = 8000; /* bytes */
+	private double speed = 1.0;
 
 	/**
@@ -48,11 +49,13 @@
 		private URL url;
 		private double offset; // seconds
-
+		private double speed; // ratio
+		
 		/*
 		 * Called to execute the commands in the other thread 
 		 */
-		protected void play(URL url, double offset) throws Exception {
+		protected void play(URL url, double offset, double speed) throws Exception {
 			this.url = url;
 			this.offset = offset;
+			this.speed = speed; 
 			command = Command.PLAY;
 			result = Result.WAITING;
@@ -85,4 +88,7 @@
 			return offset;
 		}
+		protected double speed() {
+			return speed;
+		}
 		protected URL url() {
 			return url;
@@ -102,5 +108,5 @@
 	 */
 	public static void play(URL url) throws Exception {
-		AudioPlayer.play(url, 0.0);
+		AudioPlayer.get().command.play(url, 0.0, 1.0);
 	}
 	
@@ -112,5 +118,16 @@
 	 */
 	public static void play(URL url, double seconds) throws Exception {
-		AudioPlayer.get().command.play(url, seconds);
+		AudioPlayer.get().command.play(url, seconds, 1.0);
+	}
+	
+	/**
+	 * Plays a WAV audio file from a specified position at variable speed.
+	 * @param url The resource to play, which must be a WAV file or stream
+	 * @param seconds The number of seconds into the audio to start playing
+	 * @param speed Rate at which audio playes (1.0 = real time, > 1 is faster)
+	 * @throws audio fault exception, e.g. can't open stream,  unhandleable audio format
+	 */
+	public static void play(URL url, double seconds, double speed) throws Exception {
+		AudioPlayer.get().command.play(url, seconds, speed);
 	}
 	
@@ -155,4 +172,12 @@
 	}
 	
+	/**
+	 * Speed at which we will play.
+	 * @returns double, speed multiplier
+	 */
+	public static double speed() {
+		return AudioPlayer.get().speed;
+	}
+
 	/**
 	 *  gets the singleton object, and if this is the first time, creates it along with 
@@ -241,4 +266,5 @@
 					case PLAY:	
 						double offset = command.offset();
+						speed = command.speed();
 						if (playingUrl != command.url() || 
 							stateChange != State.PAUSED || 
@@ -252,5 +278,4 @@
 							audioInputStream = AudioSystem.getAudioInputStream(playingUrl);
 							audioFormat = audioInputStream.getFormat();
-							DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
 							long nBytesRead = 0;
 							position = 0.0;
@@ -258,4 +283,6 @@
 							bytesPerSecond = audioFormat.getFrameRate() /* frames per second */
 								* audioFormat.getFrameSize() /* bytes per frame */;
+							if (speed * bytesPerSecond > 256000.0)
+								speed = 256000 / bytesPerSecond;
 							if (offset != 0.0 && adjustedOffset > 0.0) {
 								long bytesToSkip = (long)(
@@ -275,9 +302,17 @@
 								position = adjustedOffset;
 							}
-							if (audioOutputLine == null) {
-								audioOutputLine = (SourceDataLine) AudioSystem.getLine(info);
-								audioOutputLine.open(audioFormat);
-								audioOutputLine.start();
-							}
+							if (audioOutputLine != null)
+								audioOutputLine.close();
+							audioFormat = new AudioFormat(audioFormat.getEncoding(), 
+										audioFormat.getSampleRate() * (float) speed, 
+										audioFormat.getSampleSizeInBits(), 
+										audioFormat.getChannels(), 
+										audioFormat.getFrameSize(), 
+										audioFormat.getFrameRate() * (float) speed, 
+										audioFormat.isBigEndian());
+							DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
+							audioOutputLine = (SourceDataLine) AudioSystem.getLine(info);
+							audioOutputLine.open(audioFormat);
+							audioOutputLine.start();
 						}
 						stateChange = State.PLAYING;
