Additions: log(f) axis in spectrum display; log Tongen

This read-only archive contains discussions from the Adding Feature forum.
New feature request may be posted to the Adding Feature forum.
Technical support is available via the Help forum.
Locked
amicheler
Posts: 3
Joined: Tue Nov 06, 2007 2:26 am
Operating System: Please select

Additions: log(f) axis in spectrum display; log Tongen

Post by amicheler » Tue Nov 06, 2007 2:37 am

Hi,

I have tinkered a bit with the audacity source code:
I wrote a spectrum display with log frequency axis,
and integrated the hidden feature of a log sweep in the tone generator.
I hope someone checks this in...!
Thanks,
Andreas

Here's the patch against the CVS files:

Code: Select all

Index: Printing.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/Printing.cpp,v
retrieving revision 1.2
diff -u -r1.2 Printing.cpp
--- Printing.cpp	15 Jun 2006 14:26:13 -0000	1.2
+++ Printing.cpp	4 Nov 2007 23:45:48 -0000
@@ -108,10 +108,13 @@
                                 &viewInfo, false, false, false, true, false);
             break;
          case WaveTrack::SpectrumDisplay:
-            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, false);
+            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, false, false);
+            break;
+         case WaveTrack::SpectrumLogDisplay:
+            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, false, true);
             break;
          case WaveTrack::PitchDisplay:
-            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, true);
+            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, true, false);
             break;
          }
          break;
Index: TrackArtist.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/TrackArtist.cpp,v
retrieving revision 1.104
diff -u -r1.104 TrackArtist.cpp
--- TrackArtist.cpp	28 Oct 2007 20:45:15 -0000	1.104
+++ TrackArtist.cpp	6 Nov 2007 02:11:58 -0000
@@ -236,10 +236,13 @@
                             drawEnvelope,  drawSamples, drawSliders, true, muted);
                break;
             case WaveTrack::SpectrumDisplay:
-               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, false);
+               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, false, false);
+               break;
+            case WaveTrack::SpectrumLogDisplay:
+               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, false, true);
                break;
             case WaveTrack::PitchDisplay:
-               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, true);
+               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, true, false);
                break;
             }
             break;              // case Wave
@@ -331,22 +334,22 @@
    }
 
    if (t->GetKind() == Track::Wave
-       && ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumDisplay) {
-      // Spectrum
+       && ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumDisplay)
+	{  // Spectrum
       if (r.height < 60)
          return;
 
+		bool logF = ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumLogDisplay;
       double rate = ((WaveTrack *) t)->GetRate();
       int freq = lrint(rate/2.);
-      int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), freq);
-      if(maxFreq > freq)
-         maxFreq = freq;
+		int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), freq);
+		if(maxFreq > freq)
+			maxFreq = freq;
       int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
-      if(minFreq < 0) {
-         minFreq = 0;
-         gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
-      }
-
+	   if(minFreq < 0) {
+	      minFreq = 0;
+		   gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
+		}
       /*
          draw the ruler
          we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
@@ -368,6 +371,35 @@
 
       vruler->Draw(*dc);
    }
+   if (t->GetKind() == Track::Wave
+   && ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumLogDisplay)
+	{  // SpectrumLog
+      if (r.height < 10)
+         return;
+      double rate = ((WaveTrack *) t)->GetRate();
+      int freq = lrint(rate/2.);
+		int maxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), freq);
+	   if(maxFreq > freq)
+			maxFreq = freq;
+      int minFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), freq/1000.0);
+	   if(minFreq < 1) {
+	      minFreq = 1;
+		   gPrefs->Write(wxT("/SpectrumLog/MinFreq"), 1L);
+		}
+      /*
+         draw the ruler
+         we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
+         and append to the numbers a "k"
+      */
+      vruler->SetBounds(r.x, r.y+1, r.x + r.width, r.y + r.height-1);
+      vruler->SetOrientation(wxVERTICAL);
+      vruler->SetFormat(Ruler::IntFormat);
+      vruler->SetLabelEdges(true);
+      vruler->SetRange(maxFreq, minFreq);
+      vruler->SetUnits(wxT(""));
+		vruler->SetLog(true);
+      vruler->Draw(*dc);
+   }
 
    if (t->GetKind() == Track::Wave
        && ((WaveTrack *) t)->GetDisplay() == WaveTrack::PitchDisplay) {
@@ -1315,7 +1347,7 @@
 
 void TrackArtist::DrawSpectrum(WaveTrack *track,
                                wxDC & dc, wxRect & r,
-                               ViewInfo * viewInfo, bool autocorrelation)
+                               ViewInfo * viewInfo, bool autocorrelation, bool logF)
 {
    // MM: Draw background. We should optimize that a bit more.
    dc.SetPen(*wxTRANSPARENT_PEN);
@@ -1336,12 +1368,12 @@
    }
 
    for (WaveClipList::Node* it=track->GetClipIterator(); it; it=it->GetNext())
-      DrawClipSpectrum(track, it->GetData(), dc, r, viewInfo, autocorrelation);
+      DrawClipSpectrum(track, it->GetData(), dc, r, viewInfo, autocorrelation, logF);
 }
 
 void TrackArtist::DrawClipSpectrum(WaveTrack* track, WaveClip *clip,
                                wxDC & dc, wxRect & r,
-                               ViewInfo * viewInfo, bool autocorrelation)
+                               ViewInfo * viewInfo, bool autocorrelation, bool logF)
 {
    double h = viewInfo->h;
    double pps = viewInfo->zoom;
@@ -1457,14 +1489,27 @@
    bool isGrayscale = false;
    gPrefs->Read(wxT("/Spectrum/Grayscale"), &isGrayscale, false);
    int ifreq = lrint(rate/2);
-   int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), ifreq);
+   int maxFreq;
+	if (!logF)
+		maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), ifreq);
+	else
+		maxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), ifreq);
    if(maxFreq > ifreq)
       maxFreq = ifreq;
-   int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
-   if(minFreq < 0) {
-      minFreq = 0;
-      gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
-   }
+	int minFreq;
+	if (!logF)
+	{	minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
+	   if(minFreq < 0) {
+	      minFreq = 0;
+	      gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
+	   }
+	}else
+	{	minFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), ifreq/1000.0);
+	   if(minFreq < 1) {
+	      minFreq = ifreq/1000.0;
+	      gPrefs->Write(wxT("/SpectrumLog/MinFreq"), ifreq/1000.0);
+	   }
+	}
    bool usePxCache = false;
 
    if( !updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width) ) {
@@ -1483,57 +1528,119 @@
 
    int x = 0;
    sampleCount w1 = (sampleCount) ((t0*rate + x *rate *tstep) + .5);
-   while (x < mid.width) {
+
+	while (x < mid.width) {
       sampleCount w0 = w1;
       w1 = (sampleCount) ((t0*rate + (x+1) *rate *tstep) + .5);
 
-      for (int yy = 0; yy < mid.height; yy++) {
-         bool selflag = (ssel0 <= w0 && w1 < ssel1);
-         unsigned char rv, gv, bv;
-         float value;
-
-         if(!usePxCache) {
-            float bin0 = float (yy) * binPerPx + minSamples;
-            float bin1 = float (yy + 1) * binPerPx + minSamples;
-
-
-            if (int (bin1) == int (bin0))
-               value = freq[half * x + int (bin0)];
-            else {
-               float binwidth= bin1 - bin0;
-               value = freq[half * x + int (bin0)] * (1.f - bin0 + (int)bin0);
-
-               bin0 = 1 + int (bin0);
-               while (bin0 < int (bin1)) {
-                  value += freq[half * x + int (bin0)];
-                  bin0 += 1.0;
-               }
-               value += freq[half * x + int (bin1)] * (bin1 - int (bin1));
-
-               value /= binwidth;
-            }
-
-            if (!autocorrelation) {
-               // Last step converts dB to a 0.0-1.0 range
-               value = (value + 80.0) / 80.0;
-            }
-
-            if (value > 1.0)
-               value = float(1.0);
-            if (value < 0.0)
-               value = float(0.0);
-            clip->mSpecPxCache->values[x * mid.height + yy] = value;
-         }
-         else
-            value = clip->mSpecPxCache->values[x * mid.height + yy];
-
-         GetColorGradient(value, selflag, isGrayscale, &rv, &gv, &bv);
-
-         int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
-         data[px++] = rv;
-         data[px++] = gv;
-         data[px] = bv;
-      }
+		if (!logF)
+		{	for (int yy = 0; yy < mid.height; yy++) {
+				bool selflag = (ssel0 <= w0 && w1 < ssel1);
+				unsigned char rv, gv, bv;
+				float value;
+
+				if(!usePxCache) {
+					float bin0 = float (yy) * binPerPx + minSamples;
+					float bin1 = float (yy + 1) * binPerPx + minSamples;
+
+
+					if (int (bin1) == int (bin0))
+						value = freq[half * x + int (bin0)];
+					else {
+						float binwidth= bin1 - bin0;
+						value = freq[half * x + int (bin0)] * (1.f - bin0 + (int)bin0);
+
+						bin0 = 1 + int (bin0);
+						while (bin0 < int (bin1)) {
+							value += freq[half * x + int (bin0)];
+							bin0 += 1.0;
+						}
+						value += freq[half * x + int (bin1)] * (bin1 - int (bin1));
+
+						value /= binwidth;
+					}
+
+					if (!autocorrelation) {
+						// Last step converts dB to a 0.0-1.0 range
+						value = (value + 80.0) / 80.0;
+					}
+
+					if (value > 1.0)
+						value = float(1.0);
+					if (value < 0.0)
+						value = float(0.0);
+					clip->mSpecPxCache->values[x * mid.height + yy] = value;
+				}
+				else
+					value = clip->mSpecPxCache->values[x * mid.height + yy];
+
+				GetColorGradient(value, selflag, isGrayscale, &rv, &gv, &bv);
+
+				int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
+				data[px++] = rv;
+				data[px++] = gv;
+				data[px] = bv;
+			}
+		}else
+		{	int x0=x*half;
+			double e=exp(1.0f), f=rate/2/half, 
+				lmin=log(double(minFreq)),
+				lmax=log(double(maxFreq)),
+				scale=lmax-lmin, 
+				expo=exp(scale);
+			for (int yy = 0; yy < mid.height; yy++) {
+				bool selflag = (ssel0 <= w0 && w1 < ssel1);
+				unsigned char rv, gv, bv;
+				float value;
+
+				if(true) //!usePxCache) 
+				{	float h=double(yy)/mid.height, 
+						yy2=exp(h*scale+lmin)/f;
+					if (int(yy2)>=half)
+						yy2=half-1;
+					if (yy2<0)
+						yy2=0;
+					float bin0 = float (yy2);// * binPerPx + minSamples;
+					float bin1 = float (yy2 + 1);// * binPerPx + minSamples;
+
+					if (int (bin1) == int (bin0))
+						value = freq[x0 + int (bin0)];
+					else {
+						float binwidth= bin1 - bin0;
+						value = freq[x0 + int (bin0)] * (1.f - bin0 + (int)bin0);
+
+						bin0 = 1 + int (bin0);
+						while (bin0 < int (bin1)) {
+							value += freq[x0 + int (bin0)];
+							bin0 += 1.0;
+						}
+						value += freq[x0 + int (bin1)] * (bin1 - int (bin1));
+
+						value /= binwidth;
+					}
+
+					if (!autocorrelation) {
+						// Last step converts dB to a 0.0-1.0 range
+						value = (value + 80.0) / 80.0;
+					}
+
+					if (value > 1.0)
+						value = float(1.0);
+					if (value < 0.0)
+						value = float(0.0);
+					clip->mSpecPxCache->values[x * mid.height + yy] = value;
+				}
+				else
+					value = clip->mSpecPxCache->values[x * mid.height + yy];
+
+				GetColorGradient(value, selflag, isGrayscale, &rv, &gv, &bv);
+
+				int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
+				data[px++] = rv;
+				data[px++] = gv;
+				data[px] = bv;
+			}
+		}
       x++;
    }
 
Index: TrackArtist.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/TrackArtist.h,v
retrieving revision 1.27
diff -u -r1.27 TrackArtist.h
--- TrackArtist.h	18 Jun 2007 16:10:54 -0000	1.27
+++ TrackArtist.h	5 Nov 2007 21:27:14 -0000
@@ -69,7 +69,7 @@
 
    void DrawSpectrum(WaveTrack *track,
                      wxDC & dc, wxRect & r,
-                     ViewInfo * viewInfo, bool autocorrelation);
+                     ViewInfo * viewInfo, bool autocorrelation, bool logF);
 
    void DrawNoteTrack(NoteTrack *track,
                       wxDC & dc, wxRect & r, ViewInfo * viewInfo);
@@ -94,7 +94,7 @@
 
    void DrawClipSpectrum(WaveTrack *track, WaveClip *clip,
                          wxDC & dc, wxRect & r,
-                         ViewInfo * viewInfo, bool autocorrelation);
+                         ViewInfo * viewInfo, bool autocorrelation, bool logF);
 
    void SetBackgroundBrushes(wxBrush unselectedBrush, wxBrush selectedBrush,
 			     wxPen unselectedPen, wxPen selectedPen) {
Index: TrackPanel.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/TrackPanel.cpp,v
retrieving revision 1.376
diff -u -r1.376 TrackPanel.cpp
--- TrackPanel.cpp	3 Nov 2007 15:15:14 -0000	1.376
+++ TrackPanel.cpp	6 Nov 2007 02:00:14 -0000
@@ -302,6 +302,7 @@
    OnWaveformID,
    OnWaveformDBID,
    OnSpectrumID,
+   OnSpectrumLogID,
    OnPitchID,
 
    OnSplitStereoID,
@@ -505,6 +506,7 @@
    mWaveTrackMenu->Append(OnWaveformID, _("Waveform"));
    mWaveTrackMenu->Append(OnWaveformDBID, _("Waveform (dB)"));
    mWaveTrackMenu->Append(OnSpectrumID, _("Spectrum"));
+   mWaveTrackMenu->Append(OnSpectrumLogID, _("Spectrum log(f)"));
    mWaveTrackMenu->Append(OnPitchID, _("Pitch (EAC)"));
    mWaveTrackMenu->AppendSeparator();
    mWaveTrackMenu->AppendCheckItem(OnChannelMonoID, _("Mono"));
@@ -1233,7 +1235,8 @@
 {
    if (event.m_x >= GetVRulerOffset() &&
       t->GetKind() == Track::Wave &&
-      ((WaveTrack *) t)->GetDisplay() <= WaveTrack::SpectrumDisplay) {
+      (((WaveTrack *) t)->GetDisplay() <= WaveTrack::SpectrumDisplay
+		||((WaveTrack *) t)->GetDisplay() <= WaveTrack::SpectrumLogDisplay)) {
       *ppTip = _("Click to vertically zoom in, Shift-click to zoom out, Drag to create a particular zoom region.");
       SetCursor(event.ShiftDown()? *mZoomOutCursor : *mZoomInCursor);
    }
@@ -2529,7 +2532,8 @@
 
    // don't do anything if track is not wave or Spectrum
    if (mCapturedTrack->GetKind() != Track::Wave
-      || ((WaveTrack *) mCapturedTrack)->GetDisplay() > WaveTrack::SpectrumDisplay)
+      && ((WaveTrack *) mCapturedTrack)->GetDisplay() != WaveTrack::SpectrumDisplay
+      && ((WaveTrack *) mCapturedTrack)->GetDisplay() != WaveTrack::SpectrumLogDisplay)
       return;
 
    mMouseCapture = IsVZooming;
@@ -2575,11 +2579,12 @@
    }
 
    float min, max, c, l, binSize = 0.0;
-   bool spectrum;
+   bool spectrum, spectrumLog;
    int windowSize, minBins = 0;
    double rate = ((WaveTrack *)track)->GetRate();
    ((WaveTrack *) track)->GetDisplay() == WaveTrack::SpectrumDisplay ? spectrum = true : spectrum = false;
-   if(spectrum) {
+	spectrumLog=(((WaveTrack *) track)->GetDisplay() == WaveTrack::SpectrumLogDisplay);
+	if(spectrum) {
       min = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
       if(min < 0)
          min = 0;
@@ -2590,22 +2595,30 @@
       binSize = rate / windowSize;
       minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less
    }
-   else
-      track->GetDisplayBounds(&min, &max);
+   else if(spectrumLog) {
+      min = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), rate/2000.0);
+      if(min < 1)
+         min = 1;
+      max = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), rate/2.);
+      if(max > rate/2.)
+         max = rate/2.;
+      windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
+      binSize = rate / windowSize;
+      minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less
+   }else
+		track->GetDisplayBounds(&min, &max);
 
    if (IsDragZooming()) {
       // Drag Zoom
       float p1, p2, tmin, tmax;
-      tmin=min;
-      tmax=max;
-
-      p1 = (mZoomStart - ypos) / (float)height;
-      p2 = (mZoomEnd - ypos) / (float)height;
-      max = (tmax * (1.0-p1) + tmin * p1);
-      min = (tmax * (1.0-p2) + tmin * p2);
-
       // Enforce maximum vertical zoom
       if(spectrum) {
+			tmin=min;
+			tmax=max;
+			p1 = (mZoomStart - ypos) / (float)height;
+			p2 = (mZoomEnd - ypos) / (float)height;
+			max = (tmax * (1.0-p1) + tmin * p1);
+			min = (tmax * (1.0-p2) + tmin * p2);
          if(min < 0.)
             min = 0.;
          if(max < min + minBins * binSize)
@@ -2615,7 +2628,21 @@
             min = max - minBins * binSize;
          }
       }
+      else if(spectrumLog) {
+         double xmin = 1-(mZoomEnd - ypos) / (float)height;
+         double xmax = 1-(mZoomStart - ypos) / (float)height;
+			double lmin=log10(min), lmax=log10(max);
+			double d=lmax-lmin;
+			min=wxMax(1.0, pow(10, xmin*d+lmin));
+			max=wxMin(rate/2.0, pow(10, xmax*d+lmin));
+      }
       else {
+			tmin=min;
+			tmax=max;
+			p1 = (mZoomStart - ypos) / (float)height;
+			p2 = (mZoomEnd - ypos) / (float)height;
+			max = (tmax * (1.0-p1) + tmin * p1);
+			min = (tmax * (1.0-p2) + tmin * p2);
          if (max - min < 0.2) {
             c = (min+max)/2;
             min = c-0.1;
@@ -2640,6 +2667,15 @@
             max = wxMin( rate/2., c + 2*l);
          }
       }
+      else if(spectrumLog)
+		{  c = 0.5;
+         double xmin = c-1;
+         double xmax = c+1;
+			double lmin=log10(min), lmax=log10(max);
+			double d=lmax-lmin;
+			min=wxMax(1.0, pow(10, xmin*d+lmin));
+			max=wxMin(rate/2.0, pow(10, xmax*d+lmin));
+      }
       else {
          if (min <= -1.0 && max >= 1.0) {
             min = -2.0;
@@ -2665,8 +2701,20 @@
          c = (max * (1.0-p1) + min * p1);
          min = wxMax( 0.0, c - 0.5*l);
          max = wxMin( rate/2., min + l);
-      }
-      else {
+      }else if(spectrumLog) 
+		{  c = 0.5*(min+max);
+         // Enforce maximum vertical zoom
+         l = wxMax( 10. * binSize, (c - min));   //is this a sensible min? MJS
+
+         p1 = (mZoomStart - ypos) / (float)height;
+         c = 1.0-p1;
+         double xmin = wxMax(0, c-0.25);
+         double xmax = wxMin(1, c+0.25);
+			double lmin=log10(min), lmax=log10(max);
+			double d=lmax-lmin;
+			min=pow(10, xmin*d+lmin);
+			max=pow(10, xmax*d+lmin);
+      }else {
 
          // Zoom in centered on cursor
          if (min < -1.0 || max > 1.0) {
@@ -2702,6 +2750,22 @@
          }
          t = iter.Next();
       }
+   }else if(spectrumLog) {
+      gPrefs->Write(wxT("/SpectrumLog/MaxFreq"), (long)max);
+      gPrefs->Write(wxT("/SpectrumLog/MinFreq"), (long)min);
+      TrackListIterator iter(mTracks);
+      Track *t = iter.First();
+      while (t) {
+         if (t->GetKind() == Track::Wave) {
+            WaveTrack *wt = (WaveTrack *)t;
+            WaveClipList::Node* it;
+            for(it=wt->GetClipIterator(); it; it=it->GetNext()) {
+               WaveClip *clip = it->GetData();
+               clip->mSpecPxCache->valid = false;
+            }
+         }
+         t = iter.Next();
+      }
    }
    else {
       track->SetDisplayBounds(min, max);
@@ -5516,6 +5580,7 @@
       theMenu->Enable(OnWaveformDBID,
                         display != WaveTrack::WaveformDBDisplay);
       theMenu->Enable(OnSpectrumID, display != WaveTrack::SpectrumDisplay);
+      theMenu->Enable(OnSpectrumLogID, display != WaveTrack::SpectrumLogDisplay);
       theMenu->Enable(OnPitchID, display != WaveTrack::PitchDisplay);
       
       WaveTrack * track = (WaveTrack *)t;
Index: WaveTrack.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/WaveTrack.h,v
retrieving revision 1.48
diff -u -r1.48 WaveTrack.h
--- WaveTrack.h	18 Jun 2007 16:10:54 -0000	1.48
+++ WaveTrack.h	4 Nov 2007 23:45:48 -0000
@@ -310,6 +310,7 @@
       WaveformDisplay,
       WaveformDBDisplay,
       SpectrumDisplay,
+		SpectrumLogDisplay,
       PitchDisplay
    } WaveTrackDisplay;
 
Index: effects/ToneGen.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/ToneGen.cpp,v
retrieving revision 1.46
diff -u -r1.46 ToneGen.cpp
--- effects/ToneGen.cpp	10 Oct 2007 00:16:22 -0000	1.46
+++ effects/ToneGen.cpp	4 Nov 2007 22:51:22 -0000
@@ -50,24 +50,30 @@
    amplitude[1] = float(0.8);
 //   EnableForChirps();
    length = sDefaultGenerateLen;
+	interpolation=0;
 }
 
 wxString EffectToneGen::GetEffectDescription() { 
    // Note: This is useful only after values have been set. 
    /// todo update to include *all* chirp parameters??
    const wxChar* waveformNames[] = {wxT("sine"), wxT("square"), wxT("sawtooth"), wxT("square, no alias")};
+   const wxChar* interpolationNames[] = {wxT("linear"), wxT("logarithmic")};
    return wxString::Format(_("Applied effect: Generate %s wave %s, frequency = %.2f Hz, amplitude = %.2f, %.6lf seconds"), 
-      waveformNames[waveform], mbChirp ? wxT("chirp") : wxT("tone"), frequency[0], amplitude[0], length); 
+      waveformNames[waveform], mbChirp ? wxT("chirp") : wxT("tone"), 
+		frequency[0], amplitude[0], length, interpolationNames[interpolation]); 
 } 
 
 bool EffectToneGen::PromptUser()
 {
    wxArrayString waveforms;
+   wxArrayString interpolations;
    ToneGenDialog dlog(mParent, mbChirp ? _("Chirp Generator") : _("Tone Generator"));
    waveforms.Add(_("Sine"));
    waveforms.Add(_("Square"));
    waveforms.Add(_("Sawtooth"));
    waveforms.Add(_("Square, no alias"));
+   interpolations.Add(_("Linear"));
+   interpolations.Add(_("Logarithmic"));
    dlog.isSelection= false;
 
    if (mT1 > mT0) {
@@ -83,6 +89,8 @@
    dlog.amplitude[1] = amplitude[1];
    dlog.length = length;
    dlog.waveforms = &waveforms;
+	dlog.interpolation = interpolation;
+	dlog.interpolations = &interpolations;
    dlog.Init();
    dlog.TransferDataToWindow();
    dlog.Fit();
@@ -96,6 +104,11 @@
    frequency[1] = dlog.frequency[1];
    amplitude[0] = dlog.amplitude[0];
    amplitude[1] = dlog.amplitude[1];
+	interpolation = dlog.interpolation;
+	if (interpolation==0)
+		mbLogInterpolation = false;
+	if (interpolation==1)
+		mbLogInterpolation = true;
    if( !mbChirp )
    {
       frequency[1] = frequency[0];
@@ -341,6 +354,7 @@
       S.TieTextBox(wxT(""), frequency[1], 10);
       S.TieTextBox(_("Amplitude (0-1)"),amplitude[0], 10);
       S.TieTextBox(wxT(""), amplitude[1], 10);
+      S.TieChoice(_("Interpolation:"), interpolation,  interpolations);
    }
    S.EndMultiColumn();
    S.StartMultiColumn(2, wxCENTER);
Index: effects/ToneGen.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/ToneGen.h,v
retrieving revision 1.23
diff -u -r1.23 ToneGen.h
--- effects/ToneGen.h	29 Jul 2007 02:15:36 -0000	1.23
+++ effects/ToneGen.h	4 Nov 2007 22:44:32 -0000
@@ -78,6 +78,7 @@
    double length;
    float logFrequency[2];
    double mCurRate;
+	int interpolation;
 
    // mSample is an external placeholder to remember the last "buffer"
    // position so we use it to reinitialize from where we left
@@ -114,6 +115,8 @@
    double amplitude[2];
    double length;
    bool isSelection;
+	int interpolation;
+   wxArrayString *interpolations;
 
  private:
    TimeTextCtrl *mToneDurationT;
Index: widgets/Ruler.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/widgets/Ruler.cpp,v
retrieving revision 1.44
diff -u -r1.44 Ruler.cpp
--- widgets/Ruler.cpp	5 Jul 2007 11:55:38 -0000	1.44
+++ widgets/Ruler.cpp	6 Nov 2007 00:08:02 -0000
@@ -144,7 +144,7 @@
 
 void Ruler::SetFormat(RulerFormat format)
 {
-   // IntFormat, RealFormat, TimeFormat, or LinearDBFormat
+   // IntFormat, RealFormat, RealLogFormat, TimeFormat, or LinearDBFormat
 
    if (mFormat != format) {
       mFormat = format;
@@ -500,6 +500,32 @@
       mMajor = d * 2.0;
       break;
 
+   case RealLogFormat:
+      d = 0.000001;
+      // mDigits is number of digits after the decimal point.
+      mDigits = 6;
+      for(;;) {
+         if (units < d) {
+            mMinor = d;
+            mMajor = d*5.0;
+            return;
+         }
+         d *= 5.0;
+         if (units < d) {
+            mMinor = d;
+            mMajor = d*2.0;
+            return;
+         }
+         d *= 2.0;
+         mDigits--;
+         // More than 10 digit numbers?  Something is badly wrong.
+         // Probably units is coming in with too high a value.
+         wxASSERT( mDigits >= -10 );
+      }
+		mDigits++;
+      mMinor = d;
+      mMajor = d * 2.0;
+      break;
    }
 
 }
@@ -535,6 +561,13 @@
          s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
       }
       break;
+   case RealLogFormat:
+      if (mMinor >= 1.0)
+         s.Printf(wxT("%d"), (int)floor(d+0.5));
+      else {
+         s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
+      }
+      break;
    case TimeFormat:
       if (major) {
          if (d < 0) {
@@ -606,7 +639,8 @@
    }
    
    if (mUnits != wxT(""))
-      s = (s + wxT(" ") + mUnits);
+      s = (s + mUnits);
+//      s = (s + wxT(" ") + mUnits);
 
    return s;
 }
@@ -882,8 +916,9 @@
    }
    else {
       // log case
-      
-      double loLog = log10(mMin);
+      mDigits=2;	//TODO: implement dynamic digit computation
+
+		double loLog = log10(mMin);
       double hiLog = log10(mMax);
       double scale = mLength/(hiLog - loLog);
       int loDecade = (int) floor(loLog);
@@ -895,28 +930,38 @@
       
       // Major ticks are the decades
       double decade = startDecade;
-      for(i=loDecade; i<hiDecade; i++) {
-         if(i!=loDecade) {
-            val = decade;
-            if(val > mMin && val < mMax) {
+		int delta=hiDecade-loDecade, steps=abs(delta);
+		double step = delta>=0 ? 10 : 0.1;
+		double rMin=wxMin(mMin, mMax), rMax=wxMax(mMin, mMax);
+		for(i=0; i<=steps; i++) 
+		{  // if(i!=0) 
+			{  val = decade;
+            if(val > rMin && val < rMax) {
                pos = (int)(((log10(val) - loLog)*scale)+0.5);
                Tick(pos, val, true);
             }
          }
-         decade *= 10.;
+         decade *= step;
       }
       
       // Minor ticks are multiples of decades
       decade = startDecade;
-      for(i=loDecade; i<hiDecade; i++) {
-         for(j=2; j<=9; j++) {
+		int start, end, mstep;
+		if (delta > 0)
+		{	start=2; end=10; mstep=1;
+		}else
+		{	start=9; end=1; mstep=-1;
+		}
+		steps++;
+      for(i=0; i<=steps; i++) {
+         for(j=start; j!=end; j+=mstep) {
             val = decade * j;
-            if(val >= mMin && val < mMax) {
+            if(val >= rMin && val < rMax) {
                pos = (int)(((log10(val) - loLog)*scale)+0.5);
                Tick(pos, val, false);
             }
          }
-         decade *= 10.;
+         decade *= step;
       }
    }
    
Index: widgets/Ruler.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/widgets/Ruler.h,v
retrieving revision 1.20
diff -u -r1.20 Ruler.h
--- widgets/Ruler.h	5 Jul 2007 11:55:38 -0000	1.20
+++ widgets/Ruler.h	5 Nov 2007 23:39:36 -0000
@@ -27,7 +27,8 @@
    enum RulerFormat {
       IntFormat,
       RealFormat,
-      TimeFormat,
+		RealLogFormat,
+		TimeFormat,
       LinearDBFormat,
    };

amicheler
Posts: 3
Joined: Tue Nov 06, 2007 2:26 am
Operating System: Please select

Re: Additions: log(f) axis in spectrum display; log Tongen

Post by amicheler » Tue Nov 06, 2007 8:05 am

Hi all,

some more which I've written now: a convolution effect.
Its computations seem valid, but there's a bug which I couldn't fix:
after the effect has run, the only way to proceed is to save the project and exit.
then start the project again. Else Audacity crashes.
If anyone with more experience concerning the internals of Audacity knows,
how to fix the bug then it will be a nice usable effect. :-)

In this patch my last patch (spectrum display & tongen) is included also.
(how can I prepare a patch which is based on a patched CVS? Can anyone explain me how to do this?)

Cheers,
Andreas

Code: Select all

? effects/Convolution.cpp
? effects/Convolution.h
Index: BatchCommands.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/BatchCommands.cpp,v
retrieving revision 1.29
diff -u -r1.29 BatchCommands.cpp
--- BatchCommands.cpp	7 May 2007 00:59:08 -0000	1.29
+++ BatchCommands.cpp	6 Nov 2007 03:18:46 -0000
@@ -56,6 +56,7 @@
    wxT("SaveHqMaster1"),
    wxT("SaveHqMaster2"),
    wxT("StereoToMono"),
+   wxT("Convolution"),
    wxT("ExportFlac"),
    wxT("ExportMp3"),
    wxT("ExportOgg"),
@@ -231,6 +232,7 @@
 
 /* i18n-hint: Effect name translations must agree with those used elsewhere, or batch won't find them */
    AddToChain( wxT("StereoToMono") );
+   AddToChain( wxT("Convolution") );
    AddToChain( wxT("Normalize") );
    AddToChain( wxT("SaveHqMaster1") );
    AddToChain( wxT("NoiseRemoval") );
@@ -476,6 +478,14 @@
       }
       wxMessageBox(_("Stereo To Mono Effect not found"));
       return false;
+   } else if (command == wxT("Convolution")) {
+      // Convolution is an effect masquerading as a menu item.
+      Effect * f = GetEffectFromCommandName(wxT("Convolution"));
+      if (f != NULL) {
+         return ApplyEffectCommand(f, command, params);
+      }
+      wxMessageBox(_("Convolution Effect not found"));
+      return false;
    } else if (command == wxT("ExportMp3")) {
       return WriteMp3File(filename, 0); // 0 bitrate means use default/current
    } else if (command == wxT("ExportWav")) {
Index: Menus.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/Menus.cpp,v
retrieving revision 1.350
diff -u -r1.350 Menus.cpp
--- Menus.cpp	27 Oct 2007 16:06:15 -0000	1.350
+++ Menus.cpp	6 Nov 2007 03:29:40 -0000
@@ -357,6 +357,11 @@
       c->SetCommandFlags(wxT("Stereo To Mono"),
          AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag,
          AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag);
+
+      c->AddItem(wxT("Convolution"),      _("&Convolution"),            FN(OnConvolution));
+      c->SetCommandFlags(wxT("Convolution"),
+         AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag,
+         AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag);
 	}
    c->AddSeparator();
 
@@ -557,7 +562,13 @@
       c->SetCommandFlags(wxT("Stereo To Mono"),
          AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag,
          AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag);
-      c->AddItem(wxT("MixAndRender"),       _("&Mix and Render"),             FN(OnMixAndRender));
+
+		c->AddItem(wxT("Convolution"),      _("&Convolution"),            FN(OnConvolution));
+      c->SetCommandFlags(wxT("Convolution"),
+         AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag,
+         AudioIONotBusyFlag | StereoRequiredFlag | WaveTracksSelectedFlag);
+
+		c->AddItem(wxT("MixAndRender"),       _("&Mix and Render"),             FN(OnMixAndRender));
       c->SetCommandFlags(wxT("MixAndRender"),
                          AudioIONotBusyFlag | WaveTracksSelectedFlag,
                          AudioIONotBusyFlag | WaveTracksSelectedFlag);
@@ -887,6 +898,7 @@
 {
    mNormalizeIndex = -1;
    mStereoToMonoIndex = -1;
+   mConvolutionIndex = -1;
 
    for (unsigned int i = 0; i < effects->GetCount(); i++) {
       wxString effectIdentifier = (*effects)[i]->GetEffectIdentifier();
@@ -896,6 +908,9 @@
       else if (effectIdentifier == wxT("StereoToMono")) {
          mStereoToMonoIndex = i;
       }
+      else if (effectIdentifier == wxT("Convolution")) {
+         mConvolutionIndex = i;
+      }
    }
 }
 
@@ -2055,6 +2070,12 @@
       OnEffect(ALL_EFFECTS, mStereoToMonoIndex);
 }
 
+void AudacityProject::OnConvolution(int index)
+{
+   if (mConvolutionIndex >= 0)
+      OnEffect(ALL_EFFECTS, mConvolutionIndex);
+}
+
 void AudacityProject::OnProcessPlugin(int index)
 {
    OnEffect(PLUGIN_EFFECT | PROCESS_EFFECT, index);
Index: Menus.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/Menus.h,v
retrieving revision 1.115
diff -u -r1.115 Menus.h
--- Menus.h	30 Sep 2007 18:59:26 -0000	1.115
+++ Menus.h	6 Nov 2007 03:20:10 -0000
@@ -273,11 +273,13 @@
 void OnImportCleanSpeechPresets();
 void OnExportCleanSpeechPresets();
 void OnStereoToMono(int index);
+void OnConvolution(int index);
 void ResolveEffectIndices(EffectArray *effects);
 wxString BuildCleanFileName(wxString fileName);
 
 int  mNormalizeIndex;
 int  mStereoToMonoIndex;
+int  mConvolutionIndex;
 
         // Help Menu
 
Index: Printing.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/Printing.cpp,v
retrieving revision 1.2
diff -u -r1.2 Printing.cpp
--- Printing.cpp	15 Jun 2006 14:26:13 -0000	1.2
+++ Printing.cpp	4 Nov 2007 23:45:48 -0000
@@ -108,10 +108,13 @@
                                 &viewInfo, false, false, false, true, false);
             break;
          case WaveTrack::SpectrumDisplay:
-            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, false);
+            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, false, false);
+            break;
+         case WaveTrack::SpectrumLogDisplay:
+            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, false, true);
             break;
          case WaveTrack::PitchDisplay:
-            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, true);
+            artist.DrawSpectrum((WaveTrack *)n, *dc, r, &viewInfo, true, false);
             break;
          }
          break;
Index: TrackArtist.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/TrackArtist.cpp,v
retrieving revision 1.104
diff -u -r1.104 TrackArtist.cpp
--- TrackArtist.cpp	28 Oct 2007 20:45:15 -0000	1.104
+++ TrackArtist.cpp	6 Nov 2007 02:11:58 -0000
@@ -236,10 +236,13 @@
                             drawEnvelope,  drawSamples, drawSliders, true, muted);
                break;
             case WaveTrack::SpectrumDisplay:
-               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, false);
+               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, false, false);
+               break;
+            case WaveTrack::SpectrumLogDisplay:
+               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, false, true);
                break;
             case WaveTrack::PitchDisplay:
-               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, true);
+               DrawSpectrum((WaveTrack *)t, dc, rr, viewInfo, true, false);
                break;
             }
             break;              // case Wave
@@ -331,22 +334,22 @@
    }
 
    if (t->GetKind() == Track::Wave
-       && ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumDisplay) {
-      // Spectrum
+       && ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumDisplay)
+	{  // Spectrum
       if (r.height < 60)
          return;
 
+		bool logF = ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumLogDisplay;
       double rate = ((WaveTrack *) t)->GetRate();
       int freq = lrint(rate/2.);
-      int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), freq);
-      if(maxFreq > freq)
-         maxFreq = freq;
+		int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), freq);
+		if(maxFreq > freq)
+			maxFreq = freq;
       int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
-      if(minFreq < 0) {
-         minFreq = 0;
-         gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
-      }
-
+	   if(minFreq < 0) {
+	      minFreq = 0;
+		   gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
+		}
       /*
          draw the ruler
          we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
@@ -368,6 +371,35 @@
 
       vruler->Draw(*dc);
    }
+   if (t->GetKind() == Track::Wave
+   && ((WaveTrack *) t)->GetDisplay() == WaveTrack::SpectrumLogDisplay)
+	{  // SpectrumLog
+      if (r.height < 10)
+         return;
+      double rate = ((WaveTrack *) t)->GetRate();
+      int freq = lrint(rate/2.);
+		int maxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), freq);
+	   if(maxFreq > freq)
+			maxFreq = freq;
+      int minFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), freq/1000.0);
+	   if(minFreq < 1) {
+	      minFreq = 1;
+		   gPrefs->Write(wxT("/SpectrumLog/MinFreq"), 1L);
+		}
+      /*
+         draw the ruler
+         we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
+         and append to the numbers a "k"
+      */
+      vruler->SetBounds(r.x, r.y+1, r.x + r.width, r.y + r.height-1);
+      vruler->SetOrientation(wxVERTICAL);
+      vruler->SetFormat(Ruler::IntFormat);
+      vruler->SetLabelEdges(true);
+      vruler->SetRange(maxFreq, minFreq);
+      vruler->SetUnits(wxT(""));
+		vruler->SetLog(true);
+      vruler->Draw(*dc);
+   }
 
    if (t->GetKind() == Track::Wave
        && ((WaveTrack *) t)->GetDisplay() == WaveTrack::PitchDisplay) {
@@ -1315,7 +1347,7 @@
 
 void TrackArtist::DrawSpectrum(WaveTrack *track,
                                wxDC & dc, wxRect & r,
-                               ViewInfo * viewInfo, bool autocorrelation)
+                               ViewInfo * viewInfo, bool autocorrelation, bool logF)
 {
    // MM: Draw background. We should optimize that a bit more.
    dc.SetPen(*wxTRANSPARENT_PEN);
@@ -1336,12 +1368,12 @@
    }
 
    for (WaveClipList::Node* it=track->GetClipIterator(); it; it=it->GetNext())
-      DrawClipSpectrum(track, it->GetData(), dc, r, viewInfo, autocorrelation);
+      DrawClipSpectrum(track, it->GetData(), dc, r, viewInfo, autocorrelation, logF);
 }
 
 void TrackArtist::DrawClipSpectrum(WaveTrack* track, WaveClip *clip,
                                wxDC & dc, wxRect & r,
-                               ViewInfo * viewInfo, bool autocorrelation)
+                               ViewInfo * viewInfo, bool autocorrelation, bool logF)
 {
    double h = viewInfo->h;
    double pps = viewInfo->zoom;
@@ -1457,14 +1489,27 @@
    bool isGrayscale = false;
    gPrefs->Read(wxT("/Spectrum/Grayscale"), &isGrayscale, false);
    int ifreq = lrint(rate/2);
-   int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), ifreq);
+   int maxFreq;
+	if (!logF)
+		maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), ifreq);
+	else
+		maxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), ifreq);
    if(maxFreq > ifreq)
       maxFreq = ifreq;
-   int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
-   if(minFreq < 0) {
-      minFreq = 0;
-      gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
-   }
+	int minFreq;
+	if (!logF)
+	{	minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
+	   if(minFreq < 0) {
+	      minFreq = 0;
+	      gPrefs->Write(wxT("/Spectrum/MinFreq"), 0L);
+	   }
+	}else
+	{	minFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), ifreq/1000.0);
+	   if(minFreq < 1) {
+	      minFreq = ifreq/1000.0;
+	      gPrefs->Write(wxT("/SpectrumLog/MinFreq"), ifreq/1000.0);
+	   }
+	}
    bool usePxCache = false;
 
    if( !updated && clip->mSpecPxCache->valid && (clip->mSpecPxCache->len == mid.height * mid.width) ) {
@@ -1483,57 +1528,119 @@
 
    int x = 0;
    sampleCount w1 = (sampleCount) ((t0*rate + x *rate *tstep) + .5);
-   while (x < mid.width) {
+
+	while (x < mid.width) {
       sampleCount w0 = w1;
       w1 = (sampleCount) ((t0*rate + (x+1) *rate *tstep) + .5);
 
-      for (int yy = 0; yy < mid.height; yy++) {
-         bool selflag = (ssel0 <= w0 && w1 < ssel1);
-         unsigned char rv, gv, bv;
-         float value;
-
-         if(!usePxCache) {
-            float bin0 = float (yy) * binPerPx + minSamples;
-            float bin1 = float (yy + 1) * binPerPx + minSamples;
-
-
-            if (int (bin1) == int (bin0))
-               value = freq[half * x + int (bin0)];
-            else {
-               float binwidth= bin1 - bin0;
-               value = freq[half * x + int (bin0)] * (1.f - bin0 + (int)bin0);
-
-               bin0 = 1 + int (bin0);
-               while (bin0 < int (bin1)) {
-                  value += freq[half * x + int (bin0)];
-                  bin0 += 1.0;
-               }
-               value += freq[half * x + int (bin1)] * (bin1 - int (bin1));
-
-               value /= binwidth;
-            }
-
-            if (!autocorrelation) {
-               // Last step converts dB to a 0.0-1.0 range
-               value = (value + 80.0) / 80.0;
-            }
-
-            if (value > 1.0)
-               value = float(1.0);
-            if (value < 0.0)
-               value = float(0.0);
-            clip->mSpecPxCache->values[x * mid.height + yy] = value;
-         }
-         else
-            value = clip->mSpecPxCache->values[x * mid.height + yy];
-
-         GetColorGradient(value, selflag, isGrayscale, &rv, &gv, &bv);
-
-         int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
-         data[px++] = rv;
-         data[px++] = gv;
-         data[px] = bv;
-      }
+		if (!logF)
+		{	for (int yy = 0; yy < mid.height; yy++) {
+				bool selflag = (ssel0 <= w0 && w1 < ssel1);
+				unsigned char rv, gv, bv;
+				float value;
+
+				if(!usePxCache) {
+					float bin0 = float (yy) * binPerPx + minSamples;
+					float bin1 = float (yy + 1) * binPerPx + minSamples;
+
+
+					if (int (bin1) == int (bin0))
+						value = freq[half * x + int (bin0)];
+					else {
+						float binwidth= bin1 - bin0;
+						value = freq[half * x + int (bin0)] * (1.f - bin0 + (int)bin0);
+
+						bin0 = 1 + int (bin0);
+						while (bin0 < int (bin1)) {
+							value += freq[half * x + int (bin0)];
+							bin0 += 1.0;
+						}
+						value += freq[half * x + int (bin1)] * (bin1 - int (bin1));
+
+						value /= binwidth;
+					}
+
+					if (!autocorrelation) {
+						// Last step converts dB to a 0.0-1.0 range
+						value = (value + 80.0) / 80.0;
+					}
+
+					if (value > 1.0)
+						value = float(1.0);
+					if (value < 0.0)
+						value = float(0.0);
+					clip->mSpecPxCache->values[x * mid.height + yy] = value;
+				}
+				else
+					value = clip->mSpecPxCache->values[x * mid.height + yy];
+
+				GetColorGradient(value, selflag, isGrayscale, &rv, &gv, &bv);
+
+				int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
+				data[px++] = rv;
+				data[px++] = gv;
+				data[px] = bv;
+			}
+		}else
+		{	int x0=x*half;
+			double e=exp(1.0f), f=rate/2/half, 
+				lmin=log(double(minFreq)),
+				lmax=log(double(maxFreq)),
+				scale=lmax-lmin, 
+				expo=exp(scale);
+			for (int yy = 0; yy < mid.height; yy++) {
+				bool selflag = (ssel0 <= w0 && w1 < ssel1);
+				unsigned char rv, gv, bv;
+				float value;
+
+				if(true) //!usePxCache) 
+				{	float h=double(yy)/mid.height, 
+						yy2=exp(h*scale+lmin)/f;
+					if (int(yy2)>=half)
+						yy2=half-1;
+					if (yy2<0)
+						yy2=0;
+					float bin0 = float (yy2);// * binPerPx + minSamples;
+					float bin1 = float (yy2 + 1);// * binPerPx + minSamples;
+
+					if (int (bin1) == int (bin0))
+						value = freq[x0 + int (bin0)];
+					else {
+						float binwidth= bin1 - bin0;
+						value = freq[x0 + int (bin0)] * (1.f - bin0 + (int)bin0);
+
+						bin0 = 1 + int (bin0);
+						while (bin0 < int (bin1)) {
+							value += freq[x0 + int (bin0)];
+							bin0 += 1.0;
+						}
+						value += freq[x0 + int (bin1)] * (bin1 - int (bin1));
+
+						value /= binwidth;
+					}
+
+					if (!autocorrelation) {
+						// Last step converts dB to a 0.0-1.0 range
+						value = (value + 80.0) / 80.0;
+					}
+
+					if (value > 1.0)
+						value = float(1.0);
+					if (value < 0.0)
+						value = float(0.0);
+					clip->mSpecPxCache->values[x * mid.height + yy] = value;
+				}
+				else
+					value = clip->mSpecPxCache->values[x * mid.height + yy];
+
+				GetColorGradient(value, selflag, isGrayscale, &rv, &gv, &bv);
+
+				int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
+				data[px++] = rv;
+				data[px++] = gv;
+				data[px] = bv;
+			}
+		}
       x++;
    }
 
Index: TrackArtist.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/TrackArtist.h,v
retrieving revision 1.27
diff -u -r1.27 TrackArtist.h
--- TrackArtist.h	18 Jun 2007 16:10:54 -0000	1.27
+++ TrackArtist.h	5 Nov 2007 21:27:14 -0000
@@ -69,7 +69,7 @@
 
    void DrawSpectrum(WaveTrack *track,
                      wxDC & dc, wxRect & r,
-                     ViewInfo * viewInfo, bool autocorrelation);
+                     ViewInfo * viewInfo, bool autocorrelation, bool logF);
 
    void DrawNoteTrack(NoteTrack *track,
                       wxDC & dc, wxRect & r, ViewInfo * viewInfo);
@@ -94,7 +94,7 @@
 
    void DrawClipSpectrum(WaveTrack *track, WaveClip *clip,
                          wxDC & dc, wxRect & r,
-                         ViewInfo * viewInfo, bool autocorrelation);
+                         ViewInfo * viewInfo, bool autocorrelation, bool logF);
 
    void SetBackgroundBrushes(wxBrush unselectedBrush, wxBrush selectedBrush,
 			     wxPen unselectedPen, wxPen selectedPen) {
Index: TrackPanel.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/TrackPanel.cpp,v
retrieving revision 1.376
diff -u -r1.376 TrackPanel.cpp
--- TrackPanel.cpp	3 Nov 2007 15:15:14 -0000	1.376
+++ TrackPanel.cpp	6 Nov 2007 02:00:14 -0000
@@ -302,6 +302,7 @@
    OnWaveformID,
    OnWaveformDBID,
    OnSpectrumID,
+   OnSpectrumLogID,
    OnPitchID,
 
    OnSplitStereoID,
@@ -505,6 +506,7 @@
    mWaveTrackMenu->Append(OnWaveformID, _("Waveform"));
    mWaveTrackMenu->Append(OnWaveformDBID, _("Waveform (dB)"));
    mWaveTrackMenu->Append(OnSpectrumID, _("Spectrum"));
+   mWaveTrackMenu->Append(OnSpectrumLogID, _("Spectrum log(f)"));
    mWaveTrackMenu->Append(OnPitchID, _("Pitch (EAC)"));
    mWaveTrackMenu->AppendSeparator();
    mWaveTrackMenu->AppendCheckItem(OnChannelMonoID, _("Mono"));
@@ -1233,7 +1235,8 @@
 {
    if (event.m_x >= GetVRulerOffset() &&
       t->GetKind() == Track::Wave &&
-      ((WaveTrack *) t)->GetDisplay() <= WaveTrack::SpectrumDisplay) {
+      (((WaveTrack *) t)->GetDisplay() <= WaveTrack::SpectrumDisplay
+		||((WaveTrack *) t)->GetDisplay() <= WaveTrack::SpectrumLogDisplay)) {
       *ppTip = _("Click to vertically zoom in, Shift-click to zoom out, Drag to create a particular zoom region.");
       SetCursor(event.ShiftDown()? *mZoomOutCursor : *mZoomInCursor);
    }
@@ -2529,7 +2532,8 @@
 
    // don't do anything if track is not wave or Spectrum
    if (mCapturedTrack->GetKind() != Track::Wave
-      || ((WaveTrack *) mCapturedTrack)->GetDisplay() > WaveTrack::SpectrumDisplay)
+      && ((WaveTrack *) mCapturedTrack)->GetDisplay() != WaveTrack::SpectrumDisplay
+      && ((WaveTrack *) mCapturedTrack)->GetDisplay() != WaveTrack::SpectrumLogDisplay)
       return;
 
    mMouseCapture = IsVZooming;
@@ -2575,11 +2579,12 @@
    }
 
    float min, max, c, l, binSize = 0.0;
-   bool spectrum;
+   bool spectrum, spectrumLog;
    int windowSize, minBins = 0;
    double rate = ((WaveTrack *)track)->GetRate();
    ((WaveTrack *) track)->GetDisplay() == WaveTrack::SpectrumDisplay ? spectrum = true : spectrum = false;
-   if(spectrum) {
+	spectrumLog=(((WaveTrack *) track)->GetDisplay() == WaveTrack::SpectrumLogDisplay);
+	if(spectrum) {
       min = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
       if(min < 0)
          min = 0;
@@ -2590,22 +2595,30 @@
       binSize = rate / windowSize;
       minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less
    }
-   else
-      track->GetDisplayBounds(&min, &max);
+   else if(spectrumLog) {
+      min = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), rate/2000.0);
+      if(min < 1)
+         min = 1;
+      max = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), rate/2.);
+      if(max > rate/2.)
+         max = rate/2.;
+      windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
+      binSize = rate / windowSize;
+      minBins = wxMin(10, windowSize/2); //minimum 10 freq bins, unless there are less
+   }else
+		track->GetDisplayBounds(&min, &max);
 
    if (IsDragZooming()) {
       // Drag Zoom
       float p1, p2, tmin, tmax;
-      tmin=min;
-      tmax=max;
-
-      p1 = (mZoomStart - ypos) / (float)height;
-      p2 = (mZoomEnd - ypos) / (float)height;
-      max = (tmax * (1.0-p1) + tmin * p1);
-      min = (tmax * (1.0-p2) + tmin * p2);
-
       // Enforce maximum vertical zoom
       if(spectrum) {
+			tmin=min;
+			tmax=max;
+			p1 = (mZoomStart - ypos) / (float)height;
+			p2 = (mZoomEnd - ypos) / (float)height;
+			max = (tmax * (1.0-p1) + tmin * p1);
+			min = (tmax * (1.0-p2) + tmin * p2);
          if(min < 0.)
             min = 0.;
          if(max < min + minBins * binSize)
@@ -2615,7 +2628,21 @@
             min = max - minBins * binSize;
          }
       }
+      else if(spectrumLog) {
+         double xmin = 1-(mZoomEnd - ypos) / (float)height;
+         double xmax = 1-(mZoomStart - ypos) / (float)height;
+			double lmin=log10(min), lmax=log10(max);
+			double d=lmax-lmin;
+			min=wxMax(1.0, pow(10, xmin*d+lmin));
+			max=wxMin(rate/2.0, pow(10, xmax*d+lmin));
+      }
       else {
+			tmin=min;
+			tmax=max;
+			p1 = (mZoomStart - ypos) / (float)height;
+			p2 = (mZoomEnd - ypos) / (float)height;
+			max = (tmax * (1.0-p1) + tmin * p1);
+			min = (tmax * (1.0-p2) + tmin * p2);
          if (max - min < 0.2) {
             c = (min+max)/2;
             min = c-0.1;
@@ -2640,6 +2667,15 @@
             max = wxMin( rate/2., c + 2*l);
          }
       }
+      else if(spectrumLog)
+		{  c = 0.5;
+         double xmin = c-1;
+         double xmax = c+1;
+			double lmin=log10(min), lmax=log10(max);
+			double d=lmax-lmin;
+			min=wxMax(1.0, pow(10, xmin*d+lmin));
+			max=wxMin(rate/2.0, pow(10, xmax*d+lmin));
+      }
       else {
          if (min <= -1.0 && max >= 1.0) {
             min = -2.0;
@@ -2665,8 +2701,20 @@
          c = (max * (1.0-p1) + min * p1);
          min = wxMax( 0.0, c - 0.5*l);
          max = wxMin( rate/2., min + l);
-      }
-      else {
+      }else if(spectrumLog) 
+		{  c = 0.5*(min+max);
+         // Enforce maximum vertical zoom
+         l = wxMax( 10. * binSize, (c - min));   //is this a sensible min? MJS
+
+         p1 = (mZoomStart - ypos) / (float)height;
+         c = 1.0-p1;
+         double xmin = wxMax(0, c-0.25);
+         double xmax = wxMin(1, c+0.25);
+			double lmin=log10(min), lmax=log10(max);
+			double d=lmax-lmin;
+			min=pow(10, xmin*d+lmin);
+			max=pow(10, xmax*d+lmin);
+      }else {
 
          // Zoom in centered on cursor
          if (min < -1.0 || max > 1.0) {
@@ -2702,6 +2750,22 @@
          }
          t = iter.Next();
       }
+   }else if(spectrumLog) {
+      gPrefs->Write(wxT("/SpectrumLog/MaxFreq"), (long)max);
+      gPrefs->Write(wxT("/SpectrumLog/MinFreq"), (long)min);
+      TrackListIterator iter(mTracks);
+      Track *t = iter.First();
+      while (t) {
+         if (t->GetKind() == Track::Wave) {
+            WaveTrack *wt = (WaveTrack *)t;
+            WaveClipList::Node* it;
+            for(it=wt->GetClipIterator(); it; it=it->GetNext()) {
+               WaveClip *clip = it->GetData();
+               clip->mSpecPxCache->valid = false;
+            }
+         }
+         t = iter.Next();
+      }
    }
    else {
       track->SetDisplayBounds(min, max);
@@ -5516,6 +5580,7 @@
       theMenu->Enable(OnWaveformDBID,
                         display != WaveTrack::WaveformDBDisplay);
       theMenu->Enable(OnSpectrumID, display != WaveTrack::SpectrumDisplay);
+      theMenu->Enable(OnSpectrumLogID, display != WaveTrack::SpectrumLogDisplay);
       theMenu->Enable(OnPitchID, display != WaveTrack::PitchDisplay);
       
       WaveTrack * track = (WaveTrack *)t;
Index: WaveTrack.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/WaveTrack.h,v
retrieving revision 1.48
diff -u -r1.48 WaveTrack.h
--- WaveTrack.h	18 Jun 2007 16:10:54 -0000	1.48
+++ WaveTrack.h	4 Nov 2007 23:45:48 -0000
@@ -310,6 +310,7 @@
       WaveformDisplay,
       WaveformDBDisplay,
       SpectrumDisplay,
+		SpectrumLogDisplay,
       PitchDisplay
    } WaveTrackDisplay;
 
Index: effects/LoadEffects.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/LoadEffects.cpp,v
retrieving revision 1.57
diff -u -r1.57 LoadEffects.cpp
--- effects/LoadEffects.cpp	16 Sep 2007 13:46:00 -0000	1.57
+++ effects/LoadEffects.cpp	6 Nov 2007 03:18:52 -0000
@@ -36,6 +36,7 @@
 #include "Reverse.h"
 #include "Silence.h"
 #include "StereoToMono.h"
+#include "Convolution.h"
 #include "ToneGen.h"
 #include "TruncSilence.h"
 #include "Wahwah.h"
@@ -112,6 +113,7 @@
    Effect::RegisterEffect(new EffectRepeat());
    Effect::RegisterEffect(new EffectReverse());
    Effect::RegisterEffect(new EffectStereoToMono(), HIDDEN_EFFECT);// NOT in normal effects list.
+   Effect::RegisterEffect(new EffectConvolution());
    Effect::RegisterEffect(new EffectTruncSilence(), SIMPLE_EFFECT);
    Effect::RegisterEffect(new EffectWahwah());
 
Index: effects/ToneGen.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/ToneGen.cpp,v
retrieving revision 1.46
diff -u -r1.46 ToneGen.cpp
--- effects/ToneGen.cpp	10 Oct 2007 00:16:22 -0000	1.46
+++ effects/ToneGen.cpp	4 Nov 2007 22:51:22 -0000
@@ -50,24 +50,30 @@
    amplitude[1] = float(0.8);
 //   EnableForChirps();
    length = sDefaultGenerateLen;
+	interpolation=0;
 }
 
 wxString EffectToneGen::GetEffectDescription() { 
    // Note: This is useful only after values have been set. 
    /// todo update to include *all* chirp parameters??
    const wxChar* waveformNames[] = {wxT("sine"), wxT("square"), wxT("sawtooth"), wxT("square, no alias")};
+   const wxChar* interpolationNames[] = {wxT("linear"), wxT("logarithmic")};
    return wxString::Format(_("Applied effect: Generate %s wave %s, frequency = %.2f Hz, amplitude = %.2f, %.6lf seconds"), 
-      waveformNames[waveform], mbChirp ? wxT("chirp") : wxT("tone"), frequency[0], amplitude[0], length); 
+      waveformNames[waveform], mbChirp ? wxT("chirp") : wxT("tone"), 
+		frequency[0], amplitude[0], length, interpolationNames[interpolation]); 
 } 
 
 bool EffectToneGen::PromptUser()
 {
    wxArrayString waveforms;
+   wxArrayString interpolations;
    ToneGenDialog dlog(mParent, mbChirp ? _("Chirp Generator") : _("Tone Generator"));
    waveforms.Add(_("Sine"));
    waveforms.Add(_("Square"));
    waveforms.Add(_("Sawtooth"));
    waveforms.Add(_("Square, no alias"));
+   interpolations.Add(_("Linear"));
+   interpolations.Add(_("Logarithmic"));
    dlog.isSelection= false;
 
    if (mT1 > mT0) {
@@ -83,6 +89,8 @@
    dlog.amplitude[1] = amplitude[1];
    dlog.length = length;
    dlog.waveforms = &waveforms;
+	dlog.interpolation = interpolation;
+	dlog.interpolations = &interpolations;
    dlog.Init();
    dlog.TransferDataToWindow();
    dlog.Fit();
@@ -96,6 +104,11 @@
    frequency[1] = dlog.frequency[1];
    amplitude[0] = dlog.amplitude[0];
    amplitude[1] = dlog.amplitude[1];
+	interpolation = dlog.interpolation;
+	if (interpolation==0)
+		mbLogInterpolation = false;
+	if (interpolation==1)
+		mbLogInterpolation = true;
    if( !mbChirp )
    {
       frequency[1] = frequency[0];
@@ -341,6 +354,7 @@
       S.TieTextBox(wxT(""), frequency[1], 10);
       S.TieTextBox(_("Amplitude (0-1)"),amplitude[0], 10);
       S.TieTextBox(wxT(""), amplitude[1], 10);
+      S.TieChoice(_("Interpolation:"), interpolation,  interpolations);
    }
    S.EndMultiColumn();
    S.StartMultiColumn(2, wxCENTER);
Index: effects/ToneGen.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/effects/ToneGen.h,v
retrieving revision 1.23
diff -u -r1.23 ToneGen.h
--- effects/ToneGen.h	29 Jul 2007 02:15:36 -0000	1.23
+++ effects/ToneGen.h	4 Nov 2007 22:44:32 -0000
@@ -78,6 +78,7 @@
    double length;
    float logFrequency[2];
    double mCurRate;
+	int interpolation;
 
    // mSample is an external placeholder to remember the last "buffer"
    // position so we use it to reinitialize from where we left
@@ -114,6 +115,8 @@
    double amplitude[2];
    double length;
    bool isSelection;
+	int interpolation;
+   wxArrayString *interpolations;
 
  private:
    TimeTextCtrl *mToneDurationT;
Index: widgets/Ruler.cpp
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/widgets/Ruler.cpp,v
retrieving revision 1.44
diff -u -r1.44 Ruler.cpp
--- widgets/Ruler.cpp	5 Jul 2007 11:55:38 -0000	1.44
+++ widgets/Ruler.cpp	6 Nov 2007 00:08:02 -0000
@@ -144,7 +144,7 @@
 
 void Ruler::SetFormat(RulerFormat format)
 {
-   // IntFormat, RealFormat, TimeFormat, or LinearDBFormat
+   // IntFormat, RealFormat, RealLogFormat, TimeFormat, or LinearDBFormat
 
    if (mFormat != format) {
       mFormat = format;
@@ -500,6 +500,32 @@
       mMajor = d * 2.0;
       break;
 
+   case RealLogFormat:
+      d = 0.000001;
+      // mDigits is number of digits after the decimal point.
+      mDigits = 6;
+      for(;;) {
+         if (units < d) {
+            mMinor = d;
+            mMajor = d*5.0;
+            return;
+         }
+         d *= 5.0;
+         if (units < d) {
+            mMinor = d;
+            mMajor = d*2.0;
+            return;
+         }
+         d *= 2.0;
+         mDigits--;
+         // More than 10 digit numbers?  Something is badly wrong.
+         // Probably units is coming in with too high a value.
+         wxASSERT( mDigits >= -10 );
+      }
+		mDigits++;
+      mMinor = d;
+      mMajor = d * 2.0;
+      break;
    }
 
 }
@@ -535,6 +561,13 @@
          s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
       }
       break;
+   case RealLogFormat:
+      if (mMinor >= 1.0)
+         s.Printf(wxT("%d"), (int)floor(d+0.5));
+      else {
+         s.Printf(wxString::Format(wxT("%%.%df"), mDigits), d);
+      }
+      break;
    case TimeFormat:
       if (major) {
          if (d < 0) {
@@ -606,7 +639,8 @@
    }
    
    if (mUnits != wxT(""))
-      s = (s + wxT(" ") + mUnits);
+      s = (s + mUnits);
+//      s = (s + wxT(" ") + mUnits);
 
    return s;
 }
@@ -882,8 +916,9 @@
    }
    else {
       // log case
-      
-      double loLog = log10(mMin);
+      mDigits=2;	//TODO: implement dynamic digit computation
+
+		double loLog = log10(mMin);
       double hiLog = log10(mMax);
       double scale = mLength/(hiLog - loLog);
       int loDecade = (int) floor(loLog);
@@ -895,28 +930,38 @@
       
       // Major ticks are the decades
       double decade = startDecade;
-      for(i=loDecade; i<hiDecade; i++) {
-         if(i!=loDecade) {
-            val = decade;
-            if(val > mMin && val < mMax) {
+		int delta=hiDecade-loDecade, steps=abs(delta);
+		double step = delta>=0 ? 10 : 0.1;
+		double rMin=wxMin(mMin, mMax), rMax=wxMax(mMin, mMax);
+		for(i=0; i<=steps; i++) 
+		{  // if(i!=0) 
+			{  val = decade;
+            if(val > rMin && val < rMax) {
                pos = (int)(((log10(val) - loLog)*scale)+0.5);
                Tick(pos, val, true);
             }
          }
-         decade *= 10.;
+         decade *= step;
       }
       
       // Minor ticks are multiples of decades
       decade = startDecade;
-      for(i=loDecade; i<hiDecade; i++) {
-         for(j=2; j<=9; j++) {
+		int start, end, mstep;
+		if (delta > 0)
+		{	start=2; end=10; mstep=1;
+		}else
+		{	start=9; end=1; mstep=-1;
+		}
+		steps++;
+      for(i=0; i<=steps; i++) {
+         for(j=start; j!=end; j+=mstep) {
             val = decade * j;
-            if(val >= mMin && val < mMax) {
+            if(val >= rMin && val < rMax) {
                pos = (int)(((log10(val) - loLog)*scale)+0.5);
                Tick(pos, val, false);
             }
          }
-         decade *= 10.;
+         decade *= step;
       }
    }
    
Index: widgets/Ruler.h
===================================================================
RCS file: /cvsroot/audacity/audacity-src/src/widgets/Ruler.h,v
retrieving revision 1.20
diff -u -r1.20 Ruler.h
--- widgets/Ruler.h	5 Jul 2007 11:55:38 -0000	1.20
+++ widgets/Ruler.h	5 Nov 2007 23:39:36 -0000
@@ -27,7 +27,8 @@
    enum RulerFormat {
       IntFormat,
       RealFormat,
-      TimeFormat,
+		RealLogFormat,
+		TimeFormat,
       LinearDBFormat,
    };
 

Locked