Fex

Check-in [2fdfa46cc7]

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

Overview
Comment:added eraser toggle; change key bindings - Mod+Space "locks" a mode
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk | origin/sfml
Files: files | file ages | folders
SHA3-256:2fdfa46cc7039415a51bb7f9945ce032d185950f7589cba802c204c2f52b89a8
User & Date: jesse.mcclure@umassmed.edu 2016-02-17 20:54:27
Context
2016-02-17
20:54
added eraser toggle; change key bindings - Mod+Space "locks" a mode Leaf check-in: 2fdfa46cc7 user: jesse.mcclure@umassmed.edu tags: trunk, origin/sfml
19:07
log transform added; cursor colog configurable check-in: b6d5daddeb user: jesse.mcclure@umassmed.edu tags: trunk, origin/sfml
Changes

Changes to TODO.

1
2
3
4
5
6
7
8
9
10
11







12


- eraser control (toggle on / off)
	- bring back "modes"?
x Add eraser function to spectrogram module
	- needs testing
- Too many "magic numbers" for eraser sizes...
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.










<
<








>
>
>
>
>
>
>

>
1


2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18



x Add eraser function to spectrogram module
	- needs testing
- Too many "magic numbers" for eraser sizes...
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.
- Complete the python front end
	x Parse config (ini) file to pass CLI parameters to fex binary
	- Show file selection dialogs for chosing songs, and saving data
	- Make either UI agnostic, or seperate versions for GTK, QT, etc
		- Are there Win / Mac native python dialog APIs?
			- Or do these need to use GTK/QT or Tk/Wx/...
	- Need input from python coders (pull requests welcomed!)


Changes to src/config.hpp.

20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
			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();
};








>

<
|
|







20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
			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 overlay      = true;
			bool cursor       = false;

			bool eraser       = false;
		} toggle;
		char *fname = NULL, *name = NULL;

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

Changes to src/spectrogram.cpp.

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
..
69
70
71
72
73
74
75
76
77
78
79





80
81
82
83
84
85
86
87
88
89
90
91
92
...
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
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

248
249
250
251
252




253
254
255
256
257
258
259
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

int Spectrogram::mainLoop() {
	sf::Event ev;
	while (win.isOpen() && win.waitEvent(ev)) {
		evHandler(ev);
		while (win.pollEvent(ev)) evHandler(ev);
		drawMain();
		if (show.cursor) {
			if (crop1.x > 0) drawCursor(crop1.x, crop1.y);
			drawCursor(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
................................................................................
}

void Spectrogram::drawMain() {
	win.clear(conf.winBG);
	win.draw(back);
	win.draw(spec);
	thresh.setColor(conf.threshFG);
	if (show.overlay) {
		win.draw(thresh);
		win.draw(getPoints(), &ball);
		win.draw(getLines());





		win.draw(eraser);
	}
}

void Spectrogram::drawCursor(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);
................................................................................
	}
}

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

void Spectrogram::evKeyPress(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::evKeyRelease(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::evResize(sf::Event ev) {
	aspect = (ntime * win.getSize().y * conf.hop) / (float) (nfreq * win.getSize().x * conf.winlen);
}

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

	}
	else if (mod_shift) {
	}
	else if (mod_alt) {
		if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) erase();
	}
	else {
		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;
	eraser.setPosition(mouse);
}

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::erase() {
	sf::FloatRect rect = eraser.getGlobalBounds();
	sf::Transform t = eraser.getInverseTransform();
	sf::Vector2f point;
	int i, j;
................................................................................
				erasePoint(i, -j);
		}
	}
	makeOverlay();
}

void Spectrogram::evMouseButton(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:
................................................................................
			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) {
		case sf::Mouse::Button::Left: erase(); break;
	}
	else switch (ev.mouseButton.button) {
		/* NOTE: left and right are handled in evMouseMove */
		/* 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::evMouseWheel(sf::Event ev) {
	checkModKeys();
	bool vert = (ev.mouseWheelScroll.wheel == 0);
	float dx = ev.mouseWheelScroll.delta;
	/* 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




		sf::Vector2f scale = eraser.getScale();
		float r = eraser.getRotation();
		if (r < 30 || r > 330 || (r > 150 && r < 210) )
			scale.y += 0.008 * dx;
		else if ( (r > 60 && r < 120) || (r > 240 && r < 300) )
			scale.x += 0.008 * dx;
		else {
................................................................................
		}
		if (scale.x < 0.5) scale.x = 0.5;
		else if (scale.x > 5.0) scale.x = 5.0;
		if (scale.y < 0.5) scale.y = 0.5;
		else if (scale.y > 5.0) scale.y = 5.0;
		eraser.setScale(scale);
	}
	else if (mod_alt && !vert) {
		eraser.rotate(-dx);
	}
	/* 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;
		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 * dx);
		win.setView(view);
	}
}








<
<
<
<







 







|



>
>
>
>
>
|




<







 







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




|
|
>
>







|
|
|
<
>

|
<
<
|












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







 







<

<
|
<
<
<







 







<
|
|












<


<
<
<
<
<
<
>


<
<
<
>
>
>
>







 







<
<
<











34
35
36
37
38
39
40




41
42
43
44
45
46
47
..
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
...
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
...
188
189
190
191
192
193
194

195

196



197
198
199
200
201
202
203
...
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
237
238
239
240
241
242
243
244
245
...
248
249
250
251
252
253
254



255
256
257
258
259
260
261
262
263
264
265

int Spectrogram::mainLoop() {
	sf::Event ev;
	while (win.isOpen() && win.waitEvent(ev)) {
		evHandler(ev);
		while (win.pollEvent(ev)) evHandler(ev);
		drawMain();




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

void Spectrogram::drawMain() {
	win.clear(conf.winBG);
	win.draw(back);
	win.draw(spec);
	thresh.setColor(conf.threshFG);
	if (toggle.overlay) {
		win.draw(thresh);
		win.draw(getPoints(), &ball);
		win.draw(getLines());
		if (toggle.cursor) {
			if (crop1.x > 0) drawCursor(crop1.x, crop1.y);
			drawCursor(mouse.x, mouse.y);
		}
		if (toggle.eraser)
			win.draw(eraser);
	}
}

void Spectrogram::drawCursor(float x, float y) {

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

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

void Spectrogram::evKeyPress(sf::Event ev) {
	if (ev.key.code == sf::Keyboard::LShift) toggle.cursor = !toggle.cursor;
	if (ev.key.code == sf::Keyboard::RShift) toggle.cursor = !toggle.cursor;
	if (ev.key.code == sf::Keyboard::LAlt) toggle.eraser = !toggle.eraser;
	if (ev.key.code == sf::Keyboard::RAlt) toggle.eraser = !toggle.eraser;
	if (ev.key.control) switch (ev.key.code) {
		case sf::Keyboard::Space: toggle.overlay = !toggle.overlay; break;
		case sf::Keyboard::Right: conf.floor -= 0.25; makeSpectrogram(); break;
		case sf::Keyboard::Left: conf.floor += 0.25; makeSpectrogram(); break;



		case sf::Keyboard::Up: conf.threshold -= 0.25;  makeOverlay(); break;
		case sf::Keyboard::Down: conf.threshold += 0.25;  makeOverlay(); break;
		case sf::Keyboard::Q: win.close(); break;

		case sf::Keyboard::Num1: listen(1.0); break;
		case sf::Keyboard::Num2: listen(0.5); break;
		case sf::Keyboard::Num3: listen(0.333); break;
		case sf::Keyboard::Num4: listen(0.25); break;
		case sf::Keyboard::Num5: listen(0.2); break;
	}
	else if (ev.key.alt) switch (ev.key.code) {
		case sf::Keyboard::Space: toggle.eraser = !toggle.eraser; break;
	}
	if (ev.key.shift) switch (ev.key.code) {
		case sf::Keyboard::Space: toggle.cursor = !toggle.cursor; break;
	}
}

void Spectrogram::evKeyRelease(sf::Event ev) {
	if (ev.key.code == sf::Keyboard::LShift) toggle.cursor = !toggle.cursor;
	if (ev.key.code == sf::Keyboard::RShift) toggle.cursor = !toggle.cursor;
	if (ev.key.code == sf::Keyboard::LAlt) toggle.eraser = !toggle.eraser;
	if (ev.key.code == sf::Keyboard::RAlt) toggle.eraser = !toggle.eraser;
}

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

void Spectrogram::evMouseMove(sf::Event ev) {
	sf::Vector2f prev = mouse;
	mouse = win.mapPixelToCoords(sf::Vector2i(ev.mouseMove.x,ev.mouseMove.y));
	if (toggle.overlay && toggle.cursor) {

		//
	}
	else if (toggle.overlay && toggle.eraser) {


		if (sf::Mouse::isButtonPressed(sf::Mouse::Left) && toggle.eraser) erase();
	}
	else {
		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));
	float in_bounds = 1.0;
	if (mouse.x < 0) in_bounds = mouse.x = 0.0;
	else if (mouse.x > ntime) in_bounds = mouse.x = ntime;
	if (mouse.y > 0) in_bounds = mouse.y = 0.0;
	else if (mouse.y < - nfreq) in_bounds = mouse.y = - nfreq;
	if (in_bounds == 1.0) eraser.setPosition(mouse);










	else eraser.setPosition(sf::Vector2f(-ntime,nfreq));
}

void Spectrogram::erase() {
	sf::FloatRect rect = eraser.getGlobalBounds();
	sf::Transform t = eraser.getInverseTransform();
	sf::Vector2f point;
	int i, j;
................................................................................
				erasePoint(i, -j);
		}
	}
	makeOverlay();
}

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

	sf::FloatRect rect;

	if (toggle.overlay && toggle.cursor) 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:
................................................................................
			break;
		/* reset crop to full song */
		case sf::Mouse::Button::Right:
			crop1.x = -1;
			setCrop(crop1, mouse);
			break;
	}

	else if (toggle.overlay && toggle.eraser) switch (ev.mouseButton.button) {
		case sf::Mouse::Button::Left: if (toggle.eraser) erase(); break;
	}
	else switch (ev.mouseButton.button) {
		/* NOTE: left and right are handled in evMouseMove */
		/* 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::evMouseWheel(sf::Event ev) {

	bool vert = (ev.mouseWheelScroll.wheel == 0);
	float dx = ev.mouseWheelScroll.delta;






	if (toggle.overlay && toggle.cursor) {
		// TODO: anything here?  Probably not.
	}



	if (toggle.overlay && toggle.eraser) {
		/* rotate */
		if (!vert) { eraser.rotate(-dx); return; }
		/* or scale */
		sf::Vector2f scale = eraser.getScale();
		float r = eraser.getRotation();
		if (r < 30 || r > 330 || (r > 150 && r < 210) )
			scale.y += 0.008 * dx;
		else if ( (r > 60 && r < 120) || (r > 240 && r < 300) )
			scale.x += 0.008 * dx;
		else {
................................................................................
		}
		if (scale.x < 0.5) scale.x = 0.5;
		else if (scale.x > 5.0) scale.x = 5.0;
		if (scale.y < 0.5) scale.y = 0.5;
		else if (scale.y > 5.0) scale.y = 5.0;
		eraser.setScale(scale);
	}



	/* 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;
		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 * dx);
		win.setView(view);
	}
}