changeset 273:f1f47660483d

* Fix up and simplify the LayerTreeModel, removing a horrible memory leak * Move phase-unwrapped frequency estimation from SpectrogramLayer to FFTDataServer * Make the spectrum show peak phase-unwrapped frequencies as well (still needs work) * Start adding piano keyboard horizontal scale to spectrum * Debug output for id3 tags
author Chris Cannam
date Tue, 03 Jul 2007 12:46:18 +0000
parents 8bbc9e336475
children e412f65884ee
files data/fft/FFTDataServer.cpp data/fft/FFTDataServer.h data/fileio/MP3FileReader.cpp data/model/FFTModel.h
diffstat 4 files changed, 107 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/data/fft/FFTDataServer.cpp	Mon Jul 02 14:57:01 2007 +0000
+++ b/data/fft/FFTDataServer.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -1099,6 +1099,43 @@
     }
 }    
 
+bool
+FFTDataServer::estimateStableFrequency(size_t x, size_t y,
+                                       float sampleRate, float &frequency)
+{
+    frequency = (float(y) * sampleRate) / m_fftSize;
+
+    if (x+1 >= m_width || y >= m_height) return false;
+
+    // At frequency f, a phase shift of 2pi (one cycle) happens in 1/f sec.
+    // At hopsize h and sample rate sr, one hop happens in h/sr sec.
+    // At window size w, for bin b, f is b*sr/w.
+    // thus 2pi phase shift happens in w/(b*sr) sec.
+    // We need to know what phase shift we expect from h/sr sec.
+    // -> 2pi * ((h/sr) / (w/(b*sr)))
+    //  = 2pi * ((h * b * sr) / (w * sr))
+    //  = 2pi * (h * b) / w.
+
+    float oldPhase = getPhaseAt(x, y);
+    float newPhase = getPhaseAt(x+1, y);
+
+    float expectedPhase =
+	oldPhase + (2.0 * M_PI * y * m_windowIncrement) / m_fftSize;
+
+    float phaseError = princargf(newPhase - expectedPhase);
+
+//    bool stable = (fabsf(phaseError) < (1.1f * (m_windowIncrement * M_PI) / m_fftSize));
+
+    // The new frequency estimate based on the phase error resulting
+    // from assuming the "native" frequency of this bin
+
+    frequency =
+        (sampleRate * (expectedPhase + phaseError - oldPhase)) /
+        (2 * M_PI * m_windowIncrement);
+
+    return true;
+}
+
 size_t
 FFTDataServer::getFillCompletion() const 
 {
--- a/data/fft/FFTDataServer.h	Mon Jul 02 14:57:01 2007 +0000
+++ b/data/fft/FFTDataServer.h	Tue Jul 03 12:46:18 2007 +0000
@@ -92,6 +92,12 @@
         return getMagnitudeAt(x, y) > threshold;
     }
 
+    // Calculate an estimated frequency for a stable signal in this
+    // bin, using phase unwrapping.  This will be completely wrong if
+    // the signal is not stable here.
+    bool estimateStableFrequency(size_t x, size_t y,
+                                 float sampleRate, float &frequency);
+
     size_t getFillCompletion() const;
     size_t getFillExtent() const;
 
--- a/data/fileio/MP3FileReader.cpp	Mon Jul 02 14:57:01 2007 +0000
+++ b/data/fileio/MP3FileReader.cpp	Tue Jul 03 12:46:18 2007 +0000
@@ -28,6 +28,7 @@
 #ifdef HAVE_ID3TAG
 #include <id3tag.h>
 #endif
+#define DEBUG_ID3TAG 1
 
 #include <QApplication>
 #include <QFileInfo>
@@ -151,30 +152,69 @@
                                    ID3_FILE_MODE_READONLY);
     if (!file) return;
 
+    // We can do this a lot more elegantly, but we'll leave that for
+    // when we implement support for more than just the one tag!
+    
     id3_tag *tag = id3_file_tag(file);
-
-    if (tag) {
-        id3_frame *frame = id3_tag_findframe(tag, "TIT2", 0); // work title
-
-        if (frame && frame->nfields >= 2) {
-            unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
-
-            if (nstrings > 0) {
-                id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
-
-                if (ustr) {
-                    id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
-                    if (u8str) {
-                        m_title = QString::fromUtf8((const char *)u8str);
-                        free(u8str);
-                    }
-                }
-            }
-        }
+    if (!tag) {
+#ifdef DEBUG_ID3TAG
+        std::cerr << "MP3FileReader::loadTags: No ID3 tag found" << std::endl;
+#endif
+        id3_file_close(file);
+        return;
     }
 
+    id3_frame *frame = id3_tag_findframe(tag, "TIT2", 0); // work title
+    if (!frame) {
+#ifdef DEBUG_ID3TAG
+        std::cerr << "MP3FileReader::loadTags: No work title in ID3 tag" << std::endl;
+#endif
+        id3_file_close(file);
+        return;
+    }
+        
+    if (frame->nfields < 2) {
+        std::cerr << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for work title in ID3 tag" << std::endl;
+        id3_file_close(file);
+        return;
+    }
+
+    unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]);
+    if (nstrings == 0) {
+#ifdef DEBUG_ID3TAG
+        std::cerr << "MP3FileReader::loadTags: No data for work title in ID3 tag" << std::endl;
+#endif
+        id3_file_close(file);
+        return;
+    }
+
+    id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0);
+    if (!ustr) {
+#ifdef DEBUG_ID3TAG
+        std::cerr << "MP3FileReader::loadTags: Invalid or absent data for work title in ID3 tag" << std::endl;
+#endif
+        id3_file_close(file);
+        return;
+    }
+        
+    id3_utf8_t *u8str = id3_ucs4_utf8duplicate(ustr);
+    if (!u8str) {
+        std::cerr << "MP3FileReader::loadTags: ERROR: Internal error: Failed to convert UCS4 to UTF8 in ID3 title" << std::endl;
+        id3_file_close(file);
+        return;
+    }
+        
+    m_title = QString::fromUtf8((const char *)u8str);
+    free(u8str);
     id3_file_close(file);
+
+#else
+#ifdef DEBUG_ID3TAG
+    std::cerr << "MP3FileReader::loadTags: ID3 tag support not compiled in"
+              << std::endl;
 #endif
+#endif
+
 }
 
 void
--- a/data/model/FFTModel.h	Mon Jul 02 14:57:01 2007 +0000
+++ b/data/model/FFTModel.h	Tue Jul 03 12:46:18 2007 +0000
@@ -123,6 +123,11 @@
     virtual void getColumn(size_t x, Column &result) const;
     virtual QString getBinName(size_t n) const;
 
+    virtual bool estimateStableFrequency(size_t x, size_t y, float &frequency) {
+        return m_server->estimateStableFrequency(x << m_xshift, y << m_yshift,
+                                                 getSampleRate(), frequency);
+    }
+
     virtual int getCompletion() const { return m_server->getFillCompletion(); }
 
     virtual Model *clone() const;