Mercurial > hg > beatroot
changeset 4:ee8c69ea3247
* Import BeatRoot v0.5.8
author | Chris Cannam |
---|---|
date | Fri, 30 Sep 2011 15:21:26 +0100 |
parents | 18ffb64cde0d |
children | bcb4c9697967 |
files | META-INF/MANIFEST.MF at/ofai/music/beatroot/Agent.java at/ofai/music/beatroot/AudioFile.java at/ofai/music/beatroot/AudioProcessor.java at/ofai/music/beatroot/BeatRoot.java at/ofai/music/beatroot/BeatTrackDisplay.java at/ofai/music/beatroot/Chooser.java at/ofai/music/beatroot/ControlPanel.java at/ofai/music/beatroot/EventProcessor.java at/ofai/music/beatroot/GUI.java at/ofai/music/beatroot/OnsetParameterListener.java gpl.txt |
diffstat | 12 files changed, 914 insertions(+), 233 deletions(-) [+] |
line wrap: on
line diff
--- a/META-INF/MANIFEST.MF Fri Oct 08 16:14:43 2010 +0100 +++ b/META-INF/MANIFEST.MF Fri Sep 30 15:21:26 2011 +0100 @@ -1,4 +1,3 @@ Manifest-Version: 1.0 -Created-By: 1.5.0 (Sun Microsystems Inc.) Main-Class: at.ofai.music.beatroot.BeatRoot
--- a/at/ofai/music/beatroot/Agent.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/Agent.java Fri Sep 30 15:21:26 2011 +0100 @@ -20,9 +20,10 @@ package at.ofai.music.beatroot; import java.util.ListIterator; + +import at.ofai.music.audio.Util; import at.ofai.music.util.Event; import at.ofai.music.util.EventList; -import at.ofai.music.audio.Util; /** Agent is the central class for beat tracking. * Each Agent object has a tempo hypothesis, a history of tracked beats, and
--- a/at/ofai/music/beatroot/AudioFile.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/AudioFile.java Fri Sep 30 15:21:26 2011 +0100 @@ -80,9 +80,9 @@ channels = format.getChannels(); length = audioIn.getFrameLength() * frameSize; } catch (java.io.IOException e) { // includes FileNotFound - e.printStackTrace(); + System.err.println("File not found."); //e.printStackTrace(); } catch (UnsupportedAudioFileException e) { - e.printStackTrace(); + System.err.println("File not supported."); //e.printStackTrace(); } } // constructor
--- a/at/ofai/music/beatroot/AudioProcessor.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/AudioProcessor.java Fri Sep 30 15:21:26 2011 +0100 @@ -164,6 +164,9 @@ /** The estimated onset times and their saliences. */ protected EventList onsetList; + /** The y-coordinates of the onsets for plotting. Only used if doOnsetPlot is true */ + protected double[] y2Onsets; + /** GUI component which shows progress of audio processing. */ protected ProgressIndicator progressCallback; @@ -606,18 +609,61 @@ // Peaks.getSlope(energy, hop, 15, slope); // LinkedList<Integer> peaks = Peaks.findPeaks(slope, (int)Math.round(0.06 / hop), 10); - double hop = hopTime; +// double hop = hopTime; +// Peaks.normalise(spectralFlux); +// LinkedList<Integer> peaks = Peaks.findPeaks(spectralFlux, (int)Math.round(0.06 / hop), 0.35, 0.84, true); +// onsets = new double[peaks.size()]; +// double[] y2 = new double[onsets.length]; +// Iterator<Integer> it = peaks.iterator(); +// onsetList = new EventList(); +// double minSalience = Peaks.min(spectralFlux); +// for (int i = 0; i < onsets.length; i++) { +// int index = it.next(); +// onsets[i] = index * hop; +// y2[i] = spectralFlux[index]; +// Event e = BeatTrackDisplay.newBeat(onsets[i], 0); +//// if (debug) +//// System.err.printf("Onset: %8.3f %8.3f %8.3f\n", +//// onsets[i], energy[index], slope[index]); +//// e.salience = slope[index]; // or combination of energy + slope?? +// // Note that salience must be non-negative or the beat tracking system fails! +// e.salience = spectralFlux[index] - minSalience; +// onsetList.add(e); +// } + double p1 = 0.35; + double p2 = 0.84; + Peaks.normalise(spectralFlux); - LinkedList<Integer> peaks = Peaks.findPeaks(spectralFlux, (int)Math.round(0.06 / hop), 0.35, 0.84, true); + findOnsets(p1, p2); + + if (progressCallback != null) + progressCallback.setFraction(1.0); + if (doOnsetPlot) { + double[] x1 = new double[spectralFlux.length]; + for (int i = 0; i < x1.length; i++) + x1[i] = i * hopTime; + plot.addPlot(x1, spectralFlux, Color.red, 4); + plot.addPlot(onsets, y2Onsets, Color.green, 3); + plot.setTitle("Spectral flux and onsets"); + plot.fitAxes(); + } + if (debug) { + System.err.printf("Onsets: %d\nContinue? ", onsets.length); + readLine(); + } + } // processFile() + + public void findOnsets(double p1, double p2){ + LinkedList<Integer> peaks = Peaks.findPeaks(spectralFlux, (int)Math.round(0.06 / hopTime), p1, p2, true); onsets = new double[peaks.size()]; - double[] y2 = new double[onsets.length]; + y2Onsets = new double[onsets.length]; Iterator<Integer> it = peaks.iterator(); onsetList = new EventList(); double minSalience = Peaks.min(spectralFlux); for (int i = 0; i < onsets.length; i++) { int index = it.next(); - onsets[i] = index * hop; - y2[i] = spectralFlux[index]; + onsets[i] = index * hopTime; + y2Onsets[i] = spectralFlux[index]; Event e = BeatTrackDisplay.newBeat(onsets[i], 0); // if (debug) // System.err.printf("Onset: %8.3f %8.3f %8.3f\n", @@ -627,23 +673,7 @@ e.salience = spectralFlux[index] - minSalience; onsetList.add(e); } - if (progressCallback != null) - progressCallback.setFraction(1.0); - if (doOnsetPlot) { - double[] x1 = new double[spectralFlux.length]; - for (int i = 0; i < x1.length; i++) - x1[i] = i * hopTime; - plot.addPlot(x1, spectralFlux, Color.red, 4); - plot.addPlot(onsets, y2, Color.green, 3); - plot.setTitle("Spectral flux and onsets"); - plot.fitAxes(); - } - if (debug) { - System.err.printf("Onsets: %d\nContinue? ", onsets.length); - readLine(); - } - } // processFile() - + } /** Reads a text file containing a list of whitespace-separated feature values. * Created for paper submitted to ICASSP'07. * @param fileName File containing the data
--- a/at/ofai/music/beatroot/BeatRoot.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/BeatRoot.java Fri Sep 30 15:21:26 2011 +0100 @@ -398,7 +398,7 @@ "this operation may result in loss of data :)\n" + "Do you really want to quit?", "Just checking ...", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) - System.exit(0); + System.exit(0); } // quit() /** Print a warning message.
--- a/at/ofai/music/beatroot/BeatTrackDisplay.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/BeatTrackDisplay.java Fri Sep 30 15:21:26 2011 +0100 @@ -1,20 +1,20 @@ /* BeatRoot: An interactive beat tracking system - Copyright (C) 2001, 2006 by Simon Dixon + Copyright (C) 2001, 2006 by Simon Dixon - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with this program (the file gpl.txt); if not, download it from + You should have received a copy of the GNU General Public License along + with this program (the file gpl.txt); if not, download it from http://www.gnu.org/licenses/gpl.txt or write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package at.ofai.music.beatroot; @@ -29,7 +29,6 @@ import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; - import java.util.Arrays; import java.util.ListIterator; @@ -40,9 +39,9 @@ import at.ofai.music.util.EventList; /** Main panel of BeatRoot's GUI, which displays the audio and beat data - * and allows editing, scrolling, selecting, etc - * @author Simon Dixon - */ +* and allows editing, scrolling, selecting, etc +* @author Simon Dixon +*/ public class BeatTrackDisplay extends JPanel implements MouseListener, MouseMotionListener { @@ -54,6 +53,7 @@ protected GUI gui; /** width in pixels of this panel */ + protected final int defaultXSize = 1000; protected int xSize; /** height in pixels of this panel */ @@ -136,93 +136,93 @@ /** colour map used in the spectrogram */ public static final Color[] colour = { - new Color( 11, 0, 0), - new Color( 21, 0, 0), - new Color( 32, 0, 0), - new Color( 43, 0, 0), - new Color( 53, 0, 0), - new Color( 64, 0, 0), - new Color( 74, 0, 0), - new Color( 85, 0, 0), - new Color( 96, 0, 0), - new Color(106, 0, 0), - new Color(117, 0, 0), - new Color(128, 0, 0), - new Color(138, 0, 0), - new Color(149, 0, 0), - new Color(159, 0, 0), - new Color(170, 0, 0), - new Color(181, 0, 0), - new Color(191, 0, 0), - new Color(202, 0, 0), - new Color(212, 0, 0), - new Color(223, 0, 0), - new Color(234, 0, 0), - new Color(244, 0, 0), - new Color(255, 0, 0), - new Color(255, 11, 0), - new Color(255, 21, 0), - new Color(255, 32, 0), - new Color(255, 43, 0), - new Color(255, 53, 0), - new Color(255, 64, 0), - new Color(255, 74, 0), - new Color(255, 85, 0), - new Color(255, 96, 0), - new Color(255,106, 0), - new Color(255,117, 0), - new Color(255,128, 0), - new Color(255,138, 0), - new Color(255,149, 0), - new Color(255,159, 0), - new Color(255,170, 0), - new Color(255,181, 0), - new Color(255,191, 0), - new Color(255,202, 0), - new Color(255,212, 0), - new Color(255,223, 0), - new Color(255,234, 0), - new Color(255,244, 0), - new Color(255,255, 0), - new Color(255,255, 16), - new Color(255,255, 32), - new Color(255,255, 48), - new Color(255,255, 64), - new Color(255,255, 80), - new Color(255,255, 96), - new Color(255,255,112), - new Color(255,255,128), - new Color(255,255,143), - new Color(255,255,159), - new Color(255,255,175), - new Color(255,255,191), - new Color(255,255,207), - new Color(255,255,223), - new Color(255,255,239), - new Color(255,255,255)}; + new Color( 11, 0, 0), + new Color( 21, 0, 0), + new Color( 32, 0, 0), + new Color( 43, 0, 0), + new Color( 53, 0, 0), + new Color( 64, 0, 0), + new Color( 74, 0, 0), + new Color( 85, 0, 0), + new Color( 96, 0, 0), + new Color(106, 0, 0), + new Color(117, 0, 0), + new Color(128, 0, 0), + new Color(138, 0, 0), + new Color(149, 0, 0), + new Color(159, 0, 0), + new Color(170, 0, 0), + new Color(181, 0, 0), + new Color(191, 0, 0), + new Color(202, 0, 0), + new Color(212, 0, 0), + new Color(223, 0, 0), + new Color(234, 0, 0), + new Color(244, 0, 0), + new Color(255, 0, 0), + new Color(255, 11, 0), + new Color(255, 21, 0), + new Color(255, 32, 0), + new Color(255, 43, 0), + new Color(255, 53, 0), + new Color(255, 64, 0), + new Color(255, 74, 0), + new Color(255, 85, 0), + new Color(255, 96, 0), + new Color(255,106, 0), + new Color(255,117, 0), + new Color(255,128, 0), + new Color(255,138, 0), + new Color(255,149, 0), + new Color(255,159, 0), + new Color(255,170, 0), + new Color(255,181, 0), + new Color(255,191, 0), + new Color(255,202, 0), + new Color(255,212, 0), + new Color(255,223, 0), + new Color(255,234, 0), + new Color(255,244, 0), + new Color(255,255, 0), + new Color(255,255, 16), + new Color(255,255, 32), + new Color(255,255, 48), + new Color(255,255, 64), + new Color(255,255, 80), + new Color(255,255, 96), + new Color(255,255,112), + new Color(255,255,128), + new Color(255,255,143), + new Color(255,255,159), + new Color(255,255,175), + new Color(255,255,191), + new Color(255,255,207), + new Color(255,255,223), + new Color(255,255,239), + new Color(255,255,255)}; /** the colour of the amplitude envelope */ - static Color AUDIO_COLOUR = new Color(100,20,120); - - /** the colour of the MIDI piano roll */ - static Color MIDI_COLOUR = Color.BLUE; - - /** the colour of the onset markers on the amplitude envelope */ - static Color ONSET_COLOUR = Color.GREEN; - - /** the colour of the lines marking the beats */ - static Color BEAT_COLOUR = Color.BLACK; - - /** the colour of the text (for IBIs and axes) */ - static Color TEXT_COLOUR = Color.BLACK; - - /** the background colour of a selected region */ - static Color SELECTION_COLOUR = new Color(240,200,200); - + static Color AUDIO_COLOUR = new Color(100,20,120); + + /** the colour of the MIDI piano roll */ + static Color MIDI_COLOUR = Color.BLUE; + + /** the colour of the onset markers on the amplitude envelope */ + static Color ONSET_COLOUR = Color.GREEN; + + /** the colour of the lines marking the beats */ + static Color BEAT_COLOUR = Color.BLACK; + + /** the colour of the text (for IBIs and axes) */ + static Color TEXT_COLOUR = Color.BLACK; + + /** the background colour of a selected region */ + static Color SELECTION_COLOUR = new Color(240,200,200); + /** the threshold below which the minimum colour is used in the spectrogram */ - double loThreshold; + double loThreshold; - /** the threshold above which the maximum colour is used in the spectrogram */ + /** the threshold above which the maximum colour is used in the spectrogram */ double hiThreshold; /** the ratio of hop size to frame size (for aligning the spectrogram with the audio) */ @@ -310,7 +310,7 @@ midiMin = 21; midiMax = 108; onsetList = null; - onsets = new double[0]; + onsets = new double[0]; offsets = new double[0]; pitches = new int[0]; beats = b; @@ -321,6 +321,7 @@ // setBorder(BorderFactory.createLineBorder(Color.black)); addMouseListener(this); addMouseMotionListener(this); + xSize = -1; init(true); } // BeatTrackDisplay constructor @@ -328,16 +329,16 @@ * @param c The energy value * @return The corresponding colour for this energy value */ - Color getColour(double c) { - int max = colour.length - 1; - int index = (int)(max - (c - loThreshold) * + Color getColour(double c) { + int max = colour.length - 1; + int index = (int)(max - (c - loThreshold) * max / (hiThreshold - loThreshold)); - return colour[index < 0? 0: (index > max? max: index)]; - } // getColour() + return colour[index < 0? 0: (index > max? max: index)]; + } // getColour() - /** Initialises the panel after new data is loaded or preferences are changed. - * @param resetSelection Indicates whether the selected region should be cleared - */ + /** Initialises the panel after new data is loaded or preferences are changed. + * @param resetSelection Indicates whether the selected region should be cleared + */ synchronized public void init(boolean resetSelection) { selectedBeat = null; scrollDirection = 0; @@ -376,7 +377,8 @@ yMid = yTop + 352; yBottom = yMid + 150; ySize = yBottom + 20; - xSize = 1000; + if (xSize == -1) + xSize = defaultXSize; setPreferredSize(new Dimension(xSize, ySize)); } x0 = xSize; @@ -511,6 +513,13 @@ /** Renders the panel. * @param g The Graphics object for painting on this panel. */ synchronized public void paint(Graphics g) { + //xSize = gui.getWidth(); + //System.out.println("xSize repainted: now="+xSize); + /*if (xSize != getWidth()) { + xSize = getWidth(); + System.out.println("xSize changed: now="+xSize); + init(false); + }*/ if (scrollDirection != 0) { gui.scroll(scrollDirection); } @@ -527,11 +536,11 @@ /** Clears the background image to white */ protected void clearImage() { - if (gImage != null) { - gImage.setColor(Color.white); - gImage.fillRect(0, 0, xSize, ySize); - } - } // clearImage() + if (gImage != null) { + gImage.setColor(Color.white); + gImage.fillRect(0, 0, xSize, ySize); + } + } // clearImage() /** Updates the background image, after creating it if necessary */ synchronized protected void repaintImage() { @@ -631,9 +640,22 @@ g.drawLine(position, yBottom-5, position, yBottom+5); } } + g.drawString("["+beats.size()+" beats]", 5, this.yTop+10); // WG: added to show number of beats + if (gui.audioPlayer.currentFile!=null) { // WG: added to show length of audio file + String label = Double.toString(gui.audioPlayer.currentFile.length / + gui.audioPlayer.currentFile.frameRate / gui.audioPlayer.currentFile.frameSize); + int position = label.indexOf('.'); + if (position < 0) { + position = label.length(); + label += "."; + } + label += "000"; + label = label.substring(0, position + (tickGap < 0.5 ? 3 : 2)); + g.drawString(label, xSize-metrics.stringWidth(label)-2, this.ySize-5); + } } // paintAxes() - /** Paints the beats and inter-baet intervals + /** Paints the beats and inter-beat intervals * @param g The Graphics object to paint to */ synchronized protected void paintBeats(Graphics g) { @@ -970,6 +992,26 @@ selectedBeat = null; } } // selectBeat() + + /** Finds the nearest detected onset time location to a given x-coordinate. + * @param x The x-coordinate (e.g. of a mouse-click) near shich a beat is sought + * @return The onset time (s) of the nearest detected onset. + * inserted by WG, 7 Aug 2009 + */ + synchronized public double selectOnset(int x) { + if (onsets.length == 0) + return -1.0; + int i = 0; + while (i < onsets.length-1 && timeToLocation(onsets[i]) <= x){ + i++; + } + if (i == 0) + return onsets[i]; + if (Math.abs(timeToLocation(onsets[i])-x) <= Math.abs(x-timeToLocation(onsets[i-1]))) + return onsets[i]; + else + return onsets[i-1]; + } // selectOnset() /** Creates a new Event object representing a beat. * @param time The time of the beat in seconds @@ -991,6 +1033,10 @@ System.err.printf("Add at: %5.3f\n", time); } // addBeatNow() + //public void addBeatCurrentTime() { + // beatPtr.add(newBeat()) + //} + /** Adds a beat at the given time. * @param time Time in seconds of the new beat */ @@ -1204,18 +1250,18 @@ roundedRatio = 1.5; else if ((0.9 < ratio) && (ratio < 1.3)) roundedRatio = 1.0; - else if ((1.35 < ratio) && (ratio < 1.6)) - roundedRatio = 2.0/3.0; - else if ((1.8 < ratio) && (ratio < 2.5)) - roundedRatio = 0.5; - else if ((2.7 < ratio) && (ratio < 3.5)) - roundedRatio = 1.0/3.0; - else if ((3.6 < ratio) && (ratio < 4.6)) - roundedRatio = 0.25; - else if ((7.6 < ratio) && (ratio < 8.6)) - roundedRatio = 0.125; - return roundedRatio; - } // getRhythmicLevel() + else if ((1.35 < ratio) && (ratio < 1.6)) + roundedRatio = 2.0/3.0; + else if ((1.8 < ratio) && (ratio < 2.5)) + roundedRatio = 0.5; + else if ((2.7 < ratio) && (ratio < 3.5)) + roundedRatio = 1.0/3.0; + else if ((3.6 < ratio) && (ratio < 4.6)) + roundedRatio = 0.25; + else if ((7.6 < ratio) && (ratio < 8.6)) + roundedRatio = 0.125; + return roundedRatio; + } // getRhythmicLevel() /** Evaluates a beat tracking solution against an annotation of the data. * @param beatsFile The file name of the annotation data @@ -1281,7 +1327,7 @@ c++; } } - fn += correct.length - c; + fn += correct.length - c; while (b < beatsArr.length) { if (correct[correct.length - 1] < beatsArr[b]) extra++; @@ -1326,38 +1372,59 @@ synchronized(this) { int x = e.getX(); int y = e.getY(); - if ((y > 0) && (y < yTop)) { - if (!gui.audioPlayer.playing) { - currentTime = locationToTime(x); - gui.skipTo(currentTime); - scrollTo(currentTime, false); + if (e.isControlDown()) { // WG added CTRL + Left Mouse shifts beats along detected onsets + if ((y >= yTop) && (y < yBottom) && // all clicks in the spectrogram + SwingUtilities.isLeftMouseButton(e)) { // Left Button + selectBeat(x, 10); + if (selectedBeat != null) + //selectedBeatTime = selectedBeat.keyDown; + selectedBeatTime = selectOnset(x); + } else if ((y >= yTop) && (y < yBottom)) { // all clicks in the spectrogram + if (SwingUtilities.isRightMouseButton(e)) { // WG changed from Middle to Right + addBeat(locationToTime(x)); + beatPtr.previous(); + selectedBeat = beatPtr.next(); + selectedBeatTime = -1; + } else { + selectBeat(x, 10); + if (selectedBeat != null) + selectedBeatTime = selectedBeat.keyDown; + } } - } else if ((y >= yTop) && (y < yBottom)) { - if (SwingUtilities.isMiddleMouseButton(e)) { - addBeat(locationToTime(x)); - beatPtr.previous(); - selectedBeat = beatPtr.next(); - selectedBeatTime = -1; - } else { - selectBeat(x, 5); - if (selectedBeat != null) - selectedBeatTime = selectedBeat.keyDown; + } else { + if ((y > 0) && (y < yTop)) { + if (!gui.audioPlayer.playing) { + currentTime = locationToTime(x); + gui.skipTo(currentTime); + scrollTo(currentTime, false); + } + } else if ((y >= yTop) && (y < yBottom)) { // all clicks in the spectrogram + if (SwingUtilities.isRightMouseButton(e)) { // WG changed from Middle to Right + addBeat(locationToTime(x)); + beatPtr.previous(); + selectedBeat = beatPtr.next(); + selectedBeatTime = -1; + } else { + selectBeat(x, 10); + if (selectedBeat != null) + selectedBeatTime = selectedBeat.keyDown; + } + } else if ((y >= yBottom) && (y < ySize)) { + if (SwingUtilities.isLeftMouseButton(e)) { + regionSelected = true; + if (e.isShiftDown() && (startSelection >= 0)) + endSelection = locationToTime(x); + else { + startSelection = locationToTime(x); + endSelection = -1.0; + } + } else { + regionSelected = false; + startSelection = endSelection = -1.0; + repaintImage(); + } } - } else if ((y >= yBottom) && (y < ySize)) { - if (SwingUtilities.isLeftMouseButton(e)) { - regionSelected = true; - if (e.isShiftDown() && (startSelection >= 0)) - endSelection = locationToTime(x); - else { - startSelection = locationToTime(x); - endSelection = -1.0; - } - } else { - regionSelected = false; - startSelection = endSelection = -1.0; - repaintImage(); - } - } + } repaint(); } } // mousePressed() @@ -1370,9 +1437,14 @@ int x = e.getX(); int y = e.getY(); if ((selectedBeat != null) && (x >= 0) && (x <= xSize) && - (y >= 0) && (y <= ySize)) { - if (!SwingUtilities.isRightMouseButton(e)) { - selectedBeat.keyDown = locationToTime(x); + (y >= 0) && (y <= ySize)) { + if (!SwingUtilities.isMiddleMouseButton(e)) { + if (e.isControlDown()) { + selectedBeat.keyDown = selectOnset(x); + + } else { + selectedBeat.keyDown = locationToTime(x); + } reorderBeats(); } } else if (regionSelected) { @@ -1383,7 +1455,7 @@ scrollDirection = 1; else scrollDirection = 0; - if (Math.abs(timeToLocation(startSelection) - x) == 0) + if (Math.abs(timeToLocation(startSelection) - x) <= 15) // WG changed from '==0' to '<=15'; to avoid region selection, when all to end selection was desired! Aug 2009 endSelection = -1; else endSelection = locationToTime(x); @@ -1400,9 +1472,9 @@ if (selectedBeat != null) { if (SwingUtilities.isLeftMouseButton(e)) // move EditAction.add(selectedBeatTime, selectedBeat.keyDown); - else if (SwingUtilities.isMiddleMouseButton(e)) // add + else if (SwingUtilities.isRightMouseButton(e)) // add // WG changed from Middle to Right EditAction.add(-1, selectedBeat.keyDown); - else { // right button, delete + else if (SwingUtilities.isMiddleMouseButton(e)) { // right button, delete beatPtr.remove(); EditAction.add(selectedBeatTime, -1); } @@ -1501,5 +1573,24 @@ tInc = inc; overlap = lap; } // setSpectro() + + /** + * WG: To resize the panel in the x dimension. + */ + public void resizeX() { + setPreferredSize(new Dimension(xSize,ySize)); + } -} // class BeatTrackDisplay + public double getCurrentTime() { + return currentTime; + } + + public void setCurrentTime(double currentTime) { + this.currentTime = currentTime; + } + + public void setOnsetDetectionParam(double param1,double param2) { + gui.setOnsetDetectionParameter(param1,param2); + } + +} // class BeatTrackDisplay \ No newline at end of file
--- a/at/ofai/music/beatroot/Chooser.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/Chooser.java Fri Sep 30 15:21:26 2011 +0100 @@ -19,17 +19,22 @@ package at.ofai.music.beatroot; +import java.io.File; + import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.filechooser.FileSystemView; /** An extension of the Swing file chooser for specific file types. */ public class Chooser extends JFileChooser { static final long serialVersionUID = 0; // avoid compiler warning + + File file = null; /** Constructor for BeatRoot's file chooser */ public Chooser() { - super("."); + super(FileSystemView.getFileSystemView().getHomeDirectory()); // WG changed from super(".") addChoosableFileFilter(getAcceptAllFileFilter()); } // constructor @@ -41,9 +46,19 @@ public String getInputName(FileFilters ff) { String pathName = null; addChoosableFileFilter(ff); + if (file != null) { + pathName = file.getAbsolutePath(); + int index = pathName.lastIndexOf("."); + if (index > 0) + pathName = pathName.substring(0, index); + file = new File(pathName + ff.suffix); + setSelectedFile(file); + } if (showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { - if (getSelectedFile().exists()) + if (getSelectedFile().exists()) { + file = getSelectedFile(); pathName = getSelectedFile().getAbsolutePath(); + } } removeChoosableFileFilter(ff); return pathName; @@ -76,13 +91,27 @@ public String getOutputName(FileFilters ff) { String pathName = null; addChoosableFileFilter(ff); + if (file != null) { + pathName = file.getAbsolutePath(); + int index = pathName.lastIndexOf("."); + if (index > 0) + pathName = pathName.substring(0, index); + file = new File(pathName + ff.suffix); + setSelectedFile(file); + } if (showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { if (!getSelectedFile().exists() || ( JOptionPane.showConfirmDialog(null, "File " + getSelectedFile().getAbsolutePath() + " exists.\nDo you want to replace it?", "Are you sure?", - JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)) + JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)) { pathName = getSelectedFile().getAbsolutePath(); + int index = pathName.lastIndexOf("."); + if (index > 0) + pathName = pathName.substring(0, index); + file = new File(pathName + ((FileFilters)getFileFilter()).suffix); + pathName = file.getAbsolutePath(); + } } removeChoosableFileFilter(ff); return pathName; @@ -97,10 +126,18 @@ /** Opens a dialog to get a file name for saving beat information. * @return The file name for saving beat information */ public String getBeatOutName() { - //addChoosableFileFilter(FileFilters.tmfFileFilter); addChoosableFileFilter(FileFilters.textFileFilter); addChoosableFileFilter(FileFilters.csvFileFilter); String oName = getOutputName(FileFilters.tmfFileFilter); + /*if (getFileFilter()==FileFilters.textFileFilter && + !oName.endsWith(".txt")) + oName = oName + ".txt"; + if (getFileFilter()==FileFilters.tmfFileFilter && + !oName.endsWith(".tmf")) + oName = oName + ".tmf"; + if (getFileFilter()==FileFilters.csvFileFilter && + !oName.endsWith(".csv")) + oName = oName + ".csv";*/ removeChoosableFileFilter(FileFilters.textFileFilter); removeChoosableFileFilter(FileFilters.csvFileFilter); return oName;
--- a/at/ofai/music/beatroot/ControlPanel.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/ControlPanel.java Fri Sep 30 15:21:26 2011 +0100 @@ -15,13 +15,14 @@ with this program (the file gpl.txt); if not, download it from http://www.gnu.org/licenses/gpl.txt or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + */ package at.ofai.music.beatroot; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionListener; import javax.swing.BorderFactory; @@ -31,29 +32,40 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollBar; +import javax.swing.JSlider; import javax.swing.JTextField; /** The panel at the bottom of BeatRoot's GUI containing the buttons */ class ControlPanel extends JPanel { - - static final long serialVersionUID = 0; // avoid compiler warning - + + static final long serialVersionUID = 0; // avoid compiler warning + /** The listener object which handles key, button and menu events */ EventProcessor jobq; /** For layout of the grid of buttons (i.e. not including the zoom buttons) */ class ButtonPanel extends JPanel { - static final long serialVersionUID = 0; // avoid compiler warning - /** Constructor: - * @param r Number of rows of buttons - * @param c NUmber of columns of buttons + static final long serialVersionUID = 0; // avoid compiler warning + + /** + * Constructor: + * + * @param r + * Number of rows of buttons + * @param c + * NUmber of columns of buttons */ public ButtonPanel(int r, int c) { - super(new GridLayout(r,c)); + super(new GridLayout(r, c)); } // constructor - /** Adds a button to this panel. - * @param s The text on the button - * @param al The event handler for when the button is pressed + + /** + * Adds a button to this panel. + * + * @param s + * The text on the button + * @param al + * The event handler for when the button is pressed */ public void addButton(String s, ActionListener al) { JButton b = new JButton(s); @@ -62,37 +74,69 @@ } // addButton() } // inner class ButtonPanel - /** Constructor: - * @param displayPanel The main panel of BeatRoot's GUI - * @param scroller The scrollbar on BeatRoot's GUI - * @param args Not used - was used for a file name in BeatRoot 0.4 for experiments - * reported in Dixon, Goebl and Cambouropoulos, Music Perception, 2006 - * @param j The object which handles button, key and menu events + /** + * Constructor: + * + * @param displayPanel + * The main panel of BeatRoot's GUI + * @param scroller + * The scrollbar on BeatRoot's GUI + * @param args + * Not used - was used for a file name in BeatRoot 0.4 for + * experiments reported in Dixon, Goebl and Cambouropoulos, Music + * Perception, 2006 + * @param j + * The object which handles button, key and menu events */ public ControlPanel(BeatTrackDisplay displayPanel, JScrollBar scroller, - String args, EventProcessor j) { + String args, EventProcessor j) { setPreferredSize(new Dimension(1000, 100)); setLayout(new BorderLayout()); setBorder(BorderFactory.createLineBorder(Color.black, 5)); - JLabel fileName = new JLabel(); // "File: <none>"); + //JLabel fileName = new JLabel(); // "File: <none>"); JTextField argumentField = new JTextField(24); jobq = j; ButtonPanel buttonPanel; - if ((args != null) && args.endsWith(".tmf")) { // never happens - buttonPanel = new ButtonPanel(3,2); + if ((args != null) && args.endsWith(".tmf")) { // never happens + buttonPanel = new ButtonPanel(3, 2); buttonPanel.addButton("New", jobq); buttonPanel.addButton(GUI.PLAY, jobq); buttonPanel.addButton(GUI.CLEAR_BEATS, jobq); buttonPanel.addButton(GUI.STOP, jobq); buttonPanel.addButton(GUI.SAVE_BEATS, jobq); - } else { // always happens + } else { // always happens Box optionsBox = new Box(BoxLayout.X_AXIS); optionsBox.add(new JLabel("Options: ")); optionsBox.add(argumentField); - JPanel viewPanel = new JPanel(new GridLayout(1,3)); + int upperInitValue = 34; + int lowerInitValue = 84; + JTextField upperParamText = new JTextField(Integer.toString(upperInitValue),4); + upperParamText.setEditable(false); + JTextField lowerParamText = new JTextField(Integer.toString(lowerInitValue),4); + lowerParamText.setEditable(false); + OnsetParameterListener onsetParameterListener = new OnsetParameterListener( + displayPanel, upperInitValue, lowerInitValue, + upperParamText, lowerParamText); + + JSlider upperParamSlider = new JSlider(0, 100, upperInitValue); + upperParamSlider.addChangeListener(onsetParameterListener); + upperParamSlider.setName("upperSlider"); + + JSlider lowerParamSlider = new JSlider(0, 100, lowerInitValue); + lowerParamSlider.addChangeListener(onsetParameterListener); + lowerParamSlider.setName("lowerSlider"); + + + JPanel upperParamBox = new JPanel(); + upperParamBox.add(upperParamSlider); + upperParamBox.add(upperParamText); + JPanel lowerParamBox = new JPanel(new FlowLayout()); + lowerParamBox.add(lowerParamSlider); + lowerParamBox.add(lowerParamText); + JPanel viewPanel = new JPanel(new GridLayout(1, 3)); JLabel viewLabel = new JLabel("View range (s): "); JButton dec = new JButton("-"); JTextField zoomField = new JTextField(5); @@ -102,19 +146,22 @@ viewPanel.add(dec); viewPanel.add(zoomField); viewPanel.add(inc); - ZoomListener viewListener = new ZoomListener(displayPanel, scroller, - zoomField); + ZoomListener viewListener = new ZoomListener(displayPanel, + scroller, zoomField); inc.addActionListener(viewListener); zoomField.addActionListener(viewListener); dec.addActionListener(viewListener); - JPanel fileAndOptions = new JPanel(new GridLayout(3,1)); - fileAndOptions.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); - fileAndOptions.add(fileName); + JPanel fileAndOptions = new JPanel(new GridLayout(4, 1)); + fileAndOptions.setBorder(BorderFactory + .createEmptyBorder(0, 5, 0, 0)); + //fileAndOptions.add(fileName); + fileAndOptions.add(upperParamBox); + fileAndOptions.add(lowerParamBox); fileAndOptions.add(optionsBox); fileAndOptions.add(viewPanel); add(fileAndOptions, BorderLayout.WEST); - buttonPanel = new ButtonPanel(3,4); + buttonPanel = new ButtonPanel(3, 4); buttonPanel.addButton(GUI.LOAD_AUDIO, jobq); buttonPanel.addButton(GUI.BEAT_TRACK, jobq); buttonPanel.addButton(GUI.SAVE_BEATS, jobq);
--- a/at/ofai/music/beatroot/EventProcessor.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/EventProcessor.java Fri Sep 30 15:21:26 2011 +0100 @@ -125,12 +125,12 @@ audioPlayer.stop(false); gui.displayPanel.toggleAnnotateMode(); break; - case KeyEvent.VK_LEFT: + /*case KeyEvent.VK_LEFT: gui.displayPanel.selectPreviousBeat(); break; case KeyEvent.VK_RIGHT: gui.displayPanel.selectNextBeat(); - break; + break;*/ case KeyEvent.VK_HOME: gui.displayPanel.selectFirstBeat(); break; @@ -172,13 +172,52 @@ System.err.print(System.nanoTime() / 1000000 % 100000); gui.displayPanel.addBeatNow(); break; + case KeyEvent.VK_V: + gui.displayPanel.addBeat(gui.displayPanel.getCurrentTime()); + break; case KeyEvent.VK_P: audioPlayer.play(); break; case KeyEvent.VK_S: audioPlayer.stop(); break; + case KeyEvent.VK_SPACE: // WG. inserted (4 Aug 2009) + if (audioPlayer.playing) + audioPlayer.stop(); + else + audioPlayer.play(); + break; + case KeyEvent.VK_B: // WG. inserted (4 Aug 2009) + gui.displayPanel.beatTrack(); + break; + case KeyEvent.VK_Z: // WG. inserted (5 Aug 2009) + gui.displayPanel.clearBeats(); + break; + case KeyEvent.VK_RIGHT: + gui.scroll(.025); + break; + case KeyEvent.VK_LEFT: + gui.scroll(-.025); + break; } // switch + } else if (modifiers == KeyEvent.CTRL_MASK) { + switch (keyCode) { + case KeyEvent.VK_RIGHT: + gui.scroll(.1); + break; + case KeyEvent.VK_LEFT: + gui.scroll(-.1); + break; + } // switch + } else if (modifiers == KeyEvent.ALT_MASK) { + switch (keyCode) { + case KeyEvent.VK_RIGHT: + gui.scroll(.005); + break; + case KeyEvent.VK_LEFT: + gui.scroll(-.005); + break; + } // switch } } // keyPressed()
--- a/at/ofai/music/beatroot/GUI.java Fri Oct 08 16:14:43 2010 +0100 +++ b/at/ofai/music/beatroot/GUI.java Fri Sep 30 15:21:26 2011 +0100 @@ -74,6 +74,7 @@ /** The scroller for showing or changing the position of the viewport relative to the whole audio file */ protected JScrollBar scroller; + protected int scrollBarWidth = 1000; // pixel /** An intermediate level panel containing the displayPanel and scroller */ protected JPanel scrollPane; @@ -95,7 +96,7 @@ /** Version number of program - displayed as part of window title. * DO NOT EDIT: This line is also used in creating the file name of the jar file. */ - public static final String version = "0.5.7"; + public static final String version = "0.5.6"; /** Strings displayed on menus and buttons */ public static final String LOAD_AUDIO = "Load Audio Data"; @@ -192,6 +193,7 @@ displayPanel = new BeatTrackDisplay(this, beats); displayPanel.addKeyListener(listener); displayPanel.setBeats(beats); + //displayPanel.addPropertyChangeListener(arg0) EditAction.setDisplay(displayPanel); scroller = new JScrollBar(); @@ -202,9 +204,11 @@ scroller.setUnitIncrement(100); scroller.setBlockIncrement(400); scroller.setOrientation(Adjustable.HORIZONTAL); - scroller.setPreferredSize(new Dimension(1000, 17)); + scroller.setPreferredSize(new Dimension(scrollBarWidth, 17)); + scrollPane = new JPanel(); - scrollPane.setPreferredSize(new Dimension(1010, displayPanel.ySize+17+10)); + scrollPane.setLayout(new BorderLayout()); + scrollPane.setPreferredSize(new Dimension(scrollBarWidth+10, displayPanel.ySize+17+10)); scrollPane.setBackground(Color.black); scrollPane.add(displayPanel, BorderLayout.CENTER); scrollPane.add(scroller, BorderLayout.SOUTH); @@ -304,8 +308,8 @@ protected JMenu makeBeatTrackMenu() { JMenu menu = new JMenu("BeatTrack"); menu.setMnemonic(KeyEvent.VK_T); - menu.add(makeMenuItem(BEAT_TRACK, KeyEvent.VK_T, 0, false)); - menu.add(makeMenuItem(CLEAR_BEATS, KeyEvent.VK_C, 0, false)); + menu.add(makeMenuItem(BEAT_TRACK, KeyEvent.VK_B, KeyEvent.VK_B, false)); + menu.add(makeMenuItem(CLEAR_BEATS, KeyEvent.VK_Z, KeyEvent.VK_Z, false)); menu.add(makeMenuItem(MARK_METRICAL_LEVEL, KeyEvent.VK_M, KeyEvent.VK_M, false)); menu.add(makeMenuItem(CLEAR_METRICAL_LEVELS, KeyEvent.VK_L, 0, false)); return menu; @@ -353,6 +357,7 @@ } // loadBeatData() /** Saves beat data to a file chosen by a file save dialog. */ + // TODO Check for the correct extension of exported file names public void saveBeatData() { try { beats.writeBeatTrackFile(chooser.getBeatOutName()); @@ -460,6 +465,16 @@ public void scroll(int dir) { scroller.setValue(scroller.getValue() + dir * scroller.getUnitIncrement()); } // scroll() + + /** Scroll the display by a given amount in seconds. Used for keyboard input + * @param incr amount of shift in seconds (positive to the right, vice versa) + * inserted by WG, Aug 2009. + */ + public void scroll(double incr) { + displayPanel.setCurrentTime(displayPanel.getCurrentTime() + incr); + skipTo(displayPanel.getCurrentTime()); + displayPanel.scrollTo(displayPanel.getCurrentTime(), false); + } /** Copies default values into preferences dialog. */ public void setPreferences() { @@ -521,4 +536,12 @@ return percussionSounds.getString(PERCUSSION_STRINGS[level][0]); } // getPercussionSound() + public void setOnsetDetectionParameter(double param1,double param2) { + if (audioProcessor.audioFileName != null) { + audioProcessor.findOnsets(param1, param2); + audioProcessor.setDisplay(displayPanel); // after processing + updateDisplay(false); + } + } // setOnsetDetectionParameter() + } // class GUI
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/at/ofai/music/beatroot/OnsetParameterListener.java Fri Sep 30 15:21:26 2011 +0100 @@ -0,0 +1,75 @@ +/* BeatRoot: An interactive beat tracking system + Copyright (C) 2001, 2006 by Simon Dixon + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program (the file gpl.txt); if not, download it from + http://www.gnu.org/licenses/gpl.txt or write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package at.ofai.music.beatroot; + +import javax.swing.JSlider; +import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * A listener class for onset parameter slider, which notifies the GUI's main + * panel when the scrollbar is moved. + * + * @author Sebastian Flossmann + */ +class OnsetParameterListener implements ChangeListener { + BeatTrackDisplay display; + /* The current value of the upper slider */ + int p1; + + /* The current value of the lower slider */ + int p2; + + /* The Textfield showing the current value of the upper slider */ + JTextField text1; + + /* The Textfield showing the current value of the lower slider */ + JTextField text2; + + public OnsetParameterListener(BeatTrackDisplay btd, int upper, int lower, JTextField upperText, JTextField lowerText){ + display = btd; + p1 = upper; + p2 = lower; + text1 = upperText; + text2 = lowerText; + } + + public void stateChanged(ChangeEvent e) { + JSlider slider = (JSlider) e.getSource(); + if (slider.getName() == "upperSlider") { + p1 = slider.getValue(); +// if (p1 >= p2) { +// p1 = p2; +// slider.setValue(p2); +// } + text1.setText(Integer.toString(p1)); + } else { + p2 = slider.getValue(); +// if (p2 <= p1) { +// p2 = p1; +// slider.setValue(p1); +// } + text2.setText(Integer.toString(p2)); + } + display.setOnsetDetectionParam(p1/100.0, p2/100.0); + } // stateChanged() + +} // class OnsetParameterListener \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpl.txt Fri Sep 30 15:21:26 2011 +0100 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License.