Fex

Check-in [57f7252666]

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

Overview
Comment:crop window implemented
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | origin/sfml
Files: files | file ages | folders
SHA3-256: 57f725266642d08f9518e741fb49f32163fa6b9436eb26da38bbafd74439e451
User & Date: jesse.mcclure@umassmed.edu 2016-02-15 19:42:29
Context
2016-02-15
23:13
initial eraser implementation check-in: c385cdab1c user: jesse.mcclure@umassmed.edu tags: trunk, origin/sfml
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
Changes

Changes to TODO.

1

2
3
4

5




6


- Add eraser function to spectrogram module
- Add crop option
- Add log transformation to frequency?

- Implement config file reading






>

<

>
|
>
>
>
>

1
2
3

4
5
6
7
8
9
10
11

- change function names to camelCase in spectrogram
- Add eraser function to spectrogram module

- Add log transformation to frequency?
- Add cursorFG to config.cpp and python code
x Implement config file reading
	* offloaded to pyfex
	* fex only uses command line parameters
	* this makes porting across platforms easier
		* at least for me: I have no idea where a Win user would keep config files.

Changes to src/config.hpp.

16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
			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();
};








>













16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
			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);
			sf::Color cursorFG   = sf::Color(0xFF,0x00,0x00,0xAA);
		} 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
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;
}




<


<


1
2
3
4

5
6

7
8

#include "spectrogram.hpp"

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

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

	return EXIT_SUCCESS;
}

Changes to src/fft.cpp.

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
108
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
	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;
	sf::Image img;
................................................................................
}

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);
................................................................................
	}
}

void Fft::makeThreshold() {
	int t, f;
	sf::Image img;
	img.create(ntime, nfreq);
	for (f = 0; f < nfreq; ++f) for (t = 0; t < ntime; ++t)
		img.setPixel(t, f, sf::Color(255, 255, 255, (amp[nfreq * t + f] > - conf.threshold ? 255 : 0)));
	texThresh.loadFromImage(img);
	texThresh.setSmooth(true);
	thresh = sf::Sprite(texThresh);
	thresh.setScale(1.0,-1.0);
}








|











>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|


|







 







|







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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
	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);
	}
	t1 = f1 = 0;
	t2 = ntime; f2 = nfreq;
	/* prepare sprites */
	makeSpectrogram();
	makeThreshold();
	makeOverlay();
}

void Fft::setCrop(sf::Vector2f a, sf::Vector2f b) {
	if (a.x == -1) { /* reset */
		t1 = f1 = 0;
		t2 = ntime; f2 = nfreq;
	}
	else {
		a.y *= -1;
		b.y *= -1;
		t1 = (a.x < b.x ? a.x : b.x);
		t2 = (a.x < b.x ? b.x : a.x);
		f1 = (a.y < b.y ? a.y : b.y);
		f2 = (a.y < b.y ? b.y : a.y);
	}
	makeThreshold();
	makeOverlay();
}

void Fft::makeSpectrogram() {
	int t, f;
	sf::Image img;
................................................................................
}

void Fft::makeOverlay() {
//   * Change function name to "recalculate"?
	int t, f, fmax, n;;
	double max, pt, pf;
	pathLength = timeLength = 0.0;
	for (t = t1, n = 0; t < t2; ++t) {
		fmax = -1;
		max = std::numeric_limits<double>::lowest(); // TODO: is this negative?  Should it be?
		for (f = f1; f < f2; ++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);
................................................................................
	}
}

void Fft::makeThreshold() {
	int t, f;
	sf::Image img;
	img.create(ntime, nfreq);
	for (f = f1; f < f2; ++f) for (t = t1; t < t2; ++t)
		img.setPixel(t, f, sf::Color(255, 255, 255, (amp[nfreq * t + f] > - conf.threshold ? 255 : 0)));
	texThresh.loadFromImage(img);
	texThresh.setSmooth(true);
	thresh = sf::Sprite(texThresh);
	thresh.setScale(1.0,-1.0);
}

Changes to src/fft.hpp.

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
#include <SFML/Audio.hpp>

class Fft : public Config {
	private:
		sf::Texture texSpec, texThresh;
		double *freq = NULL, *time = NULL, *amp = NULL;
		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();
};








>











>
>












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
#include <SFML/Audio.hpp>

class Fft : public Config {
	private:
		sf::Texture texSpec, texThresh;
		double *freq = NULL, *time = NULL, *amp = NULL;
		unsigned short int *erase = NULL;
		int t1, t2, f1, f2;
		sf::VertexArray points, lines;

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

		void makeSpectrogram();
		void makeThreshold();
		void makeOverlay();
		void setCrop(sf::Vector2f, sf::Vector2f);
		sf::FloatRect getCrop() { return sf::FloatRect(t1, f1, t2 - t1, f2 - f1); }
		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.

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
..
46
47
48
49
50
51
52

53
54
55
56
57
58
59
..
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
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
108
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

150
151
152
153
154

155

















156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
	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);


}

Spectrogram::~Spectrogram() { }

int Spectrogram::main_loop() {
	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
................................................................................
	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;

		case sf::Event::MouseMoved: ev_mousemove(ev); break;
		case sf::Event::MouseButtonPressed: ev_button(ev); break;
		case sf::Event::MouseWheelScrolled: ev_wheel(ev); break;
		case sf::Event::Resized: ev_resize(ev); break;
	}
}

................................................................................
	if (show.overlay) {
		win.draw(thresh);
		win.draw(getPoints(), &ball);
		win.draw(getLines());
	}
}

void Spectrogram::draw_cursor() {

	sf::RectangleShape lineV(sf::Vector2f(1, nfreq));
	lineV.setFillColor(sf::Color(255,0,0,120));
	lineV.setPosition(view.getViewport().left + mouse.x, -nfreq);
	win.draw(lineV);
	sf::RectangleShape lineH(sf::Vector2f(ntime, 1));
	lineH.setFillColor(sf::Color(255,0,0,120));
	lineH.setPosition(0,view.getViewport().top + mouse.y);
	win.draw(lineH);
}

void Spectrogram::listen(float speed) {
	sf::Sound snd(song);
	snd.play();
	snd.setPitch(speed);
	while(snd.getStatus() == 2) {
		sf::RectangleShape line(sf::Vector2f(10, nfreq));
		line.setFillColor(sf::Color(0,255,0,120));
		//line.setPosition((float) view.getSize().x * snd.getPlayingOffset().asSeconds() / song.getDuration().asSeconds(), -nfreq);
		line.setPosition(ntime * (snd.getPlayingOffset() / song.getDuration()), -nfreq);
		draw_main();
		win.draw(line);
	//	if (show.hud) draw_hud();
		win.display();
	}
}

void Spectrogram::ev_close(sf::Event ev) {
}

void Spectrogram::ev_keypress(sf::Event ev) {


	if (ev.key.control) {
		if (ev.key.code == sf::Keyboard::Q) win.close();
		else if (ev.key.code == sf::Keyboard::Right) { conf.floor -= 0.25; makeSpectrogram(); }
		else if (ev.key.code == sf::Keyboard::Left) { conf.floor += 0.25; makeSpectrogram(); }
		else if (ev.key.code == sf::Keyboard::Up) { conf.threshold -= 0.25;  /* redraw overlay */ }
		else if (ev.key.code == sf::Keyboard::Down) { conf.threshold += 0.25;  /* redraw overlay */ }
	}
	else if (ev.key.alt) {
		if (ev.key.code == sf::Keyboard::C) win.setMouseCursorVisible(!(show.cursor=!show.cursor));
		if (ev.key.code == sf::Keyboard::H) show.hud = !show.hud;
		if (ev.key.code == sf::Keyboard::O) show.overlay = !show.overlay;
		if (ev.key.code == sf::Keyboard::Num1) listen(1.0);
		if (ev.key.code == sf::Keyboard::Num2) listen(0.5);
		if (ev.key.code == sf::Keyboard::Num3) listen(0.333);
		if (ev.key.code == sf::Keyboard::Num4) listen(0.25);
		if (ev.key.code == sf::Keyboard::Num5) listen(0.2);
	}
}






void Spectrogram::ev_resize(sf::Event ev) {
	aspect = (ntime * win.getSize().y * conf.hop) / (float) (nfreq * win.getSize().x * conf.winlen);
}

void Spectrogram::ev_mousemove(sf::Event ev) {
	sf::Vector2f prev = mouse;
	mouse = win.mapPixelToCoords(sf::Vector2i(ev.mouseMove.x,ev.mouseMove.y));
	if (mode == MOVE_RESIZE) {

		if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) {
			view.setSize(view.getSize().x + prev.x - mouse.x, view.getSize().y + prev.y - mouse.y);
			view.move((prev.x - mouse.x)/2.0, (prev.y - mouse.y)/2.0);
		}
		if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
			view.move(prev.x - mouse.x, prev.y - mouse.y);
		}
		win.setView(view);
		mouse = win.mapPixelToCoords(sf::Vector2i(ev.mouseMove.x,ev.mouseMove.y));
	}
	if (mouse.x < 0) mouse.x = 0.0;
	else if (mouse.x > ntime) mouse.x = ntime;
	if (mouse.y > 0) mouse.y = 0.0;
	else if (mouse.y < - nfreq) mouse.y = - nfreq;
}











void Spectrogram::ev_button(sf::Event ev) {





























	switch (ev.mouseButton.button) {


		case sf::Mouse::Button::Middle:
			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() {
	float w = view.getSize().x, h = view.getSize().y;
	float x = view.getCenter().x - w / 2.0, y = view.getCenter().y - h / 2.0;
	sf::RectangleShape shape(sf::Vector2f(w, h * 0.036));
	shape.setPosition(x, y);
	shape.setFillColor(sf::Color(0, 0, 128,100));
	win.draw(shape);
	char out[256];
	sf::Text text;
	text.setFont(font); text.setCharacterSize(28); text.setColor(sf::Color(255,255,255,200)); text.setScale(w/1000,h/1000);
	snprintf(out,255," FE: %0.3f", 123.456789);
	text.setString(out);
	text.setPosition(x, y);
	win.draw(text);
	snprintf(out,255,"(%0.3fs, %0.3fKHz) ",
		song.getDuration().asSeconds() * mouse.x / ntime,
		conf.lopass - (conf.hipass - conf.lopass) * mouse.y / nfreq
	);
	text.setString(out);
	text.setPosition(x + w - text.getGlobalBounds().width, y);
	win.draw(text);
}
*/








>
>










|
>
>
>







 







>







 







|
>

|
|


|
|










<



<








>
>








<









>
>
>
>
>








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






>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>


>





>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|









<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
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
..
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
71
72
73
74
75
76
77
78
79
80
81
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
108
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

























	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);
	mouse.x = ntime / 2; mouse.y = - nfreq / 2;
	crop1 = sf::Vector2f(-1, -1);
}

Spectrogram::~Spectrogram() { }

int Spectrogram::main_loop() {
	sf::Event ev;
	while (win.isOpen() && win.waitEvent(ev)) {
		ev_handler(ev);
		while (win.pollEvent(ev)) ev_handler(ev);
		draw_main();
		if (show.cursor) {
			if (crop1.x > 0) draw_cursor(crop1.x, crop1.y);
			draw_cursor(mouse.x, mouse.y);
		}
		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
................................................................................
	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;
		case sf::Event::KeyReleased: ev_keyrelease(ev); break;
		case sf::Event::MouseMoved: ev_mousemove(ev); break;
		case sf::Event::MouseButtonPressed: ev_button(ev); break;
		case sf::Event::MouseWheelScrolled: ev_wheel(ev); break;
		case sf::Event::Resized: ev_resize(ev); break;
	}
}

................................................................................
	if (show.overlay) {
		win.draw(thresh);
		win.draw(getPoints(), &ball);
		win.draw(getLines());
	}
}

void Spectrogram::draw_cursor(float x, float y) {
	// TODO get config cursor color
	sf::RectangleShape lineV(sf::Vector2f(1, nfreq));
	lineV.setFillColor(conf.cursorFG);
	lineV.setPosition(view.getViewport().left + x, -nfreq);
	win.draw(lineV);
	sf::RectangleShape lineH(sf::Vector2f(ntime, 1));
	lineH.setFillColor(conf.cursorFG);
	lineH.setPosition(0,view.getViewport().top + y);
	win.draw(lineH);
}

void Spectrogram::listen(float speed) {
	sf::Sound snd(song);
	snd.play();
	snd.setPitch(speed);
	while(snd.getStatus() == 2) {
		sf::RectangleShape line(sf::Vector2f(10, nfreq));
		line.setFillColor(sf::Color(0,255,0,120));

		line.setPosition(ntime * (snd.getPlayingOffset() / song.getDuration()), -nfreq);
		draw_main();
		win.draw(line);

		win.display();
	}
}

void Spectrogram::ev_close(sf::Event ev) {
}

void Spectrogram::ev_keypress(sf::Event ev) {
	if (ev.key.code == sf::Keyboard::LShift) show.cursor = true;
	if (ev.key.code == sf::Keyboard::RShift) show.cursor = true;
	if (ev.key.control) {
		if (ev.key.code == sf::Keyboard::Q) win.close();
		else if (ev.key.code == sf::Keyboard::Right) { conf.floor -= 0.25; makeSpectrogram(); }
		else if (ev.key.code == sf::Keyboard::Left) { conf.floor += 0.25; makeSpectrogram(); }
		else if (ev.key.code == sf::Keyboard::Up) { conf.threshold -= 0.25;  /* redraw overlay */ }
		else if (ev.key.code == sf::Keyboard::Down) { conf.threshold += 0.25;  /* redraw overlay */ }
	}
	else if (ev.key.alt) {

		if (ev.key.code == sf::Keyboard::H) show.hud = !show.hud;
		if (ev.key.code == sf::Keyboard::O) show.overlay = !show.overlay;
		if (ev.key.code == sf::Keyboard::Num1) listen(1.0);
		if (ev.key.code == sf::Keyboard::Num2) listen(0.5);
		if (ev.key.code == sf::Keyboard::Num3) listen(0.333);
		if (ev.key.code == sf::Keyboard::Num4) listen(0.25);
		if (ev.key.code == sf::Keyboard::Num5) listen(0.2);
	}
}

void Spectrogram::ev_keyrelease(sf::Event ev) {
	if (ev.key.code == sf::Keyboard::LShift) show.cursor = false;
	if (ev.key.code == sf::Keyboard::RShift) show.cursor = false;
}

void Spectrogram::ev_resize(sf::Event ev) {
	aspect = (ntime * win.getSize().y * conf.hop) / (float) (nfreq * win.getSize().x * conf.winlen);
}

void Spectrogram::ev_mousemove(sf::Event ev) {
	sf::Vector2f prev = mouse;
	mouse = win.mapPixelToCoords(sf::Vector2i(ev.mouseMove.x,ev.mouseMove.y));


	if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) {
		view.setSize(view.getSize().x + prev.x - mouse.x, view.getSize().y + prev.y - mouse.y);
		view.move((prev.x - mouse.x)/2.0, (prev.y - mouse.y)/2.0);
	}
	if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
		view.move(prev.x - mouse.x, prev.y - mouse.y);
	}
	win.setView(view);
	mouse = win.mapPixelToCoords(sf::Vector2i(ev.mouseMove.x,ev.mouseMove.y));

	if (mouse.x < 0) mouse.x = 0.0;
	else if (mouse.x > ntime) mouse.x = ntime;
	if (mouse.y > 0) mouse.y = 0.0;
	else if (mouse.y < - nfreq) mouse.y = - nfreq;
}

void Spectrogram::checkModKeys() {
	mod_ctrl = mod_shift = mod_alt = false;
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) mod_ctrl = true;
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::RControl)) mod_ctrl = true;
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) mod_shift = true;
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::RShift)) mod_shift = true;
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt)) mod_alt = true;
	if (sf::Keyboard::isKeyPressed(sf::Keyboard::RAlt)) mod_alt = true;
}

void Spectrogram::ev_button(sf::Event ev) {
	checkModKeys();
	sf::FloatRect rect;
	/* Control + button combinations are for working with the threshold: */
	if (mod_ctrl) switch (ev.mouseButton.button) {
	}
	/* Shift + button combinations are for working with the crop area: */
	else if (mod_shift) switch (ev.mouseButton.button) {
		/* set a crop window: requires 2 presses */
		case sf::Mouse::Button::Left:
			if (crop1.x < 0) crop1 = mouse;
			else { setCrop(crop1, mouse); crop1.x = -1; }
			break;
		/* zoom to fit crop area to window */
		case sf::Mouse::Button::Middle:
			rect = getCrop();
			rect.top -= rect.height * (1 + aspect) / 2.0;
			rect.height *= aspect;
			view.reset(rect);
			win.setView(view);
			break;
		/* reset crop to full song */
		case sf::Mouse::Button::Right:
			crop1.x = -1;
			setCrop(crop1, mouse);
			break;
	}
	/* Alt + button combinations are for working with the eraser: */
	else if (mod_alt) switch (ev.mouseButton.button) {
	}
	else switch (ev.mouseButton.button) {
		/* NOTE: left and right are handled in ev_mousemove */
		/* zoom to fit song to window */
		case sf::Mouse::Button::Middle:
			view.reset(sf::FloatRect(0, -nfreq * (1 + aspect)/2.0, ntime, nfreq * aspect));
			win.setView(view);
			break;
	}
}

void Spectrogram::ev_wheel(sf::Event ev) {
	checkModKeys();
	bool vert = (ev.mouseWheelScroll.wheel == 0);
	/* Control + wheel combinations are for working with the threshold: */
	if (mod_ctrl && vert) {
		// TODO: threshold up/down
	}
	/* Shift + wheel combinations are for working with the crop area: */
	else if (mod_shift) {
		// TODO: anything here?  Probably not.
	}
	/* Alt + wheel combinations are for working with the eraser: */
	else if (mod_alt && vert) {
		// TODO: size up/down
	}
	else if (mod_alt) {
		// TODO: shape left/right
	}
	/* No modifier wheel movements are for zooming: */
	else if (vert) {
		// 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);
	}
}


























Changes to src/spectrogram.hpp.

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
#include "fft.hpp"
#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>

class Spectrogram : public Fft {
	private:
		enum Mode { MOVE_RESIZE };
		Mode mode = MOVE_RESIZE;
		sf::RenderWindow win;
		sf::View view;
		sf::Vector2f mouse;
		sf::RectangleShape back;
		sf::Texture ball;

		float aspect;

		void draw_main();
		void draw_cursor();
		void draw_hud();


		void ev_handler(sf::Event);
		void ev_close(sf::Event);
		void ev_keypress(sf::Event);

		void ev_mousemove(sf::Event);
		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();
};








<
<


|


>



|


>



>












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
#include "fft.hpp"
#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>

class Spectrogram : public Fft {
	private:


		sf::RenderWindow win;
		sf::View view;
		sf::Vector2f mouse, crop1;
		sf::RectangleShape back;
		sf::Texture ball;
		bool mod_ctrl, mod_shift, mod_alt;
		float aspect;

		void draw_main();
		void draw_cursor(float, float);
		void draw_hud();

		void checkModKeys();
		void ev_handler(sf::Event);
		void ev_close(sf::Event);
		void ev_keypress(sf::Event);
		void ev_keyrelease(sf::Event);
		void ev_mousemove(sf::Event);
		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();
};