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 #include "headers.h"
25 
26 #include "it.h"
27 #include "song.h"
28 #include "page.h"
29 #include "video.h"
30 
31 #include "sdlmain.h"
32 
33 #include <math.h>
34 
35 #define SAMPLE_DATA_COLOR 13 /* Sample data */
36 #define SAMPLE_LOOP_COLOR 3 /* Sample loop marks */
37 #define SAMPLE_MARK_COLOR 6 /* Play mark color */
38 #define SAMPLE_BGMARK_COLOR 7 /* Play mark color after note fade / NNA */
39 
40 /* --------------------------------------------------------------------- */
41 /* sample drawing
42 there are only two changes between 8- and 16-bit samples:
43 - the type of 'data'
44 - the amount to divide (note though, this number is used twice!)
45 
46 output channels = number of oscis
47 input channels = number of channels in data
48 */
49 
_draw_sample_data_8(struct vgamem_overlay * r,signed char * data,unsigned long length,unsigned int inputchans,unsigned int outputchans)50 static void _draw_sample_data_8(struct vgamem_overlay *r,
51 	signed char *data, unsigned long length, unsigned int inputchans, unsigned int outputchans)
52 {
53 	unsigned long pos;
54 	unsigned int cc, co;
55 	int level, xs, ys, xe, ye, step;
56 	int nh, np;
57 	int chip;
58 
59 	nh = (r->height / outputchans);
60 	np = r->height - (nh / 2);
61 
62 	length /= inputchans;
63 	chip = (length < (unsigned int) r->width * 2);
64 
65 	for (cc = 0; cc < outputchans; cc++) {
66 		pos = 0;
67 		co = 0;
68 		step = MAX(1, (length / r->width) >> 8);
69 		level=0;
70 		do {
71 			level+=ceil(data[(pos * inputchans) + cc+co] * nh / (float)(SCHAR_MAX - SCHAR_MIN + 1));
72 		} while (co++ < inputchans-outputchans);
73 		xs = 0;
74 		ys = (np - 1) - level;
75 		ys = CLAMP(ys, 0, r->height - 1);
76 		do {
77 			pos += step;
78 			co = 0;
79 			level=0;
80 			do {
81 				level+=ceil(data[(pos * inputchans) + cc+co] * nh / (SCHAR_MAX - SCHAR_MIN + 1));
82 			} while (co++ < inputchans-outputchans);
83 			xe = pos * r->width / length;
84 			ye = (np - 1) - level;
85 			xe = CLAMP(xe, 0, r->width - 1);
86 			ye = CLAMP(ye, 0, r->height - 1);
87 			// 'ye' is more or less useless for small samples, but this is much cleaner
88 			// code than writing nearly the same loop four different times :P
89 			vgamem_ovl_drawline(r, xs, ys, xe, chip ? ys : ye, SAMPLE_DATA_COLOR);
90 			xs = xe;
91 			ys = ye;
92 		} while (pos < length);
93 		np -= nh;
94 	}
95 }
96 
_draw_sample_data_16(struct vgamem_overlay * r,signed short * data,unsigned long length,unsigned int inputchans,unsigned int outputchans)97 static void _draw_sample_data_16(struct vgamem_overlay *r,
98 	 signed short *data, unsigned long length, unsigned int inputchans, unsigned int outputchans)
99 {
100 	unsigned long pos;
101 	unsigned int cc, co;
102 	int level, xs, ys, xe, ye, step;
103 	int nh, np;
104 	int chip;
105 
106 	nh = (r->height / outputchans);
107 	np = r->height - (nh / 2);
108 
109 	length /= inputchans;
110 	chip = (length < (unsigned int) r->width * 2);
111 
112 	for (cc = 0; cc < outputchans; cc++) {
113 		pos = 0;
114 		co = 0;
115 		step = MAX(1, (length / r->width) >> 8);
116 		level=0;
117 		do {
118 			level += ceil(data[(pos * inputchans) + cc+co] * nh / (float)(SHRT_MAX - SHRT_MIN + 1));
119 		} while(co++ < inputchans-outputchans);
120 		xs = 0;
121 		ys = (np - 1) - level;
122 		ys = CLAMP(ys, 0, r->height - 1);
123 		do {
124 			pos += step;
125 			co = 0;
126 			level = 0;
127 			do {
128 				level = ceil(data[(pos * inputchans) + cc+co] * nh / (float)(SHRT_MAX - SHRT_MIN + 1));
129 			} while (co++ < inputchans-outputchans);
130 			xe = pos * r->width / length;
131 			ye = (np - 1) - level;
132 			xe = CLAMP(xe, 0, r->width - 1);
133 			ye = CLAMP(ye, 0, r->height - 1);
134 			vgamem_ovl_drawline(r, xs, ys, xe, chip ? ys : ye, SAMPLE_DATA_COLOR);
135 			xs = xe;
136 			ys = ye;
137 		} while (pos < length);
138 		np -= nh;
139 	}
140 }
141 
142 /* --------------------------------------------------------------------- */
143 /* these functions assume the screen is locked! */
144 
145 /* loop drawing */
_draw_sample_loop(struct vgamem_overlay * r,song_sample_t * sample)146 static void _draw_sample_loop(struct vgamem_overlay *r, song_sample_t * sample)
147 {
148 	int loopstart, loopend, y;
149 	int c = ((status.flags & CLASSIC_MODE) ? SAMPLE_DATA_COLOR : SAMPLE_LOOP_COLOR);
150 
151 	if (!(sample->flags & CHN_LOOP))
152 		return;
153 
154 	loopstart = sample->loop_start * (r->width - 1) / sample->length;
155 	loopend = sample->loop_end * (r->width - 1) / sample->length;
156 
157 	y = 0;
158 	do {
159 		vgamem_ovl_drawpixel(r, loopstart, y, 0); vgamem_ovl_drawpixel(r, loopend, y, 0); y++;
160 		vgamem_ovl_drawpixel(r, loopstart, y, c); vgamem_ovl_drawpixel(r, loopend, y, c); y++;
161 		vgamem_ovl_drawpixel(r, loopstart, y, c); vgamem_ovl_drawpixel(r, loopend, y, c); y++;
162 		vgamem_ovl_drawpixel(r, loopstart, y, 0); vgamem_ovl_drawpixel(r, loopend, y, 0); y++;
163 	} while (y < r->height);
164 }
165 
_draw_sample_susloop(struct vgamem_overlay * r,song_sample_t * sample)166 static void _draw_sample_susloop(struct vgamem_overlay *r, song_sample_t * sample)
167 {
168 	int loopstart, loopend, y;
169 	int c = ((status.flags & CLASSIC_MODE) ? SAMPLE_DATA_COLOR : SAMPLE_LOOP_COLOR);
170 
171 	if (!(sample->flags & CHN_SUSTAINLOOP))
172 		return;
173 
174 	loopstart = sample->sustain_start * (r->width - 1) / sample->length;
175 	loopend = sample->sustain_end * (r->width - 1) / sample->length;
176 
177 	y = 0;
178 	do {
179 		vgamem_ovl_drawpixel(r, loopstart, y, c); vgamem_ovl_drawpixel(r, loopend, y, c); y++;
180 		vgamem_ovl_drawpixel(r, loopstart, y, 0); vgamem_ovl_drawpixel(r, loopend, y, 0); y++;
181 		vgamem_ovl_drawpixel(r, loopstart, y, c); vgamem_ovl_drawpixel(r, loopend, y, c); y++;
182 		vgamem_ovl_drawpixel(r, loopstart, y, 0); vgamem_ovl_drawpixel(r, loopend, y, 0); y++;
183 	} while (y < r->height);
184 }
185 
186 /* this does the lines for playing samples */
_draw_sample_play_marks(struct vgamem_overlay * r,song_sample_t * sample)187 static void _draw_sample_play_marks(struct vgamem_overlay *r, song_sample_t * sample)
188 {
189 	int n, x, y;
190 	int c;
191 	song_voice_t *channel;
192 	unsigned int *channel_list;
193 
194 	if (song_get_mode() == MODE_STOPPED)
195 		return;
196 
197 	song_lock_audio();
198 
199 	n = song_get_mix_state(&channel_list);
200 	while (n--) {
201 		channel = song_get_mix_channel(channel_list[n]);
202 		if (channel->current_sample_data != sample->data)
203 			continue;
204 		if (!channel->final_volume) continue;
205 		c = (channel->flags & (CHN_KEYOFF | CHN_NOTEFADE)) ? SAMPLE_BGMARK_COLOR : SAMPLE_MARK_COLOR;
206 		x = channel->position * (r->width - 1) / sample->length;
207 		if (x >= r->width) {
208 			/* this does, in fact, happen :( */
209 			continue;
210 		}
211 		y = 0;
212 		do {
213 			/* unrolled 8 times */
214 			vgamem_ovl_drawpixel(r, x, y++, c);
215 			vgamem_ovl_drawpixel(r, x, y++, c);
216 			vgamem_ovl_drawpixel(r, x, y++, c);
217 			vgamem_ovl_drawpixel(r, x, y++, c);
218 			vgamem_ovl_drawpixel(r, x, y++, c);
219 			vgamem_ovl_drawpixel(r, x, y++, c);
220 			vgamem_ovl_drawpixel(r, x, y++, c);
221 			vgamem_ovl_drawpixel(r, x, y++, c);
222 		} while (y < r->height);
223 	}
224 
225 	song_unlock_audio();
226 }
227 
228 /* --------------------------------------------------------------------- */
229 /* meat! */
230 
draw_sample_data(struct vgamem_overlay * r,song_sample_t * sample)231 void draw_sample_data(struct vgamem_overlay *r, song_sample_t *sample)
232 {
233 	vgamem_ovl_clear(r, 0);
234 
235 	if (sample->flags & CHN_ADLIB) {
236 	    vgamem_ovl_clear(r, 2);
237 	    vgamem_ovl_apply(r);
238 		char buf1[32], buf2[32];
239 
240 		int y1 = r->y1, y2 = y1+3;
241 
242 		draw_box(59,y1, 77,y2, BOX_THICK | BOX_INNER | BOX_INSET); // data
243 		draw_box(54,y1, 58,y2, BOX_THIN | BOX_INNER | BOX_OUTSET); // button
244 		draw_text_len("Mod", 3, 55,y1+1, 0,2);
245 		draw_text_len("Car", 3, 55,y1+2, 0,2);
246 
247 		sprintf(buf1, "%02X %02X %02X %02X %02X %02X", // length:6*3-1=17
248 			sample->adlib_bytes[0],
249 			sample->adlib_bytes[2],
250 			sample->adlib_bytes[4],
251 			sample->adlib_bytes[6],
252 			sample->adlib_bytes[8],
253 			sample->adlib_bytes[10]);
254 		sprintf(buf2, "%02X %02X %02X %02X %02X",      // length: 5*3-1=14
255 			sample->adlib_bytes[1],
256 			sample->adlib_bytes[3],
257 			sample->adlib_bytes[5],
258 			sample->adlib_bytes[7],
259 			sample->adlib_bytes[9]);
260 		draw_text_len(buf1, 17, 60,y1+1, 2,0);
261 		draw_text_len(buf2, 17, 60,y1+2, 2,0);
262 		return;
263 	}
264 
265 	if (!sample->length || !sample->data) {
266 		vgamem_ovl_apply(r);
267 		return;
268 	}
269 
270 	/* do the actual drawing */
271 	int chans = sample->flags & CHN_STEREO ? 2 : 1;
272 	if (sample->flags & CHN_16BIT)
273 		_draw_sample_data_16(r, (signed short *) sample->data,
274 				sample->length * chans,
275 				chans, chans);
276 	else
277 		_draw_sample_data_8(r, sample->data,
278 				sample->length * chans,
279 				chans, chans);
280 
281 	if ((status.flags & CLASSIC_MODE) == 0)
282 		_draw_sample_play_marks(r, sample);
283 	_draw_sample_loop(r, sample);
284 	_draw_sample_susloop(r, sample);
285 	vgamem_ovl_apply(r);
286 }
287 
draw_sample_data_rect_16(struct vgamem_overlay * r,signed short * data,int length,unsigned int inputchans,unsigned int outputchans)288 void draw_sample_data_rect_16(struct vgamem_overlay *r, signed short *data,
289 	int length, unsigned int inputchans, unsigned int outputchans)
290 {
291 	vgamem_ovl_clear(r, 0);
292 	_draw_sample_data_16(r, data, length, inputchans, outputchans);
293 	vgamem_ovl_apply(r);
294 }
295 
draw_sample_data_rect_8(struct vgamem_overlay * r,signed char * data,int length,unsigned int inputchans,unsigned int outputchans)296 void draw_sample_data_rect_8(struct vgamem_overlay *r, signed char *data,
297 	int length, unsigned int inputchans, unsigned int outputchans)
298 {
299 	vgamem_ovl_clear(r, 0);
300 	_draw_sample_data_8(r, data, length, inputchans, outputchans);
301 	vgamem_ovl_apply(r);
302 }
303 
304