Mercurial > hg > dmlib
comparison tools/ppl.c @ 652:d9888292f971
Rename again.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 16 Apr 2013 06:04:22 +0300 |
parents | utils/ppl.c@e2ac08228a0f |
children | 3d813c81f33c |
comparison
equal
deleted
inserted
replaced
651:e2ac08228a0f | 652:d9888292f971 |
---|---|
1 /* | |
2 * Cyrbe Pasci Player - A simple SDL-based UI for XM module playing | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
4 * (C) Copyright 2012 Tecnic Software productions (TNSP) | |
5 * | |
6 * Please read file 'COPYING' for information on license and distribution. | |
7 */ | |
8 #include <SDL.h> | |
9 #include "dmlib.h" | |
10 | |
11 #include "jss.h" | |
12 #include "jssmod.h" | |
13 #include "jssmix.h" | |
14 #include "jssplr.h" | |
15 | |
16 #include "dmargs.h" | |
17 #include "dmimage.h" | |
18 #include "dmtext.h" | |
19 | |
20 #include "setupfont.h" | |
21 | |
22 | |
23 struct | |
24 { | |
25 BOOL exitFlag; | |
26 SDL_Surface *screen; | |
27 SDL_Event event; | |
28 int optScrWidth, optScrHeight, optVFlags, optScrDepth; | |
29 | |
30 int actChannel; | |
31 BOOL pauseFlag; | |
32 | |
33 JSSModule *mod; | |
34 JSSMixer *dev; | |
35 JSSPlayer *plr; | |
36 SDL_AudioSpec afmt; | |
37 } engine; | |
38 | |
39 struct | |
40 { | |
41 Uint32 boxBg, inboxBg, box1, box2, viewDiv, activeRow, activeChannel; | |
42 } col; | |
43 | |
44 | |
45 DMBitmapFont *font = NULL; | |
46 | |
47 char *optFilename = NULL; | |
48 int optOutFormat = JSS_AUDIO_S16, | |
49 optOutChannels = 2, | |
50 optOutFreq = 48000, | |
51 optMuteOChannels = -1, | |
52 optStartOrder = 0; | |
53 BOOL optUsePlayTime = FALSE; | |
54 size_t optPlayTime; | |
55 | |
56 | |
57 DMOptArg optList[] = | |
58 { | |
59 { 0, '?', "help", "Show this help", OPT_NONE }, | |
60 { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, | |
61 { 2, 0, "fs", "Fullscreen", OPT_NONE }, | |
62 { 3, 'w', "window", "Initial window size/resolution -w 640x480", OPT_ARGREQ }, | |
63 | |
64 { 4, '1', "16bit", "16-bit output", OPT_NONE }, | |
65 { 5, '8', "8bit", "8-bit output", OPT_NONE }, | |
66 { 6, 'm', "mono", "Mono output", OPT_NONE }, | |
67 { 7, 's', "stereo", "Stereo output", OPT_NONE }, | |
68 { 8, 'f', "freq", "Output frequency", OPT_ARGREQ }, | |
69 | |
70 { 9, 'M', "mute", "Mute other channels than #", OPT_ARGREQ }, | |
71 { 10, 'o', "order", "Start from order #", OPT_ARGREQ }, | |
72 { 11, 't', "time", "Play for # seconds", OPT_ARGREQ }, | |
73 }; | |
74 | |
75 const int optListN = sizeof(optList) / sizeof(optList[0]); | |
76 | |
77 | |
78 void argShowHelp() | |
79 { | |
80 dmPrintBanner(stdout, dmProgName, "[options] <module>"); | |
81 dmArgsPrintHelp(stdout, optList, optListN); | |
82 } | |
83 | |
84 | |
85 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
86 { | |
87 switch (optN) { | |
88 case 0: | |
89 argShowHelp(); | |
90 exit(0); | |
91 break; | |
92 | |
93 case 1: | |
94 dmVerbosity++; | |
95 break; | |
96 | |
97 case 2: | |
98 engine.optVFlags |= SDL_FULLSCREEN; | |
99 break; | |
100 | |
101 case 3: | |
102 { | |
103 int w, h; | |
104 if (sscanf(optArg, "%dx%d", &w, &h) == 2) | |
105 { | |
106 if (w < 320 || h < 200 || w > 3200 || h > 3200) | |
107 { | |
108 dmError("Invalid width or height: %d x %d\n", w, h); | |
109 return FALSE; | |
110 } | |
111 engine.optScrWidth = w; | |
112 engine.optScrHeight = h; | |
113 } | |
114 else | |
115 { | |
116 dmError("Invalid size argument '%s'.\n", optArg); | |
117 return FALSE; | |
118 } | |
119 } | |
120 break; | |
121 | |
122 case 4: | |
123 optOutFormat = JSS_AUDIO_S16; | |
124 break; | |
125 | |
126 case 5: | |
127 optOutFormat = JSS_AUDIO_U8; | |
128 break; | |
129 | |
130 case 6: | |
131 optOutChannels = JSS_AUDIO_MONO; | |
132 break; | |
133 | |
134 case 7: | |
135 optOutChannels = JSS_AUDIO_STEREO; | |
136 break; | |
137 | |
138 case 8: | |
139 optOutFreq = atoi(optArg); | |
140 break; | |
141 | |
142 case 9: | |
143 optMuteOChannels = atoi(optArg); | |
144 break; | |
145 | |
146 case 10: | |
147 optStartOrder = atoi(optArg); | |
148 break; | |
149 | |
150 case 11: | |
151 optPlayTime = atoi(optArg); | |
152 optUsePlayTime = TRUE; | |
153 break; | |
154 | |
155 default: | |
156 dmError("Unknown option '%s'.\n", currArg); | |
157 return FALSE; | |
158 } | |
159 | |
160 return TRUE; | |
161 } | |
162 | |
163 | |
164 BOOL argHandleFile(char *currArg) | |
165 { | |
166 if (!optFilename) | |
167 optFilename = currArg; | |
168 else | |
169 { | |
170 dmError("Too many filename arguments '%s'\n", currArg); | |
171 return FALSE; | |
172 } | |
173 | |
174 return TRUE; | |
175 } | |
176 | |
177 | |
178 void dmDrawBMTextConstQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt) | |
179 { | |
180 const char *ptr = fmt; | |
181 DMUnscaledBlitFunc blit = NULL; | |
182 | |
183 while (*ptr) | |
184 { | |
185 int ch = *ptr++; | |
186 SDL_Surface *glyph; | |
187 | |
188 if (ch == '_') | |
189 { | |
190 xc += 4; | |
191 continue; | |
192 } | |
193 | |
194 if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL) | |
195 { | |
196 if (blit == NULL) | |
197 blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode); | |
198 | |
199 blit(glyph, xc, yc, screen); | |
200 xc += font->width; | |
201 } | |
202 else | |
203 xc += font->width; | |
204 } | |
205 } | |
206 | |
207 | |
208 void dmDrawBMTextVAQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, va_list ap) | |
209 { | |
210 char tmp[512]; | |
211 vsnprintf(tmp, sizeof(tmp), fmt, ap); | |
212 dmDrawBMTextConstQ(screen, font, mode, xc, yc, tmp); | |
213 } | |
214 | |
215 | |
216 void dmDrawBMTextQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, ...) | |
217 { | |
218 va_list ap; | |
219 | |
220 va_start(ap, fmt); | |
221 dmDrawBMTextVAQ(screen, font, mode, xc, yc, fmt, ap); | |
222 va_end(ap); | |
223 } | |
224 | |
225 | |
226 Uint32 dmCol(float r, float g, float b) | |
227 { | |
228 return dmMapRGB(engine.screen, 255.0f * r, 255.0f * g, 255.0f * b); | |
229 } | |
230 | |
231 | |
232 BOOL dmInitializeVideo() | |
233 { | |
234 SDL_FreeSurface(engine.screen); | |
235 | |
236 engine.screen = SDL_SetVideoMode( | |
237 engine.optScrWidth, engine.optScrHeight, engine.optScrDepth, | |
238 engine.optVFlags | SDL_RESIZABLE | SDL_SWSURFACE | SDL_HWPALETTE); | |
239 | |
240 if (engine.screen == NULL) | |
241 { | |
242 dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); | |
243 return FALSE; | |
244 } | |
245 | |
246 col.inboxBg = dmCol(0.6, 0.5, 0.2); | |
247 col.boxBg = dmCol(0.7, 0.6, 0.3); | |
248 col.box1 = dmCol(1.0, 0.9, 0.6); | |
249 col.box2 = dmCol(0.3, 0.3, 0.15); | |
250 col.viewDiv = dmCol(0,0,0); | |
251 col.activeRow = dmCol(0.5,0.4,0.1); | |
252 col.activeChannel = dmCol(0.6, 0.8, 0.2); | |
253 | |
254 return TRUE; | |
255 } | |
256 | |
257 | |
258 void dmDisplayChn(SDL_Surface *screen, int x0, int y0, int x1, int y1, int nchannel, JSSChannel *chn) | |
259 { | |
260 int yh = y1 - y0 - 2; | |
261 if (yh < 10 || chn == NULL) | |
262 return; | |
263 | |
264 int xc, ym = y0 + (y1 - y0) / 2, vol = FP_GETH(chn->chVolume); | |
265 int pitch = screen->pitch / sizeof(Uint32); | |
266 int len = FP_GETH(chn->chSize); | |
267 DMFixedPoint offs = chn->chPos; | |
268 Uint32 coln = dmCol(0.0, 0.8, 0.0), colx = dmCol(1.0, 0, 0); | |
269 Uint32 *pix = screen->pixels; | |
270 Sint16 *data = chn->chData; | |
271 | |
272 | |
273 dmFillBox3D(screen, x0, y0, x1, y1, | |
274 (chn->chMute ? dmCol(0.3,0.1,0.1) : dmCol(0,0,0)), | |
275 nchannel == engine.actChannel ? colx : col.box2, | |
276 nchannel == engine.actChannel ? colx : col.box1); | |
277 | |
278 if (chn->chData == NULL || !chn->chPlaying) | |
279 return; | |
280 | |
281 if (chn->chDirection) | |
282 { | |
283 for (xc = x0 + 1; xc < x1 - 1; xc++) | |
284 { | |
285 if (FP_GETH(offs) >= len) | |
286 break; | |
287 Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255); | |
288 pix[xc + val * pitch] = coln; | |
289 FP_ADD(offs, chn->chDeltaO); | |
290 } | |
291 } | |
292 else | |
293 { | |
294 for (xc = x0 + 1; xc < x1 - 1; xc++) | |
295 { | |
296 if (FP_GETH(offs) < 0) | |
297 break; | |
298 Sint16 val = ym + (data[FP_GETH(offs)] * yh * vol) / (65535 * 255); | |
299 pix[xc + val * pitch] = coln; | |
300 FP_SUB(offs, chn->chDeltaO); | |
301 } | |
302 } | |
303 } | |
304 | |
305 | |
306 void dmDisplayChannels(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSMixer *dev) | |
307 { | |
308 int nchannel, qx, qy, | |
309 qwidth = x1 - x0, | |
310 qheight = y1 - y0, | |
311 nwidth = jsetNChannels, | |
312 nheight = 1; | |
313 | |
314 if (qheight < 40) | |
315 return; | |
316 | |
317 while (qwidth / nwidth <= 60 && qheight / nheight >= 40) | |
318 { | |
319 nheight++; | |
320 nwidth /= nheight; | |
321 } | |
322 | |
323 // fprintf(stderr, "%d x %d\n", nwidth, nheight); | |
324 | |
325 if (qheight / nheight <= 40) | |
326 { | |
327 nwidth = qwidth / 60; | |
328 nheight = qheight / 40; | |
329 } | |
330 | |
331 qwidth /= nwidth; | |
332 qheight /= nheight; | |
333 | |
334 for (nchannel = qy = 0; qy < nheight && nchannel < jsetNChannels; qy++) | |
335 { | |
336 for (qx = 0; qx < nwidth && nchannel < jsetNChannels; qx++) | |
337 { | |
338 int xc = x0 + qx * qwidth, | |
339 yc = y0 + qy * qheight; | |
340 | |
341 dmDisplayChn(screen, xc + 1, yc + 1, | |
342 xc + qwidth - 1, yc + qheight - 1, | |
343 nchannel, &dev->channels[nchannel]); | |
344 | |
345 nchannel++; | |
346 } | |
347 } | |
348 } | |
349 | |
350 | |
351 static const char patNoteTable[12][3] = | |
352 { | |
353 "C-", "C#", "D-", | |
354 "D#", "E-", "F-", | |
355 "F#", "G-", "G#", | |
356 "A-", "A#", "B-" | |
357 }; | |
358 | |
359 | |
360 #define jmpNMODEffectTable (36) | |
361 static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
362 | |
363 static const char jmpHexTab[16] = "0123456789ABCDEF"; | |
364 | |
365 static inline char dmHexVal(int v) | |
366 { | |
367 return jmpHexTab[v & 15]; | |
368 } | |
369 | |
370 void dmPrintNote(SDL_Surface *screen, int xc, int yc, JSSNote *n) | |
371 { | |
372 char text[32]; | |
373 char *ptr = text; | |
374 | |
375 switch (n->note) | |
376 { | |
377 case jsetNotSet: | |
378 strcpy(ptr, "..._"); | |
379 break; | |
380 case jsetNoteOff: | |
381 strcpy(ptr, "===_"); | |
382 break; | |
383 default: | |
384 sprintf(ptr, "%s%i_", | |
385 patNoteTable[n->note % 12], | |
386 n->note / 12); | |
387 break; | |
388 } | |
389 | |
390 ptr += 4; | |
391 | |
392 if (n->instrument != jsetNotSet) | |
393 { | |
394 int v = n->instrument + 1; | |
395 *ptr++ = dmHexVal(v >> 4); | |
396 *ptr++ = dmHexVal(v); | |
397 } | |
398 else | |
399 { | |
400 *ptr++ = '.'; | |
401 *ptr++ = '.'; | |
402 } | |
403 *ptr++ = '_'; | |
404 | |
405 if (n->volume == jsetNotSet) | |
406 { | |
407 *ptr++ = '.'; | |
408 *ptr++ = '.'; | |
409 } | |
410 else | |
411 if (n->volume >= 0x00 && n->volume <= 0x40) | |
412 { | |
413 *ptr++ = dmHexVal(n->volume >> 4); | |
414 *ptr++ = dmHexVal(n->volume); | |
415 } | |
416 else | |
417 { | |
418 char c; | |
419 switch (n->volume & 0xf0) | |
420 { | |
421 case 0x50: c = '-'; break; | |
422 case 0x60: c = '+'; break; | |
423 case 0x70: c = '/'; break; | |
424 case 0x80: c = '\\'; break; | |
425 case 0x90: c = 'S'; break; | |
426 case 0xa0: c = 'V'; break; | |
427 case 0xb0: c = 'P'; break; | |
428 case 0xc0: c = '<'; break; | |
429 case 0xd0: c = '>'; break; | |
430 case 0xe0: c = 'M'; break; | |
431 default: c = '?'; break; | |
432 } | |
433 *ptr++ = c; | |
434 *ptr++ = dmHexVal(n->volume); | |
435 } | |
436 *ptr++ = '_'; | |
437 | |
438 if (n->effect >= 0 && n->effect < jmpNMODEffectTable) | |
439 *ptr++ = jmpMODEffectTable[n->effect]; | |
440 else | |
441 *ptr++ = (n->effect == jsetNotSet ? '.' : '?'); | |
442 | |
443 if (n->param != jsetNotSet) | |
444 { | |
445 *ptr++ = dmHexVal(n->param >> 4); | |
446 *ptr++ = dmHexVal(n->param); | |
447 } | |
448 else | |
449 { | |
450 *ptr++ = '.'; | |
451 *ptr++ = '.'; | |
452 } | |
453 | |
454 *ptr = 0; | |
455 | |
456 dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT, xc, yc, text); | |
457 } | |
458 | |
459 | |
460 void dmDisplayPattern(SDL_Surface *screen, int x0, int y0, int x1, int y1, JSSPattern *pat, int row) | |
461 { | |
462 int cwidth = (font->width * 10 + 3 * 4 + 5), | |
463 lwidth = 6 + font->width * 3, | |
464 qy0 = y0 + font->height + 2, | |
465 qy1 = y1 - font->height - 2, | |
466 qwidth = ((x1 - x0 - lwidth) / cwidth), | |
467 qheight = ((qy1 - qy0 - 4) / (font->height + 1)), | |
468 nrow, nchannel, yc, choffs, | |
469 midrow = qheight / 2; | |
470 | |
471 if (engine.actChannel < qwidth / 2) | |
472 choffs = 0; | |
473 else | |
474 if (engine.actChannel >= pat->nchannels - qwidth/2) | |
475 choffs = pat->nchannels - qwidth; | |
476 else | |
477 choffs = engine.actChannel - qwidth/2; | |
478 | |
479 dmDrawBox3D(screen, x0 + lwidth, qy0, x1, qy1, col.box2, col.box1); | |
480 | |
481 for (nchannel = 0; nchannel < qwidth; nchannel++) | |
482 { | |
483 int bx0 = x0 + lwidth + 1 + nchannel * cwidth, | |
484 bx1 = bx0 + cwidth; | |
485 | |
486 if (engine.actChannel == nchannel + choffs) | |
487 { | |
488 dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.activeChannel); | |
489 } | |
490 else | |
491 { | |
492 dmFillRect(screen, bx0+1, qy0 + 1, bx1-1, qy1 - 1, col.inboxBg); | |
493 } | |
494 } | |
495 | |
496 yc = qy0 + 2 + (font->height + 1) * midrow; | |
497 dmFillRect(screen, x0 + lwidth + 1, yc - 1, x1 - 1, yc + font->height, col.activeRow); | |
498 | |
499 for (nchannel = 0; nchannel < qwidth; nchannel++) | |
500 { | |
501 int bx0 = x0 + lwidth + 1 + nchannel * cwidth, | |
502 bx1 = bx0 + cwidth; | |
503 | |
504 dmDrawVLine(screen, qy0 + 1, qy1 - 1, bx1, col.viewDiv); | |
505 | |
506 if (jvmGetMute(engine.dev, nchannel + choffs)) | |
507 { | |
508 dmDrawBMTextConstQ(screen, font, DMD_TRANSPARENT, | |
509 bx0 + (cwidth - font->width * 5) / 2, qy1 + 3, "MUTED"); | |
510 } | |
511 | |
512 dmDrawBMTextQ(screen, font, DMD_TRANSPARENT, | |
513 bx0 + (cwidth - font->width * 3) / 2, y0 + 1, "%3d", | |
514 nchannel + choffs); | |
515 } | |
516 | |
517 for (nrow = 0; nrow < qheight; nrow++) | |
518 { | |
519 int crow = nrow - midrow + row; | |
520 yc = qy0 + 2 + (font->height + 1) * nrow; | |
521 | |
522 if (crow >= 0 && crow < pat->nrows) | |
523 { | |
524 dmDrawBMTextQ(screen, font, DMD_TRANSPARENT, x0, yc, "%03d", crow); | |
525 | |
526 for (nchannel = 0; nchannel < qwidth; nchannel++) | |
527 { | |
528 if (choffs + nchannel >= pat->nchannels) | |
529 break; | |
530 | |
531 dmPrintNote(screen, x0 + lwidth + 4 + nchannel * cwidth, yc, | |
532 pat->data + (pat->nchannels * crow) + choffs + nchannel); | |
533 } | |
534 } | |
535 } | |
536 } | |
537 | |
538 | |
539 void audioCallback(void *userdata, Uint8 *stream, int len) | |
540 { | |
541 JSSMixer *d = (JSSMixer *) userdata; | |
542 | |
543 if (d != NULL) | |
544 { | |
545 jvmRenderAudio(d, stream, len / jvmGetSampleSize(d)); | |
546 } | |
547 } | |
548 | |
549 | |
550 void dmMuteChannels(BOOL mute) | |
551 { | |
552 int i; | |
553 for (i = 0; i < engine.mod->nchannels; i++) | |
554 jvmMute(engine.dev, i, mute); | |
555 } | |
556 | |
557 int main(int argc, char *argv[]) | |
558 { | |
559 BOOL initSDL = FALSE, audioInit = FALSE; | |
560 DMResource *file = NULL; | |
561 int result = -1; | |
562 BOOL muteState = FALSE; | |
563 | |
564 memset(&engine, 0, sizeof(engine)); | |
565 | |
566 engine.optScrWidth = 640; | |
567 engine.optScrHeight = 480; | |
568 engine.optScrDepth = 32; | |
569 | |
570 dmInitProg("CBP", "Cyrbe Basci Player", "0.1", NULL, NULL); | |
571 | |
572 // Parse arguments | |
573 if (!dmArgsProcess(argc, argv, optList, optListN, | |
574 argHandleOpt, argHandleFile, TRUE)) | |
575 exit(1); | |
576 | |
577 // Open the files | |
578 if (optFilename == NULL) | |
579 { | |
580 dmError("No filename specified.\n"); | |
581 return 1; | |
582 } | |
583 | |
584 if ((file = dmf_create_stdio(optFilename, "rb")) == NULL) | |
585 { | |
586 int err = dmGetErrno(); | |
587 dmError("Error opening file '%s', %d: (%s)\n", | |
588 optFilename, err, dmErrorStr(err)); | |
589 return 1; | |
590 } | |
591 | |
592 // Initialize miniJSS | |
593 jssInit(); | |
594 | |
595 // Read module file | |
596 dmMsg(1, "Reading file: %s\n", optFilename); | |
597 #ifdef JSS_SUP_XM | |
598 dmMsg(2, "* Trying XM...\n"); | |
599 result = jssLoadXM(file, &engine.mod); | |
600 #endif | |
601 #ifdef JSS_SUP_JSSMOD | |
602 if (result != 0) | |
603 { | |
604 size_t bufgot, bufsize = dmfsize(file); | |
605 Uint8 *buf = dmMalloc(bufsize); | |
606 dmfseek(file, 0L, SEEK_SET); | |
607 dmMsg(2, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf); | |
608 if ((bufgot = dmfread(buf, 1, bufsize, file)) != bufsize) | |
609 { | |
610 dmf_close(file); | |
611 dmError("Error reading file (not enough data %d), #%d: %s\n", | |
612 bufgot, dmferror(file), dmErrorStr(dmferror(file))); | |
613 goto error_exit; | |
614 } | |
615 result = jssLoadJSSMOD(buf, bufsize, &engine.mod); | |
616 dmFree(buf); | |
617 } | |
618 #endif | |
619 dmf_close(file); | |
620 | |
621 if (result != DMERR_OK) | |
622 { | |
623 dmError("Error loading module file, %d: %s\n", | |
624 result, dmErrorStr(result)); | |
625 goto error_exit; | |
626 } | |
627 | |
628 // Try to convert it | |
629 if ((result = jssConvertModuleForPlaying(engine.mod)) != DMERR_OK) | |
630 { | |
631 dmError("Could not convert module for playing, %d: %s\n", | |
632 result, dmErrorStr(result)); | |
633 goto error_exit; | |
634 } | |
635 | |
636 // Get font | |
637 // file = dmf_create_stdio("fnsmall.fnt", "rb"); | |
638 file = dmf_create_memio(NULL, "pplfont.fnt", engineSetupFont, sizeof(engineSetupFont)); | |
639 if (file == NULL) | |
640 { | |
641 dmError("Error opening font file 'pplfont.fnt'.\n"); | |
642 goto error_exit; | |
643 } | |
644 result = dmLoadBitmapFont(file, &font); | |
645 dmf_close(file); | |
646 if (result != DMERR_OK) | |
647 { | |
648 dmError("Could not load font from file, %d: %s\n", | |
649 result, dmErrorStr(result)); | |
650 goto error_exit; | |
651 } | |
652 | |
653 // Initialize SDL components | |
654 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) | |
655 { | |
656 dmError("Could not initialize SDL: %s\n", SDL_GetError()); | |
657 goto error_exit; | |
658 } | |
659 initSDL = TRUE; | |
660 | |
661 | |
662 // Initialize mixing device | |
663 dmMsg(2, "Initializing miniJSS mixer with: %d, %d, %d\n", | |
664 optOutFormat, optOutChannels, optOutFreq); | |
665 | |
666 engine.dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO); | |
667 if (engine.dev == NULL) | |
668 { | |
669 dmError("jvmInit() returned NULL\n"); | |
670 goto error_exit; | |
671 } | |
672 | |
673 switch (optOutFormat) | |
674 { | |
675 case JSS_AUDIO_S16: engine.afmt.format = AUDIO_S16SYS; break; | |
676 case JSS_AUDIO_U16: engine.afmt.format = AUDIO_U16SYS; break; | |
677 case JSS_AUDIO_S8: engine.afmt.format = AUDIO_S8; break; | |
678 case JSS_AUDIO_U8: engine.afmt.format = AUDIO_U8; break; | |
679 default: | |
680 dmError("Unsupported audio format %d (could not set matching SDL format)\n", | |
681 optOutFormat); | |
682 goto error_exit; | |
683 } | |
684 | |
685 engine.afmt.freq = optOutFreq; | |
686 engine.afmt.channels = optOutChannels; | |
687 engine.afmt.samples = optOutFreq / 16; | |
688 engine.afmt.callback = audioCallback; | |
689 engine.afmt.userdata = (void *) engine.dev; | |
690 | |
691 // Open the audio device | |
692 if (SDL_OpenAudio(&engine.afmt, NULL) < 0) | |
693 { | |
694 dmError("Couldn't open SDL audio: %s\n", | |
695 SDL_GetError()); | |
696 goto error_exit; | |
697 } | |
698 audioInit = TRUE; | |
699 | |
700 // Initialize player | |
701 if ((engine.plr = jmpInit(engine.dev)) == NULL) | |
702 { | |
703 dmError("jmpInit() returned NULL\n"); | |
704 goto error_exit; | |
705 } | |
706 | |
707 jvmSetCallback(engine.dev, jmpExec, engine.plr); | |
708 jmpSetModule(engine.plr, engine.mod); | |
709 jmpPlayOrder(engine.plr, optStartOrder); | |
710 jvmSetGlobalVol(engine.dev, 64); | |
711 | |
712 if (optMuteOChannels >= 0 && optMuteOChannels < engine.mod->nchannels) | |
713 { | |
714 dmMuteChannels(TRUE); | |
715 jvmMute(engine.dev, optMuteOChannels, FALSE); | |
716 engine.actChannel = optMuteOChannels; | |
717 muteState = TRUE; | |
718 } | |
719 | |
720 // Initialize video | |
721 if (!dmInitializeVideo()) | |
722 goto error_exit; | |
723 | |
724 SDL_WM_SetCaption(dmProgDesc, dmProgName); | |
725 | |
726 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); | |
727 | |
728 // okay, main loop here ... "play" module and print out info | |
729 SDL_LockAudio(); | |
730 SDL_PauseAudio(0); | |
731 SDL_UnlockAudio(); | |
732 | |
733 int currTick, prevTick = 0, prevRow = -1; | |
734 | |
735 while (!engine.exitFlag) | |
736 { | |
737 currTick = SDL_GetTicks(); | |
738 BOOL force = (currTick - prevTick > 500), updated = FALSE; | |
739 | |
740 while (SDL_PollEvent(&engine.event)) | |
741 switch (engine.event.type) | |
742 { | |
743 case SDL_KEYDOWN: | |
744 switch (engine.event.key.keysym.sym) | |
745 { | |
746 case SDLK_ESCAPE: | |
747 engine.exitFlag = TRUE; | |
748 break; | |
749 | |
750 case SDLK_SPACE: | |
751 engine.pauseFlag = !engine.pauseFlag; | |
752 SDL_PauseAudio(engine.pauseFlag); | |
753 break; | |
754 | |
755 case SDLK_LEFT: | |
756 if (engine.actChannel > 0) | |
757 { | |
758 engine.actChannel--; | |
759 force = TRUE; | |
760 } | |
761 break; | |
762 | |
763 case SDLK_RIGHT: | |
764 if (engine.actChannel < engine.mod->nchannels) | |
765 { | |
766 engine.actChannel++; | |
767 force = TRUE; | |
768 } | |
769 break; | |
770 | |
771 case SDLK_m: | |
772 if (engine.event.key.keysym.mod & KMOD_SHIFT) | |
773 { | |
774 muteState = !muteState; | |
775 dmMuteChannels(muteState); | |
776 } | |
777 else | |
778 if (engine.event.key.keysym.mod & KMOD_CTRL) | |
779 { | |
780 dmMuteChannels(FALSE); | |
781 } | |
782 else | |
783 { | |
784 jvmMute(engine.dev, engine.actChannel, !jvmGetMute(engine.dev, engine.actChannel)); | |
785 } | |
786 force = TRUE; | |
787 break; | |
788 | |
789 case SDLK_PAGEUP: | |
790 JSS_LOCK(engine.dev); | |
791 JSS_LOCK(engine.plr); | |
792 jmpChangeOrder(engine.plr, dmClamp(engine.plr->order - 1, 0, engine.mod->norders)); | |
793 JSS_UNLOCK(engine.plr); | |
794 JSS_UNLOCK(engine.dev); | |
795 force = TRUE; | |
796 break; | |
797 | |
798 case SDLK_PAGEDOWN: | |
799 JSS_LOCK(engine.dev); | |
800 JSS_LOCK(engine.plr); | |
801 jmpChangeOrder(engine.plr, dmClamp(engine.plr->order + 1, 0, engine.mod->norders)); | |
802 JSS_UNLOCK(engine.plr); | |
803 JSS_UNLOCK(engine.dev); | |
804 force = TRUE; | |
805 break; | |
806 | |
807 case SDLK_f: | |
808 engine.optVFlags ^= SDL_FULLSCREEN; | |
809 if (!dmInitializeVideo()) | |
810 goto error_exit; | |
811 force = TRUE; | |
812 break; | |
813 | |
814 default: | |
815 break; | |
816 } | |
817 | |
818 break; | |
819 | |
820 case SDL_VIDEORESIZE: | |
821 engine.optScrWidth = engine.event.resize.w; | |
822 engine.optScrHeight = engine.event.resize.h; | |
823 | |
824 if (!dmInitializeVideo()) | |
825 goto error_exit; | |
826 | |
827 break; | |
828 | |
829 case SDL_VIDEOEXPOSE: | |
830 break; | |
831 | |
832 case SDL_QUIT: | |
833 engine.exitFlag = TRUE; | |
834 break; | |
835 } | |
836 | |
837 | |
838 #if 1 | |
839 JSS_LOCK(engine.plr); | |
840 JSSPattern *currPattern = engine.plr->pattern; | |
841 int currRow = engine.plr->row; | |
842 if (!engine.plr->isPlaying) | |
843 engine.exitFlag = TRUE; | |
844 JSS_UNLOCK(engine.plr); | |
845 | |
846 if (currRow != prevRow || force) | |
847 { | |
848 prevRow = currRow; | |
849 force = TRUE; | |
850 } | |
851 | |
852 // Draw frame | |
853 if (SDL_MUSTLOCK(engine.screen) != 0 && SDL_LockSurface(engine.screen) != 0) | |
854 { | |
855 dmError("Can't lock surface.\n"); | |
856 goto error_exit; | |
857 } | |
858 | |
859 if (force) | |
860 { | |
861 dmClearSurface(engine.screen, col.boxBg); | |
862 | |
863 dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5, "%s v%s by ccr/TNSP - (c) Copyright 2012 TNSP", dmProgDesc, dmProgVersion); | |
864 | |
865 dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12 + 11, | |
866 "Song: '%s'", | |
867 engine.mod->moduleName); | |
868 | |
869 dmDisplayPattern(engine.screen, 5, 40, | |
870 engine.screen->w - 6, engine.screen->h * 0.8, | |
871 currPattern, currRow); | |
872 | |
873 JSS_LOCK(engine.plr); | |
874 dmDrawBMTextQ(engine.screen, font, DMD_TRANSPARENT, 5, 5 + 12, | |
875 "Tempo: %3d | Speed: %3d | Row: %3d/%-3d | Order: %3d/%-3d | Pattern: %3d/%-3d", | |
876 engine.plr->tempo, engine.plr->speed, | |
877 engine.plr->row, engine.plr->pattern->nrows, | |
878 engine.plr->order, engine.mod->norders, | |
879 engine.plr->npattern, engine.mod->npatterns); | |
880 JSS_UNLOCK(engine.plr); | |
881 updated = TRUE; | |
882 } | |
883 | |
884 if (force || currTick - prevTick >= (engine.pauseFlag ? 100 : 20)) | |
885 { | |
886 JSS_LOCK(engine.dev); | |
887 dmDisplayChannels(engine.screen, 5, engine.screen->h * 0.8 + 5, | |
888 engine.screen->w - 5, engine.screen->h - 5, engine.dev); | |
889 JSS_UNLOCK(engine.dev); | |
890 updated = TRUE; | |
891 } | |
892 | |
893 if (force) | |
894 prevTick = currTick; | |
895 | |
896 #endif | |
897 // Flip screen | |
898 if (SDL_MUSTLOCK(engine.screen) != 0) | |
899 SDL_UnlockSurface(engine.screen); | |
900 | |
901 if (updated) | |
902 SDL_Flip(engine.screen); | |
903 | |
904 SDL_Delay(engine.pauseFlag ? 100 : 30); | |
905 } | |
906 | |
907 error_exit: | |
908 if (engine.screen) | |
909 SDL_FreeSurface(engine.screen); | |
910 | |
911 dmMsg(0, "Audio shutdown.\n"); | |
912 if (audioInit) | |
913 { | |
914 SDL_LockAudio(); | |
915 SDL_PauseAudio(1); | |
916 SDL_UnlockAudio(); | |
917 SDL_CloseAudio(); | |
918 } | |
919 | |
920 jmpClose(engine.plr); | |
921 jvmClose(engine.dev); | |
922 jssFreeModule(engine.mod); | |
923 | |
924 dmFreeBitmapFont(font); | |
925 | |
926 if (initSDL) | |
927 SDL_Quit(); | |
928 | |
929 jssClose(); | |
930 | |
931 return 0; | |
932 } |