Chris@224: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@224: 
Chris@224: /*
Chris@224:     Sonic Visualiser
Chris@224:     An audio file viewer and annotation editor.
Chris@224:     Centre for Digital Music, Queen Mary, University of London.
Chris@224:     This file copyright 2006 Chris Cannam.
Chris@224:     
Chris@224:     This program is free software; you can redistribute it and/or
Chris@224:     modify it under the terms of the GNU General Public License as
Chris@224:     published by the Free Software Foundation; either version 2 of the
Chris@224:     License, or (at your option) any later version.  See the file
Chris@224:     COPYING included with this distribution for more information.
Chris@224: */
Chris@224: 
Chris@224: #include "LogRange.h"
Chris@573: #include "system/System.h"
Chris@224: 
Chris@224: #include <algorithm>
Chris@464: #include <iostream>
Chris@224: #include <cmath>
Chris@224: 
Chris@224: void
Chris@224: LogRange::mapRange(float &min, float &max, float logthresh)
Chris@224: {
Chris@224:     if (min > max) std::swap(min, max);
Chris@224:     if (max == min) max = min + 1;
Chris@224: 
Chris@690: //    SVDEBUG << "LogRange::mapRange: min = " << min << ", max = " << max << endl;
Chris@464: 
Chris@224:     if (min >= 0.f) {
Chris@224: 
Chris@224:         max = log10f(max); // we know max != 0
Chris@224: 
Chris@224:         if (min == 0.f) min = std::min(logthresh, max);
Chris@224:         else min = log10f(min);
Chris@224: 
Chris@690: //        SVDEBUG << "LogRange::mapRange: positive: min = " << min << ", max = " << max << endl;
Chris@464: 
Chris@224:     } else if (max <= 0.f) {
Chris@224:         
Chris@224:         min = log10f(-min); // we know min != 0
Chris@224:         
Chris@224:         if (max == 0.f) max = std::min(logthresh, min);
Chris@224:         else max = log10f(-max);
Chris@224:         
Chris@224:         std::swap(min, max);
Chris@224:         
Chris@690: //        SVDEBUG << "LogRange::mapRange: negative: min = " << min << ", max = " << max << endl;
Chris@464: 
Chris@224:     } else {
Chris@224:         
Chris@224:         // min < 0 and max > 0
Chris@224:         
Chris@224:         max = log10f(std::max(max, -min));
Chris@224:         min = std::min(logthresh, max);
Chris@464: 
Chris@690: //        SVDEBUG << "LogRange::mapRange: spanning: min = " << min << ", max = " << max << endl;
Chris@224:     }
Chris@224: 
Chris@224:     if (min == max) min = max - 1;
Chris@224: }        
Chris@224: 
Chris@224: float
Chris@224: LogRange::map(float value, float thresh)
Chris@224: {
Chris@224:     if (value == 0.f) return thresh;
Chris@224:     return log10f(fabsf(value));
Chris@224: }
Chris@224: 
Chris@266: float
Chris@266: LogRange::unmap(float value)
Chris@266: {
Chris@266:     return powf(10.0, value);
Chris@266: }
Chris@478: 
Chris@478: static float
Chris@478: sd(const std::vector<float> &values, size_t start, size_t n)
Chris@478: {
Chris@478:     float sum = 0.f, mean = 0.f, variance = 0.f;
Chris@478:     for (size_t i = 0; i < n; ++i) {
Chris@478:         sum += values[start + i];
Chris@478:     }
Chris@478:     mean = sum / n;
Chris@478:     for (size_t i = 0; i < n; ++i) {
Chris@478:         float diff = values[start + i] - mean;
Chris@478:         variance += diff * diff;
Chris@478:     }
Chris@478:     variance = variance / n;
Chris@478:     return sqrtf(variance);
Chris@478: }
Chris@478: 
Chris@478: bool
Chris@478: LogRange::useLogScale(std::vector<float> values)
Chris@478: {
Chris@478:     // Principle: Partition the data into two sets around the median;
Chris@478:     // calculate the standard deviation of each set; if the two SDs
Chris@478:     // are very different, it's likely that a log scale would be good.
Chris@478: 
Chris@478:     if (values.size() < 4) return false;
Chris@478:     std::sort(values.begin(), values.end());
Chris@478:     size_t mi = values.size() / 2;
Chris@478: 
Chris@478:     float sd0 = sd(values, 0, mi);
Chris@478:     float sd1 = sd(values, mi, values.size() - mi);
Chris@478: 
Chris@690:     SVDEBUG << "LogRange::useLogScale: sd0 = "
Chris@687:               << sd0 << ", sd1 = " << sd1 << endl;
Chris@478: 
Chris@478:     if (sd0 == 0 || sd1 == 0) return false;
Chris@478: 
Chris@478:     // I wonder what method of determining "one sd much bigger than
Chris@478:     // the other" would be appropriate here...
Chris@478:     if (std::max(sd0, sd1) / std::min(sd0, sd1) > 10.f) return true;
Chris@478:     else return false;
Chris@478: }
Chris@478: