1 /*
2  * Schism Tracker - a cross-platform Impulse Tracker clone
3  * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4  * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5  * copyright (c) 2009 Storlek & Mrs. Brisby
6  * copyright (c) 2010-2012 Storlek
7  * URL: http://schismtracker.org/
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #define NEED_TIME
25 #include "headers.h"
26 
27 #include "it.h"
28 #include "page.h"
29 #include "midi.h"
30 
31 #include "song.h"
32 
33 /* --------------------------------------------------------------------- */
34 
35 static int top_midi_port = 0;
36 static int current_port = 0;
37 static struct widget widgets_midi[17];
38 static time_t last_midi_poll = 0;
39 
40 /* --------------------------------------------------------------------- */
41 
midi_output_config(void)42 static void midi_output_config(void)
43 {
44 	set_page(PAGE_MIDI_OUTPUT);
45 }
46 
47 
update_ip_ports(void)48 static void update_ip_ports(void)
49 {
50 	if (widgets_midi[12].d.thumbbar.value > 0 && (status.flags & NO_NETWORK)) {
51 		status_text_flash("Networking is disabled");
52 		widgets_midi[12].d.thumbbar.value = 0;
53 	} else {
54 		ip_midi_setports(widgets_midi[12].d.thumbbar.value);
55 	}
56 
57 	last_midi_poll = 0;
58 	status.flags |= NEED_UPDATE;
59 }
60 
update_midi_values(void)61 static void update_midi_values(void)
62 {
63 	midi_flags = 0
64 	|       (widgets_midi[1].d.toggle.state ? MIDI_TICK_QUANTIZE : 0)
65 	|       (widgets_midi[2].d.toggle.state ? MIDI_BASE_PROGRAM1 : 0)
66 	|       (widgets_midi[3].d.toggle.state ? MIDI_RECORD_NOTEOFF : 0)
67 	|       (widgets_midi[4].d.toggle.state ? MIDI_RECORD_VELOCITY : 0)
68 	|       (widgets_midi[5].d.toggle.state ? MIDI_RECORD_AFTERTOUCH : 0)
69 	|       (widgets_midi[6].d.toggle.state ? MIDI_CUT_NOTE_OFF : 0)
70 	|       (widgets_midi[9].d.toggle.state ? MIDI_PITCHBEND : 0)
71 	;
72 	if (widgets_midi[11].d.toggle.state)
73 		current_song->flags |= SONG_EMBEDMIDICFG;
74 	else
75 		current_song->flags &= ~SONG_EMBEDMIDICFG;
76 
77 	midi_amplification = widgets_midi[7].d.thumbbar.value;
78 	midi_c5note = widgets_midi[8].d.thumbbar.value;
79 	midi_pitch_depth = widgets_midi[10].d.thumbbar.value;
80 }
81 
get_midi_config(void)82 static void get_midi_config(void)
83 {
84 	widgets_midi[1].d.toggle.state = !!(midi_flags & MIDI_TICK_QUANTIZE);
85 	widgets_midi[2].d.toggle.state = !!(midi_flags & MIDI_BASE_PROGRAM1);
86 	widgets_midi[3].d.toggle.state = !!(midi_flags & MIDI_RECORD_NOTEOFF);
87 	widgets_midi[4].d.toggle.state = !!(midi_flags & MIDI_RECORD_VELOCITY);
88 	widgets_midi[5].d.toggle.state = !!(midi_flags & MIDI_RECORD_AFTERTOUCH);
89 	widgets_midi[6].d.toggle.state = !!(midi_flags & MIDI_CUT_NOTE_OFF);
90 	widgets_midi[9].d.toggle.state = !!(midi_flags & MIDI_PITCHBEND);
91 	widgets_midi[11].d.toggle.state = !!(current_song->flags & SONG_EMBEDMIDICFG);
92 
93 	widgets_midi[7].d.thumbbar.value = midi_amplification;
94 	widgets_midi[8].d.thumbbar.value = midi_c5note;
95 	widgets_midi[10].d.thumbbar.value = midi_pitch_depth;
96 	widgets_midi[12].d.thumbbar.value = ip_midi_getports();
97 }
98 
toggle_port(void)99 static void toggle_port(void)
100 {
101 	struct midi_port *p;
102 	p = midi_engine_port(current_port, NULL);
103 	if (p) {
104 		status.flags |= NEED_UPDATE;
105 
106 		if (p->disable) if (!p->disable(p)) return;
107 		switch (p->io) {
108 		case 0:
109 			if (p->iocap & MIDI_INPUT) p->io = MIDI_INPUT;
110 			else if (p->iocap & MIDI_OUTPUT) p->io = MIDI_OUTPUT;
111 			break;
112 		case MIDI_INPUT:
113 			if (p->iocap & MIDI_OUTPUT) p->io = MIDI_OUTPUT;
114 			else p->io = 0;
115 			break;
116 		case MIDI_OUTPUT:
117 			if (p->iocap & MIDI_INPUT) p->io |= MIDI_INPUT;
118 			else p->io = 0;
119 			break;
120 		case MIDI_INPUT|MIDI_OUTPUT:
121 			p->io = 0;
122 			break;
123 		};
124 
125 		if (p->enable) {
126 			if (!p->enable(p)) {
127 				p->io = 0;
128 				return;
129 			}
130 		}
131 	}
132 }
133 
midi_page_handle_key(struct key_event * k)134 static int midi_page_handle_key(struct key_event * k)
135 {
136 	int new_port = current_port;
137 	int pos;
138 
139 	if (k->mouse == MOUSE_SCROLL_UP) {
140 		new_port -= MOUSE_SCROLL_LINES;
141 	} else if (k->mouse == MOUSE_SCROLL_DOWN) {
142 		new_port += MOUSE_SCROLL_LINES;
143 	} else if (k->mouse) {
144 		if (k->x >= 3 && k->x <= 11 && k->y >= 15 && k->y <= 27) {
145 			if (k->mouse == MOUSE_DBLCLICK) {
146 				if (k->state == KEY_PRESS)
147 					return 0;
148 				toggle_port();
149 				return 1;
150 			}
151 			new_port = top_midi_port + (k->y - 15);
152 		} else {
153 			return 0;
154 		}
155 	}
156 
157 	switch (k->sym) {
158 	case SDLK_SPACE:
159 		if (k->state == KEY_PRESS)
160 			return 1;
161 		toggle_port();
162 		return 1;
163 	case SDLK_PAGEUP:
164 		new_port -= 13;
165 		if (new_port < 0) new_port = 0;
166 		break;
167 	case SDLK_PAGEDOWN:
168 		new_port += 13;
169 		if (new_port >= midi_engine_port_count()) {
170 			new_port = midi_engine_port_count() - 1;
171 		}
172 		break;
173 	case SDLK_HOME:
174 		new_port = 0;
175 		break;
176 	case SDLK_END:
177 		new_port = midi_engine_port_count() - 1;
178 		break;
179 	case SDLK_UP:
180 		new_port--;
181 		break;
182 	case SDLK_DOWN:
183 		new_port++;
184 		break;
185 	case SDLK_TAB:
186 		if (k->state == KEY_RELEASE)
187 			return 1;
188 		change_focus_to(1);
189 		status.flags |= NEED_UPDATE;
190 		return 1;
191 	default:
192 		if (!k->mouse) return 0;
193 		break;
194 	};
195 	if (k->state == KEY_RELEASE)
196 		return 0;
197 
198 	if (new_port != current_port) {
199 		if (new_port < 0 || new_port >= midi_engine_port_count()) {
200 			new_port = current_port;
201 			if (k->sym == SDLK_DOWN) {
202 				change_focus_to(1);
203 			}
204 		}
205 
206 		current_port = new_port;
207 		if (current_port < top_midi_port)
208 			top_midi_port = current_port;
209 
210 		pos = current_port - top_midi_port;
211 		if (pos > 12) top_midi_port = current_port - 12;
212 		if (top_midi_port < 0) top_midi_port = 0;
213 
214 		status.flags |= NEED_UPDATE;
215 	}
216 
217 	return 1;
218 }
219 
midi_page_redraw(void)220 static void midi_page_redraw(void)
221 {
222 	draw_text(       "Tick quantize", 6, 30, 0, 2);
223 	draw_text(      "Base Program 1", 5, 31, 0, 2);
224 	draw_text(     "Record Note-Off", 4, 32, 0, 2);
225 	draw_text(     "Record Velocity", 4, 33, 0, 2);
226 	draw_text(   "Record Aftertouch", 2, 34, 0, 2);
227 	draw_text(        "Cut note off", 7, 35, 0, 2);
228 
229 	draw_fill_chars(23, 30, 24, 35, 0);
230 	draw_box(19,29,25,36, BOX_THIN|BOX_INNER|BOX_INSET);
231 
232 	draw_box(52,29,73,32, BOX_THIN|BOX_INNER|BOX_INSET);
233 
234 	draw_fill_chars(56, 34, 72, 34, 0);
235 	draw_box(52,33,73,36, BOX_THIN|BOX_INNER|BOX_INSET);
236 
237 	draw_fill_chars(56, 38, 72, 38, 0);
238 	draw_box(52,37,73,39, BOX_THIN|BOX_INNER|BOX_INSET);
239 
240 	draw_text(    "Amplification", 39, 30, 0, 2);
241 	draw_text(   "C-5 Note-value", 38, 31, 0, 2);
242 	draw_text("Output MIDI pitch", 35, 34, 0, 2);
243 	draw_text("Pitch wheel depth", 35, 35, 0, 2);
244 	draw_text(  "Embed MIDI data", 37, 38, 0, 2);
245 
246 	draw_text(    "IP MIDI ports", 39, 41, 0, 2);
247 	draw_box(52,40,73,42, BOX_THIN|BOX_INNER|BOX_INSET);
248 }
249 
midi_page_draw_portlist(void)250 static void midi_page_draw_portlist(void)
251 {
252 	struct midi_port *p;
253 	const char *name, *state;
254 	char buffer[64];
255 	int i, n, ct, fg, bg;
256 	unsigned long j;
257 	time_t now;
258 
259 	draw_fill_chars(3, 15, 76, 28, 0);
260 	draw_text("Midi ports:", 2, 13, 0, 2);
261 	draw_box(2,14,77,28, BOX_THIN|BOX_INNER|BOX_INSET);
262 
263 	time(&now);
264 	if ((now - last_midi_poll) > 10) {
265 		last_midi_poll = now;
266 		midi_engine_poll_ports();
267 	}
268 
269 	ct = midi_engine_port_count();
270 	for (i = 0; i < 13; i++) {
271 		draw_char(168, 12, i + 15, 2, 0);
272 
273 		if (top_midi_port + i >= ct)
274 			continue; /* err */
275 
276 		p = midi_engine_port(top_midi_port + i, &name);
277 		if (current_port == top_midi_port + i
278 		    && ACTIVE_WIDGET.type == WIDGET_OTHER) {
279 			fg = 0;
280 			bg = 3;
281 		} else {
282 			fg = 5;
283 			bg = 0;
284 		}
285 		draw_text_len(name, 64, 13, 15+i, 5, 0);
286 
287 		/* portability: should use difftime */
288 		if (status.flags & MIDI_EVENT_CHANGED
289 		    && (time(NULL) - status.last_midi_time) < 3
290 		    && ((!status.last_midi_port && p->io & MIDI_OUTPUT)
291 		    || p == (struct midi_port *) status.last_midi_port)) {
292 			for (j = n = 0; j < 21 && j < status.last_midi_len; j++) { /* 21 is approx 64/3 */
293 				sprintf(buffer + n, "%02X ", status.last_midi_event[j]);
294 				n += 3;
295 			}
296 			draw_text(buffer, 77 - strlen(buffer), 15+i,
297 				status.last_midi_port ? 4 : 10, 0);
298 		}
299 
300 		switch (p->io) {
301 			case 0:                         state = "Disabled "; break;
302 			case MIDI_INPUT:                state = "   Input "; break;
303 			case MIDI_OUTPUT:               state = "  Output "; break;
304 			case MIDI_INPUT | MIDI_OUTPUT:  state = "  Duplex "; break;
305 			default:                        state = " Enabled?"; break;
306 		}
307 		draw_text(state, 3, 15 + i, fg, bg);
308 	}
309 }
310 
311 /* --------------------------------------------------------------------- */
312 
midi_load_page(struct page * page)313 void midi_load_page(struct page *page)
314 {
315 	page->title = "Midi Screen (Shift-F1)";
316 	page->draw_const = midi_page_redraw;
317 	page->song_changed_cb = NULL;
318 	page->predraw_hook = NULL;
319 	page->playback_update = NULL;
320 	page->handle_key = NULL;
321 	page->set_page = get_midi_config;
322 	page->total_widgets = 15;
323 	page->widgets = widgets_midi;
324 	page->help_index = HELP_GLOBAL;
325 
326 	create_other(widgets_midi + 0, 0, midi_page_handle_key, midi_page_draw_portlist);
327 	widgets_midi[0].x = 2;
328 	widgets_midi[0].y = 14;
329 	widgets_midi[0].width = 75;
330 	widgets_midi[0].height = 15;
331 
332 	create_toggle(widgets_midi + 1, 20, 30, 0, 2, 7, 7, 7, update_midi_values);
333 	create_toggle(widgets_midi + 2, 20, 31, 1, 3, 8, 8, 8, update_midi_values);
334 	create_toggle(widgets_midi + 3, 20, 32, 2, 4, 8, 8, 8, update_midi_values);
335 	create_toggle(widgets_midi + 4, 20, 33, 3, 5, 9, 9, 9, update_midi_values);
336 	create_toggle(widgets_midi + 5, 20, 34, 4, 6, 9, 9, 9, update_midi_values);
337 	create_toggle(widgets_midi + 6, 20, 35, 5, 13, 10, 10, 10, update_midi_values);
338 	create_thumbbar(widgets_midi + 7, 53, 30, 20, 0, 8, 1, update_midi_values, 0, 200);
339 	create_thumbbar(widgets_midi + 8, 53, 31, 20, 7, 9, 2, update_midi_values, 0, 127);
340 	create_toggle(widgets_midi + 9, 53, 34, 8, 10, 5, 5, 5, update_midi_values);
341 	create_thumbbar(widgets_midi + 10, 53, 35, 20, 9, 11, 6, update_midi_values, 0, 48);
342 	create_toggle(widgets_midi + 11, 53, 38, 10, 12, 13, 13, 13, update_midi_values);
343 	create_thumbbar(widgets_midi + 12, 53, 41, 20, 11, 12, 13, update_ip_ports, 0, 128);
344 	create_button(widgets_midi + 13, 2, 41, 27, 6, 14, 12, 12, 12,
345 		midi_output_config, "MIDI Output Configuration", 2);
346 	create_button(widgets_midi + 14, 2, 44, 27, 13, 14, 12, 12, 12,
347 		cfg_midipage_save, "Save Output Configuration", 2);
348 }
349 
350