# HG changeset patch # User Chris Cannam # Date 1183466778 0 # Node ID f1f47660483d2ce49a49b1cb443648ff61e25a0c # Parent 8bbc9e336475759966eaeb50b346591fb54d16ee * 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 diff -r 8bbc9e336475 -r f1f47660483d data/fft/FFTDataServer.cpp --- 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 { diff -r 8bbc9e336475 -r f1f47660483d data/fft/FFTDataServer.h --- 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; diff -r 8bbc9e336475 -r f1f47660483d data/fileio/MP3FileReader.cpp --- 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 #endif +#define DEBUG_ID3TAG 1 #include #include @@ -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 diff -r 8bbc9e336475 -r f1f47660483d data/model/FFTModel.h --- 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;