Fex

Check-in [324d1ee857]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:begining python ui
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | origin/sfml
Files: files | file ages | folders
SHA3-256:324d1ee857e649ba5708856eeff38cd2b433cac0d6f9248479e69d16e1eed0c8
User & Date: jesse.mcclure@umassmed.edu 2016-02-12 17:12:15
Context
2016-02-15
19:42
crop window implemented check-in: 57f7252666 user: jesse.mcclure@umassmed.edu tags: trunk, origin/sfml
2016-02-12
17:12
begining python ui check-in: 324d1ee857 user: jesse.mcclure@umassmed.edu tags: trunk, origin/sfml
2016-02-10
18:35
cleaning up overlap drawing check-in: e7099aefda user: jesse.mcclure@umassmed.edu tags: trunk, origin/sfml
Changes

Deleted Makefile.win.static.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
PROG        =  fex
VER         =  3.0pre
PREFIX     ?=  /usr
CC          =  i686-w64-mingw32-c++
CXX         =  i686-w64-mingw32-c++
CONFFILE   ?=  ${PREFIX}/share/${PROG}/${PROG}rc
DEFS        =  -DPROGRAM_NAME=${PROG} -DPROGRAM_VER=${VER} -DDEFAULT_CONFIG=${CONFFILE}
CPPFLAGS   ?=  -D_FORTIFY_SOURCE=2
CXXFLAGS   ?=  -O2 -pipe --param=ssp-buffer-size=4
LDFLAGS    ?=  -Wl,-O1,--sort-common,--as-needed
CXXFLAGS   +=  -static $(shell i686-w64-mingw32-pkg-config --static --cflags fftw3) -std=c++11 ${DEFS}
LDLIBS     +=  -static $(shell i686-w64-mingw32-pkg-config --static --libs fftw3) $(shell pkg-config --static --libs sfml-all)
MODULES     =  config fex fft spectrogram
MANPAGES    =  ${PROG}.1 ${PROG}rc.5
VPATH       =  src

${PROG}: ${MODULES:%=%.o}

%.o: %.cpp %.hpp

clean:
	@rm -rf ${PROG}-*.tar.gz
	@rm -rf ${MODULES:%=%.o}

distclean: clean
	@rm -rf ${PROG} *.tar.gz

tarball: distclean
	@tar -czf ${PROG}-${VER}.tar.gz *

bundle: ${PROG}
	@mv ${PROG} ${PROG}.exe
	@cp /usr/i686-w64-mingw32/bin/libfftw3-3.dll ./
	@cp /usr/i686-w64-mingw32/bin/libfftw3f-3.dll ./
	@cp /usr/i686-w64-mingw32/bin/libfftw3l-3.dll ./
	@cp /usr/i686-w64-mingw32/bin/sfml-audio-2.dll ./
	@cp /usr/i686-w64-mingw32/bin/sfml-graphics-2.dll ./
	@cp /usr/i686-w64-mingw32/bin/sfml-network-2.dll ./
	@cp /usr/i686-w64-mingw32/bin/sfml-system-2.dll ./
	@cp /usr/i686-w64-mingw32/bin/sfml-window-2.dll ./
	@tar -czf ${PROG}-${VER}-win32.tar.gz ${PROG}.exe *.dll
	@rm -f *.dll

.PHONY: clean distclean tarball bundle
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
























































































Changes to TODO.

1
2
3
4





- Fix scaling of HUD
	* add second view?
- Add eraser function to spectrogram module





<
<

>
>
>
>
1


2
3
4
5
6



- Add eraser function to spectrogram module
- Add crop option
- Add log transformation to frequency?
- Implement config file reading

Added pyfex/default.ini.























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

[defaults]
window length              = 256
bin size                   = 64
low pass                   = 01.25
high pass                  = 10.00
threshold                  = 18.00
floor                      = 24.00

[colors]
window background          = #606468FF
spectrogram background     = #FFFFFFFF
spectrogram foreground     = #000000FF
threshold                  = #58749848
point border               = #3399FFFF
point fill                 = #3399FF48
lines                      = #E86850FF

[warbler]
#match file extension       = wav
#match mime type            = audio/x-wav
match path                 = warber
threshold                  = 14.00

[sparrow]
match path                 = sparrow
threshold                  = 24.00

Added pyfex/fexUI.





















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env python

from sys import argv
from subprocess import DEVNULL, check_output
from configparser import ConfigParser

ini = ConfigParser()
ini.read('default.ini')
conf = ini['defaults']
col = ini['colors']
args = [
	'./fex',
	'--winSize=' +    conf['window length'],
	'--binSize=' +    conf['bin size'],
	'--loPass=' +     conf['low pass'],
	'--hiPass=' +     conf['high pass'],
	'--threshold=' +  conf['threshold'],
	'--floor=' +      conf['floor'],
	'--winBG=' +      col['window background'],
	'--specBG=' +     col['spectrogram background'],
	'--specFG=' +     col['spectrogram foreground'],
	'--threshFG=' +   col['threshold'],
	'--pointBG=' +    col['point fill'],
	'--pointFG=' +    col['point border'],
	'--linesFG=' +    col['lines']
]

def run_fex(files):
	if len(files) == 0:
		print("zero")
		# prompt with dialog returning list of files
		# run_fex(list of files)
	elif len(files) == 1:
		print(check_output(args + [files[0]],stderr=DEVNULL).decode("utf-8"))
	else:
		print("many")
		# loop through files running fex on each

argv.remove(argv[0])
run_fex(argv)


Changes to src/config.cpp.

1
2

3











4
5

































6
7
8
9
10
11
12
13
14
15
16


#include "config.hpp"













Config::Config(int argc, const char **argv) {
	// TODO read config file to override defaults

































	if (argc != 2) exit(1);
	fname = strdup(argv[1]);
	char *dup = strdup(argv[1]);
	name = strdup(basename(dup));
	free(dup);
}

Config::~Config() {
	if (name) free(name);
	if (fname) free(fname);
}



>

>
>
>
>
>
>
>
>
>
>
>
|
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|








>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

#include "config.hpp"
#include <getopt.h>

sf::Color toColor(char *str) {
	if (str[0] == '#') ++str;
	unsigned long hex = strtoul(str,NULL,16);
	unsigned short int r,g,b,a;
	r = (hex & 0xFF000000) >> 24;
	g = (hex & 0x00FF0000) >> 16;
	b = (hex & 0x0000FF00) >> 8;
	a = (hex & 0x000000FF);
	return sf::Color(r,g,b,a);
}

Config::Config(int argc, char *const *argv) {

	struct option opts[] = {
		{ "winSize",     optional_argument, 0, 0 },
		{ "binSize",     optional_argument, 0, 0 },
		{ "loPass",      optional_argument, 0, 0 },
		{ "hiPass",      optional_argument, 0, 0 },
		{ "threshold",   optional_argument, 0, 0 },
		{ "floor",       optional_argument, 0, 0 },
		{ "winBG",       optional_argument, 0, 0 },
		{ "specBG",      optional_argument, 0, 0 },
		{ "specFG",      optional_argument, 0, 0 },
		{ "threshFG",    optional_argument, 0, 0 },
		{ "pointBG",     optional_argument, 0, 0 },
		{ "pointFG",     optional_argument, 0, 0 },
		{ "linesFG",     optional_argument, 0, 0 },
		{0,              0,                 0, 0 }
	};
	int i, c, index;
	for (i = 1; (c=getopt_long_only(argc, argv, "", opts, &index)) != -1; ++i) {
		if (c != 0 || !optarg) continue;
		else if (index == 0)  conf.winlen     = atoi(optarg);
		else if (index == 1)  conf.hop        = atoi(optarg);
		else if (index == 2)  conf.lopass     = atof(optarg);
		else if (index == 3)  conf.hipass     = atof(optarg);
		else if (index == 4)  conf.threshold  = atof(optarg);
		else if (index == 5)  conf.floor      = atof(optarg);
		else if (index == 6)  conf.winBG      = toColor(optarg);
		else if (index == 7)  conf.specBG     = toColor(optarg);
		else if (index == 8)  conf.specFG     = toColor(optarg);
		else if (index == 9)  conf.threshFG   = toColor(optarg);
		else if (index == 10) conf.pointBG    = toColor(optarg);
		else if (index == 11) conf.pointFG    = toColor(optarg);
		else if (index == 12) conf.linesFG    = toColor(optarg);
	}
	if (i > argc) exit(1);
	fname = strdup(argv[i]);
	char *dup = strdup(argv[i]);
	name = strdup(basename(dup));
	free(dup);
}

Config::~Config() {
	if (name) free(name);
	if (fname) free(fname);
}

Changes to src/config.hpp.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Config {
	protected:
		struct {
			int winlen           = 256;
			int hop              = 64;
			double lopass        = 01.25;
			double hipass        = 10.00;
			double threshold     = 14.00;
			double floor         = 24.00;
			sf::Color winBG      = sf::Color(0xFE,0xEF,0xE0);
			sf::Color specBG     = sf::Color(0xFF,0xFF,0xFF);
			sf::Color specFG     = sf::Color(0x00,0x00,0x00);
			sf::Color threshFG   = sf::Color(0x58,0x74,0x98,0x48);
			sf::Color pointFB    = sf::Color(0x33,0x99,0xFF);
			sf::Color pointBG    = sf::Color(0x33,0x99,0xFF,0x48);
			sf::Color linesFG    = sf::Color(0xE8,0x68,0x50);
		} conf;
		struct {
			bool cursor       = false;
			bool hud          = true;
			bool overlay      = true;
		} show;
		char *fname = NULL, *name = NULL;

	public:
		Config(int, const char **);
		~Config();
};








|

|



|











|



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Config {
	protected:
		struct {
			int winlen           = 256;
			int hop              = 64;
			double lopass        = 01.25;
			double hipass        = 10.00;
			double threshold     = 18.00;
			double floor         = 24.00;
			sf::Color winBG      = sf::Color(0x60,0x64,0x68);
			sf::Color specBG     = sf::Color(0xFF,0xFF,0xFF);
			sf::Color specFG     = sf::Color(0x00,0x00,0x00);
			sf::Color threshFG   = sf::Color(0x58,0x74,0x98,0x48);
			sf::Color pointFG    = sf::Color(0x33,0x99,0xFF);
			sf::Color pointBG    = sf::Color(0x33,0x99,0xFF,0x48);
			sf::Color linesFG    = sf::Color(0xE8,0x68,0x50);
		} conf;
		struct {
			bool cursor       = false;
			bool hud          = true;
			bool overlay      = true;
		} show;
		char *fname = NULL, *name = NULL;

	public:
		Config(int, char *const *);
		~Config();
};

Changes to src/fex.cpp.

1
2
3
4

5
6

7
8

#include "spectrogram.hpp"

int main(int argc, const char **argv) {

	Spectrogram spec(argc, argv);
	spec.main_loop();

	return EXIT_SUCCESS;
}



|
>


>


1
2
3
4
5
6
7
8
9
10

#include "spectrogram.hpp"

int main(int argc, char *const *argv) {
printf("ONE\n");
	Spectrogram spec(argc, argv);
	spec.main_loop();
printf("TWO\n");
	return EXIT_SUCCESS;
}

Changes to src/fft.cpp.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
82
83
84
85
86
87
88

89
90
91
92







93
94
95
96
97
98
99
...
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128





129
130
131
132
133
134
135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
Blackman     0.42659   0.49656   0.076849  0.00
Nuttall      0.355768  0.487396  0.144232  0.012604
BlackNutt    0.3635819 0.4891775 0.1365995 0.0106411
BlackHarris  0.35875   0.48829   0.14128   0.01168

*/

Fft::Fft(int argc, const char **argv) : Config(argc, argv) {
	song.loadFromFile(fname);
	ntime = song.getSampleCount() / conf.hop;
	nfreq = conf.winlen / 2 + 0.5;
	time = (double *) malloc(ntime * sizeof(double));
	freq = (double *) malloc(nfreq * sizeof(double));
	amp = (double *) malloc(nfreq * ntime * sizeof(double));
	erase = (unsigned short int *) calloc(nfreq * ntime, sizeof(unsigned short int *));
................................................................................
	amp = new_amp;
	/* normalize, log transform and scale to dB */
	double max = 0.0;
	for (t = 0; t < ntime; ++t) for (f = 0; f < nfreq; ++f)
		if (amp[nfreq * t + f] > max) max = amp[nfreq * t + f];
	for (t = 0; t < ntime; ++t) for (f = 0; f < nfreq; ++f)
		amp[nfreq * t + f] = 10.0 * log10(amp[nfreq * t + f] / max);

	lines.setPrimitiveType(sf::LinesStrip);
	lines.resize(ntime);
	points.setPrimitiveType(sf::Quads);
	points.resize(ntime * 4);







	makeSpectrogram();
	makeThreshold();
	makeOverlay();
}

void Fft::makeSpectrogram() {
	int t, f;
................................................................................
	texSpec.loadFromImage(img);
	texSpec.setSmooth(true);
	spec = sf::Sprite(texSpec);
	spec.setScale(1.0,-1.0);
}

void Fft::makeOverlay() {
// TODO add fex calculation here?
//   * Change function name to "recalculate"?
	int t, f, fmax, n;;
	double max;

	for (t = 0, n = 0; t < ntime; ++t) {
		fmax = -1;
		max = std::numeric_limits<double>::lowest(); // TODO: is this negative?  Should it be?
		for (f = 0; f < nfreq; ++f) {
			if (erase[nfreq * t + f]) continue;
			if (amp[nfreq * t + f] < max) continue;
			max = amp[nfreq * t + (fmax=f)];
		}
		if (max < -1.0 * conf.threshold) continue;





		lines[n].position = sf::Vector2f(t + 0.5, - fmax - 0.5);
		points[4*n].position = sf::Vector2f(t, - fmax);
		points[4*n+1].position = sf::Vector2f(t + 1, - fmax);
		points[4*n+2].position = sf::Vector2f(t + 1, - fmax - 1);
		points[4*n+3].position = sf::Vector2f(t, - fmax - 1);
// TODO: These next lines should only need to be done once ... move to constructor?
		lines[n].color = conf.linesFG;
		points[4*n].texCoords = sf::Vector2f(0,0);
		points[4*n+1].texCoords = sf::Vector2f(64,0);
		points[4*n+2].texCoords = sf::Vector2f(64,64);
		points[4*n+3].texCoords = sf::Vector2f(0,64);
		++n;
	}
	for (; n < ntime; ++n) {

		lines[n].position = lines[n-1].position;
		points[4*n].position = sf::Vector2f(-2,0);
		points[4*n+1].position = sf::Vector2f(-2,0);
		points[4*n+2].position = sf::Vector2f(-2,0);
		points[4*n+3].position = sf::Vector2f(-2,0);
	}
}







|







 







>




>
>
>
>
>
>
>







 







<


|
>









>
>
>
>
>





<
<
<
<
<
<



>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146






147
148
149
150
151
152
153
154
155
156
157
Blackman     0.42659   0.49656   0.076849  0.00
Nuttall      0.355768  0.487396  0.144232  0.012604
BlackNutt    0.3635819 0.4891775 0.1365995 0.0106411
BlackHarris  0.35875   0.48829   0.14128   0.01168

*/

Fft::Fft(int argc, char *const *argv) : Config(argc, argv) {
	song.loadFromFile(fname);
	ntime = song.getSampleCount() / conf.hop;
	nfreq = conf.winlen / 2 + 0.5;
	time = (double *) malloc(ntime * sizeof(double));
	freq = (double *) malloc(nfreq * sizeof(double));
	amp = (double *) malloc(nfreq * ntime * sizeof(double));
	erase = (unsigned short int *) calloc(nfreq * ntime, sizeof(unsigned short int *));
................................................................................
	amp = new_amp;
	/* normalize, log transform and scale to dB */
	double max = 0.0;
	for (t = 0; t < ntime; ++t) for (f = 0; f < nfreq; ++f)
		if (amp[nfreq * t + f] > max) max = amp[nfreq * t + f];
	for (t = 0; t < ntime; ++t) for (f = 0; f < nfreq; ++f)
		amp[nfreq * t + f] = 10.0 * log10(amp[nfreq * t + f] / max);
	/* prepare point and line overlays */
	lines.setPrimitiveType(sf::LinesStrip);
	lines.resize(ntime);
	points.setPrimitiveType(sf::Quads);
	points.resize(ntime * 4);
	for (t = 0; t < ntime; ++t) {
		lines[t].color = conf.linesFG;
		points[4*t].texCoords = sf::Vector2f(0,0);
		points[4*t+1].texCoords = sf::Vector2f(64,0);
		points[4*t+2].texCoords = sf::Vector2f(64,64);
		points[4*t+3].texCoords = sf::Vector2f(0,64);
	}
	makeSpectrogram();
	makeThreshold();
	makeOverlay();
}

void Fft::makeSpectrogram() {
	int t, f;
................................................................................
	texSpec.loadFromImage(img);
	texSpec.setSmooth(true);
	spec = sf::Sprite(texSpec);
	spec.setScale(1.0,-1.0);
}

void Fft::makeOverlay() {

//   * Change function name to "recalculate"?
	int t, f, fmax, n;;
	double max, pt, pf;
	pathLength = timeLength = 0.0;
	for (t = 0, n = 0; t < ntime; ++t) {
		fmax = -1;
		max = std::numeric_limits<double>::lowest(); // TODO: is this negative?  Should it be?
		for (f = 0; f < nfreq; ++f) {
			if (erase[nfreq * t + f]) continue;
			if (amp[nfreq * t + f] < max) continue;
			max = amp[nfreq * t + (fmax=f)];
		}
		if (max < -1.0 * conf.threshold) continue;
		if (n) { /* increment lengths for fex calculation for all but first point */
			pathLength += hypot(freq[fmax]-pf,time[t]-pt);
			timeLength += time[t]-pt;
		}
		pf = freq[fmax]; pt = time[t];
		lines[n].position = sf::Vector2f(t + 0.5, - fmax - 0.5);
		points[4*n].position = sf::Vector2f(t, - fmax);
		points[4*n+1].position = sf::Vector2f(t + 1, - fmax);
		points[4*n+2].position = sf::Vector2f(t + 1, - fmax - 1);
		points[4*n+3].position = sf::Vector2f(t, - fmax - 1);






		++n;
	}
	for (; n < ntime; ++n) {
		/* set remaining lines to last point and points to off screen */
		lines[n].position = lines[n-1].position;
		points[4*n].position = sf::Vector2f(-2,0);
		points[4*n+1].position = sf::Vector2f(-2,0);
		points[4*n+2].position = sf::Vector2f(-2,0);
		points[4*n+3].position = sf::Vector2f(-2,0);
	}
}

Changes to src/fft.hpp.

11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
		unsigned short int *erase = NULL;
		sf::VertexArray points, lines;

	protected:
		sf::Sprite spec, thresh;
		sf::SoundBuffer song;
		int ntime, nfreq;


		void makeSpectrogram();
		void makeThreshold();
		void makeOverlay();
		sf::VertexArray const &getPoints() const { return points; };
		sf::VertexArray const &getLines() const { return lines; };

		void eraseShift();
		void erasePoint(int, int);
		void eraseUndo();

	public:
		Fft(int, const char **);
		~Fft();
};








>












|



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
		unsigned short int *erase = NULL;
		sf::VertexArray points, lines;

	protected:
		sf::Sprite spec, thresh;
		sf::SoundBuffer song;
		int ntime, nfreq;
		double pathLength, timeLength;

		void makeSpectrogram();
		void makeThreshold();
		void makeOverlay();
		sf::VertexArray const &getPoints() const { return points; };
		sf::VertexArray const &getLines() const { return lines; };

		void eraseShift();
		void erasePoint(int, int);
		void eraseUndo();

	public:
		Fft(int, char *const *);
		~Fft();
};

Changes to src/spectrogram.cpp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
...
148
149
150
151
152
153
154





155
156
157
158
159
160
161

#include "spectrogram.hpp"

Spectrogram::Spectrogram(int argc, const char **argv) : Fft(argc, argv) {
//	if (getenv("FEX_FULLSCREEN"))
		win.create(sf::VideoMode::getDesktopMode(), "Fex", sf::Style::Fullscreen);
//	else
//		win.create(sf::VideoMode(640,480), "Fex");
	aspect = (ntime * win.getSize().y * conf.hop) / (float) (nfreq * win.getSize().x * conf.winlen);
	view.reset(sf::FloatRect(0, -nfreq * (1 + aspect)/2.0, ntime, nfreq * aspect));
	win.setView(view);
	spec.setColor(conf.specFG);
	sf::RenderTexture render_ball;
	render_ball.setSmooth(true);
	render_ball.create(64,64);
	sf::CircleShape dot(32);
	dot.setFillColor(conf.pointBG);
	dot.setOutlineColor(conf.pointFB);
	dot.setOutlineThickness(-8);
	render_ball.draw(dot);
	ball = render_ball.getTexture();
	back.setSize(sf::Vector2f(ntime,-nfreq));
	back.setFillColor(conf.specBG);
}

................................................................................
	sf::Event ev;
	while (win.isOpen() && win.waitEvent(ev)) {
		ev_handler(ev);
		while (win.pollEvent(ev)) ev_handler(ev);
		draw_main();
		if (show.cursor) draw_cursor();
		win.display();
	char out[256];
	snprintf(out,255,"%s - (%0.3fs, %0.3fKHz) FE: %lf ",
		name,
		song.getDuration().asSeconds() * mouse.x / ntime,
		conf.lopass - (conf.hipass - conf.lopass) * mouse.y / nfreq,
		1234.5
	);
	win.setTitle(sf::String(out));
	}

	return 0;
}

void Spectrogram::ev_handler(sf::Event ev) {
	switch (ev.type) {
		case sf::Event::Closed: ev_close(ev); break;
		case sf::Event::KeyPressed: ev_keypress(ev); break;
................................................................................
			view.reset(sf::FloatRect(0, -nfreq * (1 + aspect)/2.0, ntime, nfreq * aspect));
			break;
	}
}

void Spectrogram::ev_wheel(sf::Event ev) {
	if (ev.mouseWheelScroll.wheel == 0) { /* vertical */





		view.zoom(1.0 - 0.0075 * ev.mouseWheelScroll.delta);
		win.setView(view);
	}
}

/*
void Spectrogram::draw_hud() {



|













|







 







|
|
|
|
|
|
|
|

>







 







>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

#include "spectrogram.hpp"

Spectrogram::Spectrogram(int argc, char *const *argv) : Fft(argc, argv) {
//	if (getenv("FEX_FULLSCREEN"))
		win.create(sf::VideoMode::getDesktopMode(), "Fex", sf::Style::Fullscreen);
//	else
//		win.create(sf::VideoMode(640,480), "Fex");
	aspect = (ntime * win.getSize().y * conf.hop) / (float) (nfreq * win.getSize().x * conf.winlen);
	view.reset(sf::FloatRect(0, -nfreq * (1 + aspect)/2.0, ntime, nfreq * aspect));
	win.setView(view);
	spec.setColor(conf.specFG);
	sf::RenderTexture render_ball;
	render_ball.setSmooth(true);
	render_ball.create(64,64);
	sf::CircleShape dot(32);
	dot.setFillColor(conf.pointBG);
	dot.setOutlineColor(conf.pointFG);
	dot.setOutlineThickness(-8);
	render_ball.draw(dot);
	ball = render_ball.getTexture();
	back.setSize(sf::Vector2f(ntime,-nfreq));
	back.setFillColor(conf.specBG);
}

................................................................................
	sf::Event ev;
	while (win.isOpen() && win.waitEvent(ev)) {
		ev_handler(ev);
		while (win.pollEvent(ev)) ev_handler(ev);
		draw_main();
		if (show.cursor) draw_cursor();
		win.display();
		char out[256];
		snprintf(out,255,"%s - (%0.3fs, %0.3fKHz) FE: %lf ",
			name,
			song.getDuration().asSeconds() * mouse.x / ntime,
			conf.lopass - (conf.hipass - conf.lopass) * mouse.y / nfreq,
			1234.5
		);
		win.setTitle(sf::String(out));
	}
	printf("%lf\t%lf\t%lf\n", pathLength, timeLength, pathLength / timeLength);
	return 0;
}

void Spectrogram::ev_handler(sf::Event ev) {
	switch (ev.type) {
		case sf::Event::Closed: ev_close(ev); break;
		case sf::Event::KeyPressed: ev_keypress(ev); break;
................................................................................
			view.reset(sf::FloatRect(0, -nfreq * (1 + aspect)/2.0, ntime, nfreq * aspect));
			break;
	}
}

void Spectrogram::ev_wheel(sf::Event ev) {
	if (ev.mouseWheelScroll.wheel == 0) { /* vertical */
		// TODO, make 0.0075 step size customizable?
		float vx = view.getSize().x / ntime, vy = view.getSize().y / nfreq;
		float dx = ev.mouseWheelScroll.delta;
		if (dx < 0 && vx > 1.20 && vy > 1.20) return;
		if (dx > 0 && vx < 0.01 && vy > 0.01) return;
		view.zoom(1.0 - 0.0075 * ev.mouseWheelScroll.delta);
		win.setView(view);
	}
}

/*
void Spectrogram::draw_hud() {

Changes to src/spectrogram.hpp.

26
27
28
29
30
31
32
33
34
35
36
		void ev_resize(sf::Event);
		void ev_button(sf::Event);
		void ev_wheel(sf::Event);
	protected:
		void listen(float=1.0);
	public:
		int main_loop();
		Spectrogram(int, const char **);
		~Spectrogram();
};








|



26
27
28
29
30
31
32
33
34
35
36
		void ev_resize(sf::Event);
		void ev_button(sf::Event);
		void ev_wheel(sf::Event);
	protected:
		void listen(float=1.0);
	public:
		int main_loop();
		Spectrogram(int, char *const *);
		~Spectrogram();
};