view base/ColumnOp.h @ 1188:d9698ee93659 spectrogram-minor-refactor

Extend column logic to peak frequency display as well, and correct some scopes according to whether values are per source column or per target pixel
author Chris Cannam
date Mon, 20 Jun 2016 12:00:32 +0100
parents fd40a5335968
children f6998e304b36
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*
    Sonic Visualiser
    An audio file viewer and annotation editor.
    Centre for Digital Music, Queen Mary, University of London.
    This file copyright 2006-2016 Chris Cannam and QMUL.
    
    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.  See the file
    COPYING included with this distribution for more information.
*/

#ifndef COLUMN_OP_H
#define COLUMN_OP_H

#include "BaseTypes.h"

#include <cmath>

class ColumnOp
{
public:
    typedef std::vector<float> Column;

    enum Normalization {
        NoNormalization,
        NormalizeColumns,
        NormalizeVisibleArea,
        NormalizeHybrid
    };

    static Column fftScale(const Column &in, int fftSize) {
	
	Column out;
	out.reserve(in.size());
	float scale = 2.f / float(fftSize);
	for (auto v: in) {
	    out.push_back(v * scale);
	}
	
	return out;
    }

    static bool isPeak(const Column &in, int ix) {
	
	if (!in_range_for(in, ix-1)) return false;
	if (!in_range_for(in, ix+1)) return false;
	if (in[ix] < in[ix+1]) return false;
	if (in[ix] < in[ix-1]) return false;
	
	return true;
    }

    static Column peakPick(const Column &in) {
	
	std::vector<float> out(in.size(), 0.f);
	for (int i = 0; in_range_for(in, i); ++i) {
	    if (isPeak(in, i)) {
		out[i] = in[i];
	    }
	}
	
	return out;
    }

    static Column normalize(const Column &in, Normalization n) {

	if (n == NoNormalization || n == NormalizeVisibleArea) {
	    return in;
	}
	
	float max = *max_element(in.begin(), in.end());

	if (n == NormalizeColumns && max == 0.f) {
	    return in;
	}

	if (n == NormalizeHybrid && max <= 0.f) {
	    return in;
	}
    
	std::vector<float> out;
	out.reserve(in.size());

	float scale;
	if (n == NormalizeHybrid) {
	    scale = log10f(max + 1.f) / max;
	} else {
	    scale = 1.f / max;
	}
    
	for (auto v: in) {
	    out.push_back(v * scale);
	}
	return out;
    }

    static Column applyGain(const Column &in, float gain) {
	
	if (gain == 1.f) {
	    return in;
	}
	Column out;
	out.reserve(in.size());
	for (auto v: in) {
	    out.push_back(v * gain);
	}
	return out;
    }

    static Column distribute(const Column &in,
			     int h,
			     const std::vector<double> &binfory,
			     int minbin,
			     bool interpolate) {
	
	std::vector<float> out(h, 0.f);
	int bins = int(in.size());

	for (int y = 0; y < h; ++y) {
        
	    double sy0 = binfory[y] - minbin;
	    double sy1 = sy0 + 1;
	    if (y+1 < h) {
		sy1 = binfory[y+1] - minbin;
	    }
        
	    if (interpolate && fabs(sy1 - sy0) < 1.0) {
            
		double centre = (sy0 + sy1) / 2;
		double dist = (centre - 0.5) - rint(centre - 0.5);
		int bin = int(centre);

		int other = (dist < 0 ? (bin-1) : (bin+1));

		if (bin < 0) bin = 0;
		if (bin >= bins) bin = bins-1;

		if (other < 0 || other >= bins) {
		    other = bin;
		}

		double prop = 1.0 - fabs(dist);

		double v0 = in[bin];
		double v1 = in[other];
                
		out[y] = float(prop * v0 + (1.0 - prop) * v1);

	    } else { // not interpolating this one

		int by0 = int(sy0 + 0.0001);
		int by1 = int(sy1 + 0.0001);
		if (by1 < by0 + 1) by1 = by0 + 1;

		for (int bin = by0; bin < by1; ++bin) {

		    float value = in[bin];

		    if (value > out[y]) {
			out[y] = value;
		    }
		}
	    }
	}

	return out;
    }

};

#endif